001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one
003: * or more contributor license agreements. See the NOTICE file
004: * distributed with this work for additional information
005: * regarding copyright ownership. The ASF licenses this file
006: * to you under the Apache License, Version 2.0 (the
007: * "License"); you may not use this file except in compliance
008: * with the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing,
013: * software distributed under the License is distributed on an
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015: * KIND, either express or implied. See the License for the
016: * specific language governing permissions and limitations
017: * under the License.
018: */
019: package org.apache.axis2.jaxws.handler;
020:
021: import org.apache.axis2.jaxws.ExceptionFactory;
022: import org.apache.axis2.jaxws.context.factory.MessageContextFactory;
023: import org.apache.axis2.jaxws.i18n.Messages;
024: import org.apache.axis2.jaxws.marshaller.impl.alt.MethodMarshallerUtils;
025: import org.apache.axis2.jaxws.message.Message;
026: import org.apache.axis2.jaxws.message.Protocol;
027: import org.apache.axis2.jaxws.message.XMLFault;
028: import org.apache.axis2.jaxws.message.factory.MessageFactory;
029: import org.apache.axis2.jaxws.message.util.XMLFaultUtils;
030: import org.apache.axis2.jaxws.registry.FactoryRegistry;
031: import org.apache.axis2.jaxws.utility.SAAJFactory;
032: import org.apache.commons.logging.Log;
033: import org.apache.commons.logging.LogFactory;
034:
035: import javax.xml.soap.SOAPBody;
036: import javax.xml.soap.SOAPConstants;
037: import javax.xml.soap.SOAPFault;
038: import javax.xml.soap.SOAPMessage;
039: import javax.xml.ws.ProtocolException;
040: import javax.xml.ws.WebServiceException;
041: import javax.xml.ws.handler.Handler;
042: import javax.xml.ws.handler.LogicalHandler;
043: import javax.xml.ws.handler.soap.SOAPHandler;
044:
045: import java.util.ArrayList;
046: import java.util.Iterator;
047: import java.util.List;
048:
049: public class HandlerChainProcessor {
050:
051: private static final Log log = LogFactory
052: .getLog(HandlerChainProcessor.class);
053:
054: public enum Direction {
055: IN, OUT
056: };
057:
058: // the type of message, not indicative of one-way vs. request-response
059: public enum MEP {
060: REQUEST, RESPONSE
061: };
062:
063: private javax.xml.ws.handler.MessageContext currentMC; // just a pointer
064: private LogicalMessageContext logicalMC = null;
065: private SoapMessageContext soapMC = null;
066:
067: private MEPContext mepCtx;
068:
069: private List<Handler> handlers = null;
070:
071: // track start/end of logical and protocol handlers in the list
072: // The two scenarios are: 1) run logical handlers only, 2) run all handlers
073: // logical start is always 0
074: // protocol start is always logicalLength + 1
075: // list end is always handlers.size()-1
076: private int logicalLength = 0;
077:
078: private final static int SUCCESSFUL = 0;
079: private final static int FAILED = 1;
080: private final static int PROTOCOL_EXCEPTION = 2;
081: private final static int OTHER_EXCEPTION = 3;
082: // save it if Handler.handleMessage throws one in
083: // HandlerChainProcessor.handleMessage
084: private RuntimeException savedException;
085: private Protocol proto; // need to save it incase we have to make a fault message
086:
087: /*
088: * HandlerChainProcess expects null, empty list, or an already-sorted
089: * list. If the chain passed into here came from our HandlerChainResolver,
090: * it is sorted already. If a client app created or manipulated the list,
091: * it may not be sorted. The processChain and processFault methods check
092: * for this by calling verifyChain.
093: */
094: public HandlerChainProcessor(List<Handler> chain, Protocol proto) {
095: if (chain == null) {
096: handlers = new ArrayList<Handler>();
097: } else
098: handlers = chain;
099: this .proto = proto;
100: }
101:
102: /*
103: * sortChain will properly sort the chain, logical then protocol, since it may be
104: * a chain built or modified by a client application. Also keep track of
105: * start/end for each type of handler.
106: */
107: private void sortChain() throws WebServiceException {
108:
109: ArrayList<Handler> logicalHandlers = new ArrayList<Handler>();
110: ArrayList<Handler> protocolHandlers = new ArrayList<Handler>();
111:
112: Iterator handlerIterator = handlers.iterator();
113:
114: while (handlerIterator.hasNext()) {
115: // this is a safe cast since the handlerResolver and binding.setHandlerChain
116: // and InvocationContext.setHandlerChain verifies it before we get here
117: Handler handler = (Handler) handlerIterator.next();
118: // JAXWS 9.2.1.2 sort them by Logical, then SOAP
119: if (LogicalHandler.class.isAssignableFrom(handler
120: .getClass()))
121: logicalHandlers.add((LogicalHandler) handler);
122: else if (SOAPHandler.class.isAssignableFrom(handler
123: .getClass()))
124: // instanceof ProtocolHandler
125: protocolHandlers.add((SOAPHandler) handler);
126: else if (Handler.class.isAssignableFrom(handler.getClass())) {
127: // TODO: NLS better error message
128: throw ExceptionFactory.makeWebServiceException(Messages
129: .getMessage("handlerChainErr1", handler
130: .getClass().getName()));
131: } else {
132: // TODO: NLS better error message
133: throw ExceptionFactory.makeWebServiceException(Messages
134: .getMessage("handlerChainErr2", handler
135: .getClass().getName()));
136: }
137: }
138:
139: logicalLength = logicalHandlers.size();
140:
141: // JAXWS 9.2.1.2 sort them by Logical, then SOAP
142: handlers.clear();
143: handlers.addAll(logicalHandlers);
144: handlers.addAll(protocolHandlers);
145: }
146:
147: /**
148: * @param mc
149: * By the time processChain method is called, we already have the sorted chain,
150: * and now we have the direction, MEP, MessageContext, and if a response is expected. We should
151: * be able to handle everything from here, no pun intended.
152: *
153: * Two things a user of processChain should check when the method completes:
154: * 1. Has the MessageContext.MESSAGE_OUTBOUND_PROPERTY changed, indicating reversal of message direction
155: * 2. Has the message been converted to a fault message? (indicated by a flag in the message)
156: */
157: public boolean processChain(MEPContext mepCtx, Direction direction,
158: MEP mep, boolean expectResponse) {
159:
160: if (handlers.size() == 0)
161: return true;
162:
163: this .mepCtx = mepCtx;
164: sortChain();
165: initContext(direction);
166:
167: if (direction == Direction.OUT) { // 9.3.2 outbound
168: currentMC
169: .put(
170: javax.xml.ws.handler.MessageContext.MESSAGE_OUTBOUND_PROPERTY,
171: (direction == Direction.OUT));
172: callGenericHandlers(mep, expectResponse, 0,
173: handlers.size() - 1, direction);
174: } else { // IN case - 9.3.2 inbound
175: currentMC
176: .put(
177: javax.xml.ws.handler.MessageContext.MESSAGE_OUTBOUND_PROPERTY,
178: (direction == Direction.OUT));
179: callGenericHandlers(mep, expectResponse,
180: handlers.size() - 1, 0, direction);
181: }
182:
183: // message context may have been changed to be response, and message
184: // converted
185: // according to the JAXWS spec 9.3.2.1 footnote 2
186: if ((Boolean) (currentMC
187: .get(javax.xml.ws.handler.MessageContext.MESSAGE_OUTBOUND_PROPERTY)) != (direction == Direction.OUT))
188: return false;
189: return true;
190:
191: }
192:
193: /*
194: * This is the implementation of JAX-WS 2.0 section 9.3.2.1
195: */
196: private void callGenericHandlers(MEP mep, boolean expectResponse,
197: int start, int end, Direction direction)
198: throws RuntimeException {
199:
200: // if this is a response message, expectResponse should always be false
201: if (mep == MEP.RESPONSE)
202: expectResponse = false;
203:
204: int i = start;
205: int result = SUCCESSFUL;
206:
207: // declared and initialized just in case we need them
208: // in a reverse flow situation
209: int newStart = 0, newStart_inclusive = 0, newEnd = 0;
210: Direction newDirection = direction;
211:
212: if (direction == Direction.OUT) {
213: while ((i <= end) && (result == SUCCESSFUL)) {
214: result = handleMessage(((Handler) handlers.get(i)),
215: direction, expectResponse);
216: newStart = i - 1;
217: newStart_inclusive = i;
218: newEnd = 0;
219: newDirection = Direction.IN;
220: i++;
221: if (result == SUCCESSFUL) // don't switch if failed, since we'll be reversing directions
222: switchContext(direction, i);
223: }
224: } else { // IN case
225: while ((i >= end) && (result == SUCCESSFUL)) {
226: result = handleMessage(((Handler) handlers.get(i)),
227: direction, expectResponse);
228: newStart = i + 1;
229: newStart_inclusive = i;
230: newEnd = handlers.size() - 1;
231: newDirection = Direction.OUT;
232: i--;
233: if (result == SUCCESSFUL) // don't switch if failed, since we'll be reversing directions
234: switchContext(direction, i);
235: }
236: }
237:
238: if (newDirection == direction) // we didn't actually process anything, probably due to empty list
239: return; // no need to continue
240:
241: // 9.3.2.3 in all situations, we want to close as many handlers as
242: // were invoked prior to completion or exception throwing
243: if (expectResponse) {
244: if (result == FAILED) {
245: // we should only use callGenericHandlers_avoidRecursion in this case
246: callGenericHandlers_avoidRecursion(newStart, newEnd,
247: newDirection);
248: callCloseHandlers(newStart_inclusive, newEnd,
249: newDirection);
250: } else if (result == PROTOCOL_EXCEPTION) {
251: try {
252: callGenericHandleFault(newStart, newEnd,
253: newDirection);
254: callCloseHandlers(newStart_inclusive, newEnd,
255: newDirection);
256: } catch (RuntimeException re) {
257: callCloseHandlers(newStart_inclusive, newEnd,
258: newDirection);
259: // TODO: NLS log and throw
260: throw re;
261: }
262: } else if (result == OTHER_EXCEPTION) {
263: callCloseHandlers(newStart_inclusive, newEnd,
264: newDirection);
265: // savedException initialized in HandlerChainProcessor.handleMessage
266: // TODO: NLS log and throw
267: throw savedException;
268: }
269: } else { // everything was successful OR finished processing handlers
270: /*
271: * This is a little confusing. There are several cases we should be
272: * aware of. An incoming request with false expectResponse is
273: * equivalent to server inbound one-way, for example.
274: *
275: * An outgoing response is server outbound, and is always marked
276: * with a false expectResponse. The problem, however, is that the
277: * direction for the call to closehandlers will be incorrect. In
278: * this case, the handlers should be closed in the opposite order of
279: * the ORIGINAL invocation.
280: */
281: if (mep.equals(MEP.REQUEST)) {
282: // a request that requires no response is a one-way message
283: // and we should only close whomever got invoked
284: callCloseHandlers(newStart_inclusive, newEnd,
285: newDirection);
286: } else {
287: // it's a response, so we can safely assume that
288: // ALL the handlers were invoked on the request,
289: // so we need to close ALL of them
290: if (direction.equals(Direction.IN)) {
291: callCloseHandlers(handlers.size() - 1, 0, direction);
292: } else {
293: callCloseHandlers(0, handlers.size() - 1, direction);
294: }
295: if (savedException != null) {
296: // we have a saved exception, throw it (JAX-WS 9.3.2.1 "Throw any
297: // other runtime exception --> No response" case.
298: throw savedException;
299: }
300: }
301: }
302: }
303:
304: /*
305: * callGenericHandlers_avoidRecursion should ONLY be called from one place.
306: * TODO: We cannot necessarily assume no false returns and no exceptions will be
307: * thrown from here even though the handlers we will be calling have all already
308: * succeeded in callGenericHandlers.
309: */
310: private void callGenericHandlers_avoidRecursion(int start, int end,
311: Direction direction) {
312: int i = start;
313:
314: if (direction == Direction.OUT) {
315: for (; i <= end; i++) {
316: switchContext(direction, i);
317: Handler handler = (Handler) handlers.get(i);
318:
319: if (log.isDebugEnabled()) {
320: log.debug("Invoking handleMessage on: "
321: + handler.getClass().getName());
322: }
323: handler.handleMessage(currentMC);
324: }
325: } else { // IN case
326: for (; i >= end; i--) {
327: switchContext(direction, i);
328: Handler handler = (Handler) handlers.get(i);
329:
330: if (log.isDebugEnabled()) {
331: log.debug("Invoking handleMessage on: "
332: + handler.getClass().getName());
333: }
334: handler.handleMessage(currentMC);
335: }
336: }
337: }
338:
339: /**
340: * Calls handleMessage on the Handler. If an exception is thrown and a response is expected, the
341: * MessageContext is updated with the handler information
342: *
343: * @returns SUCCESSFUL if successfully, UNSUCCESSFUL if false, EXCEPTION if exception thrown
344: */
345: private int handleMessage(Handler handler, Direction direction,
346: boolean expectResponse) throws RuntimeException {
347: try {
348: if (log.isDebugEnabled()) {
349: log.debug("Invoking handleMessage on: "
350: + handler.getClass().getName());
351: }
352: boolean success = handler.handleMessage(currentMC);
353: if (success) {
354: if (log.isDebugEnabled()) {
355: log.debug("handleMessage() returned true");
356: }
357: return SUCCESSFUL;
358: } else {
359: if (log.isDebugEnabled()) {
360: log.debug("handleMessage() returned false");
361: }
362: if (expectResponse)
363: currentMC
364: .put(
365: javax.xml.ws.handler.MessageContext.MESSAGE_OUTBOUND_PROPERTY,
366: (direction != Direction.OUT));
367: return FAILED;
368: }
369: } catch (RuntimeException re) {
370: // RuntimeException and ProtocolException
371: if (log.isDebugEnabled()) {
372: log
373: .debug("An exception was thrown during the handleMessage() invocation");
374: log.debug("Exception: " + re.getClass().getName() + ":"
375: + re.getMessage());
376: }
377:
378: savedException = re;
379: if (expectResponse)
380: // mark it as reverse direction
381: currentMC
382: .put(
383: javax.xml.ws.handler.MessageContext.MESSAGE_OUTBOUND_PROPERTY,
384: (direction != Direction.OUT));
385: if (ProtocolException.class.isAssignableFrom(re.getClass())) {
386: convertToFaultMessage(mepCtx, re, proto);
387: // just re-initialize the current handler message context since
388: // that will pick up the now-changed message
389: return PROTOCOL_EXCEPTION;
390: }
391: return OTHER_EXCEPTION;
392: }
393:
394: }
395:
396: /*
397: * start and end should be INclusive of the handlers that have already been
398: * invoked on Handler.handleMessage or Handler.handleFault
399: */
400: private void callCloseHandlers(int start, int end,
401: Direction direction) {
402: int i = start;
403:
404: if (direction == Direction.OUT) {
405: for (; i <= end; i++) {
406: try {
407: switchContext(direction, i);
408: Handler handler = (Handler) handlers.get(i);
409: if (log.isDebugEnabled()) {
410: log.debug("Invoking close on: "
411: + handler.getClass().getName());
412: }
413: handler.close(currentMC);
414:
415: // TODO when we close, are we done with the handler instance, and thus
416: // may call the PreDestroy annotated method? I don't think so, especially
417: // if we've cached the handler list somewhere.
418: } catch (Exception e) {
419: if (log.isDebugEnabled()) {
420: log
421: .debug("An Exception occurred while calling handler.close()");
422: log.debug("Exception: "
423: + e.getClass().getName() + ":"
424: + e.getMessage());
425: }
426: }
427: }
428: } else { // IN case
429: for (; i >= end; i--) {
430: try {
431: switchContext(direction, i);
432: Handler handler = (Handler) handlers.get(i);
433: if (log.isDebugEnabled()) {
434: log.debug("Invoking close on: "
435: + handler.getClass().getName());
436: }
437: handler.close(currentMC);
438:
439: // TODO when we close, are we done with the handler instance, and thus
440: // may call the PreDestroy annotated method? I don't think so, especially
441: // if we've cached the handler list somewhere.
442: } catch (Exception e) {
443: if (log.isDebugEnabled()) {
444: log
445: .debug("An Exception occurred while calling handler.close()");
446: log.debug("Exception: "
447: + e.getClass().getName() + ":"
448: + e.getMessage());
449: }
450: }
451: }
452: }
453: }
454:
455: /*
456: * processFault is available for a server to use when the endpoint
457: * throws an exception or a client when it gets a fault response message
458: *
459: * In both cases, all of the handlers have run successfully in the
460: * opposite direction as this call to callHandleFault, and thus
461: * should be closed.
462: */
463: public void processFault(MEPContext mepCtx, Direction direction) {
464:
465: // direction.IN = client
466: // direction.OUT = server
467: if (handlers.size() == 0)
468: return;
469:
470: this .mepCtx = mepCtx;
471: sortChain();
472: initContext(direction);
473: currentMC
474: .put(
475: javax.xml.ws.handler.MessageContext.MESSAGE_OUTBOUND_PROPERTY,
476: (direction == Direction.OUT));
477:
478: try {
479: if (direction == Direction.OUT) {
480: callGenericHandleFault(0, handlers.size() - 1,
481: direction);
482: } else { // IN case
483: callGenericHandleFault(handlers.size() - 1, 0,
484: direction);
485: }
486: } catch (RuntimeException re) {
487: // TODO: log it
488: throw re;
489: } finally {
490: // we can close all the Handlers in reverse order
491: if (direction == Direction.OUT) {
492: initContext(Direction.IN);
493: callCloseHandlers(0, handlers.size() - 1, Direction.OUT);
494: } else { // IN case
495: initContext(Direction.IN);
496: callCloseHandlers(handlers.size() - 1, 0, Direction.IN);
497: }
498: }
499: }
500:
501: /*
502: * The callGenericHandleFault caller is responsible for closing any invoked
503: * Handlers. We don't know how far the Handler.handleMessage calls got
504: * before a failure may have occurred.
505: *
506: * Regardless of the Handler.handleFault result, the flow is the same (9.3.2.2)
507: */
508: private void callGenericHandleFault(int start, int end,
509: Direction direction) throws RuntimeException {
510:
511: int i = start;
512:
513: // we may be starting in the middle of the list, and therefore may need to switch contexts
514: switchContext(direction, i);
515:
516: if (direction == Direction.OUT) {
517: for (; i <= end; i++) {
518: Handler handler = (Handler) handlers.get(i);
519: if (log.isDebugEnabled()) {
520: log.debug("Invoking handleFault on: "
521: + handler.getClass().getName());
522: }
523: boolean success = handler.handleFault(currentMC);
524:
525: if (!success)
526: break;
527: switchContext(direction, i + 1);
528: }
529: } else { // IN case
530: for (; i >= end; i--) {
531: Handler handler = (Handler) handlers.get(i);
532: if (log.isDebugEnabled()) {
533: log.debug("Invoking handleFault on: "
534: + handler.getClass().getName());
535: }
536: boolean success = handler.handleFault(currentMC);
537:
538: if (!success)
539: break;
540: switchContext(direction, i - 1);
541: }
542: }
543: }
544:
545: public static void convertToFaultMessage(MEPContext mepCtx,
546: Exception e, Protocol protocol) {
547:
548: // need to check if message is already a fault message or not,
549: // probably by way of a flag (isFault) in the MessageContext or Message
550: if (log.isDebugEnabled()) {
551: log
552: .debug("Creating a fault Message object for the exception: "
553: + e.getClass().getName());
554: }
555:
556: try {
557: /* TODO TODO TODO
558: * There has GOT to be a better way to do this.
559: */
560: if (protocol == Protocol.soap11
561: || protocol == Protocol.soap12) {
562: String protocolNS = (protocol == Protocol.soap11) ? SOAPConstants.URI_NS_SOAP_1_1_ENVELOPE
563: : SOAPConstants.URI_NS_SOAP_1_2_ENVELOPE;
564:
565: // The following set of instructions is used to avoid
566: // some unimplemented methods in the Axis2 SAAJ implementation
567: XMLFault xmlFault = MethodMarshallerUtils
568: .createXMLFaultFromSystemException(e);
569: javax.xml.soap.MessageFactory mf = SAAJFactory
570: .createMessageFactory(protocolNS);
571: SOAPMessage message = mf.createMessage();
572: SOAPBody body = message.getSOAPBody();
573: SOAPFault soapFault = XMLFaultUtils.createSAAJFault(
574: xmlFault, body);
575:
576: // TODO something is wrong here. The message should be a response message, not
577: // a request message. I don't see how to change that. (see the debugger...)
578: // TODO probably also need to turn on message.WRITE_XML_DECLARATION
579: MessageFactory msgFactory = (MessageFactory) FactoryRegistry
580: .getFactory(MessageFactory.class);
581: Message msg = msgFactory.createFrom(message);
582: mepCtx.setMessage(msg);
583:
584: } else {
585: throw ExceptionFactory
586: .makeWebServiceException("We only support SOAP11 and SOAP12 for JAXWS handlers");
587: }
588:
589: } catch (Exception ex) {
590: throw ExceptionFactory.makeWebServiceException(ex);
591: }
592:
593: }
594:
595: private void initContext(Direction direction) {
596: soapMC = MessageContextFactory.createSoapMessageContext(mepCtx
597: .getMessageContext());
598: logicalMC = MessageContextFactory
599: .createLogicalMessageContext(mepCtx.getMessageContext());
600: if (direction == Direction.OUT) {
601: // logical context, then SOAP
602: if ((logicalLength == 0) && (handlers.size() > 0)) // we only have soap handlers
603: currentMC = soapMC; //MessageContextFactory.createSoapMessageContext(mepCtx.getMessageContext());
604: else
605: currentMC = logicalMC; //MessageContextFactory.createLogicalMessageContext(mepCtx.getMessageContext());
606: } else {
607: // SOAP context, then logical
608: if ((logicalLength == handlers.size())
609: && (handlers.size() > 0)) // we only have logical handlers
610: currentMC = logicalMC; //MessageContextFactory.createLogicalMessageContext(mepCtx.getMessageContext());
611: else
612: currentMC = soapMC; //MessageContextFactory.createSoapMessageContext(mepCtx.getMessageContext());
613: }
614: }
615:
616: private void switchContext(Direction direction, int index) {
617:
618: if ((logicalLength == handlers.size()) || (logicalLength == 0))
619: return; // all handlers must be the same type, so no context switch
620:
621: if (((direction == Direction.OUT) && (index == logicalLength))
622: || ((direction == Direction.IN) && (index == (logicalLength - 1)))) {
623: //if (currentMC.getClass().isAssignableFrom(LogicalMessageContext.class))
624: if (currentMC == logicalMC) // object check, not .equals()
625: currentMC = soapMC; //MessageContextFactory.createSoapMessageContext(mepCtx.getMessageContext());
626: else
627: currentMC = logicalMC; //MessageContextFactory.createLogicalMessageContext(mepCtx.getMessageContext());
628: }
629: }
630:
631: }
|