Author: scheu Date: Mon Oct 25 14:28:02 2010 New Revision: 1027138 URL: http://svn.apache.org/viewvc?rev=1027138&view=rev Log: AXIS2-4862 Contributor:Rich Scheuerle Summary: Fixed JAX-WS handler chain processing to propogate the (possibly edited) Message to the Axis2 MessageContext when the handler chain is reversed due to a "return false" from user's JAX-WS handler. This ensures that the correct message is returned to the inbound handlers and client application.
Also noticed that the JAX-WS fault processing has a similar problem. Fixed the code in that case too. Also noticed that the JAX-WS fault processing is not examining the handler's message prior to making a new message. This contradicts the specification. I changed to the code to examine the current message and see if it is a fault. If it is, then that fault is retained. If not, then the thown protocol Exception is converted into a fault message and becomes the current message. Modified: axis/axis2/java/core/trunk/modules/jaxws-integration/test/org/apache/axis2/jaxws/sample/AddNumbersHandlerTests.java axis/axis2/java/core/trunk/modules/jaxws/src/org/apache/axis2/jaxws/handler/HandlerChainProcessor.java Modified: axis/axis2/java/core/trunk/modules/jaxws-integration/test/org/apache/axis2/jaxws/sample/AddNumbersHandlerTests.java URL: http://svn.apache.org/viewvc/axis/axis2/java/core/trunk/modules/jaxws-integration/test/org/apache/axis2/jaxws/sample/AddNumbersHandlerTests.java?rev=1027138&r1=1027137&r2=1027138&view=diff ============================================================================== --- axis/axis2/java/core/trunk/modules/jaxws-integration/test/org/apache/axis2/jaxws/sample/AddNumbersHandlerTests.java (original) +++ axis/axis2/java/core/trunk/modules/jaxws-integration/test/org/apache/axis2/jaxws/sample/AddNumbersHandlerTests.java Mon Oct 25 14:28:02 2010 @@ -823,7 +823,7 @@ public class AddNumbersHandlerTests exte fail("Should have got an exception, but we didn't."); } catch(Exception e) { e.printStackTrace(); - assertTrue("Exception should be SOAPFaultException", e instanceof SOAPFaultException); + assertTrue("Exception should be SOAPFaultException. Found " +e.getClass() + " "+ e.getMessage(), e instanceof SOAPFaultException); assertEquals("I don't like the value 99", ((SOAPFaultException)e).getMessage()); String log = readLogFile(); Modified: axis/axis2/java/core/trunk/modules/jaxws/src/org/apache/axis2/jaxws/handler/HandlerChainProcessor.java URL: http://svn.apache.org/viewvc/axis/axis2/java/core/trunk/modules/jaxws/src/org/apache/axis2/jaxws/handler/HandlerChainProcessor.java?rev=1027138&r1=1027137&r2=1027138&view=diff ============================================================================== --- axis/axis2/java/core/trunk/modules/jaxws/src/org/apache/axis2/jaxws/handler/HandlerChainProcessor.java (original) +++ axis/axis2/java/core/trunk/modules/jaxws/src/org/apache/axis2/jaxws/handler/HandlerChainProcessor.java Mon Oct 25 14:28:02 2010 @@ -19,9 +19,11 @@ package org.apache.axis2.jaxws.handler; +import org.apache.axis2.AxisFault; import org.apache.axis2.jaxws.Constants; import org.apache.axis2.jaxws.ExceptionFactory; import org.apache.axis2.jaxws.context.factory.MessageContextFactory; +import org.apache.axis2.jaxws.core.MessageContext; import org.apache.axis2.jaxws.handler.factory.HandlerPostInvokerFactory; import org.apache.axis2.jaxws.handler.factory.HandlerPreInvokerFactory; import org.apache.axis2.jaxws.i18n.Messages; @@ -30,6 +32,7 @@ import org.apache.axis2.jaxws.message.Me import org.apache.axis2.jaxws.message.Protocol; import org.apache.axis2.jaxws.message.XMLFault; import org.apache.axis2.jaxws.message.factory.MessageFactory; +import org.apache.axis2.jaxws.message.util.MessageUtils; import org.apache.axis2.jaxws.message.util.XMLFaultUtils; import org.apache.axis2.jaxws.registry.FactoryRegistry; import org.apache.axis2.jaxws.utility.SAAJFactory; @@ -278,30 +281,75 @@ public class HandlerChainProcessor { // were invoked prior to completion or exception throwing if (expectResponse) { if (result == FAILED) { + if (log.isDebugEnabled()) { + log.debug("Handler returned false...Start running the handlers in reverse"); + } + // One of that handlers returned false, therefore the handler processing + // is stoped and the transport outbound will be avoided. + // This may be due to an exception or it may be due the customer intentionally + // preventing a message from flowing outbound. + // we should only use callGenericHandlers_avoidRecursion in this case // the message context is now an outbound message context, // and should be marked as such so the SOAPHeadersAdapter will // "install" with the correct property key. - mepCtx.getMessageContext().setOutbound(newDirection == Direction.OUT); - SOAPHeadersAdapter.install(mepCtx.getMessageContext()); + + // Get the MessageContext and switch its direction + MessageContext jaxwsMC = mepCtx.getMessageContext(); + jaxwsMC.setOutbound(newDirection == Direction.OUT); + SOAPHeadersAdapter.install(jaxwsMC); callGenericHandlers_avoidRecursion(newStart, newEnd, newDirection); + + // Now we need to place the Message (which is the one edited by the handler) + // onto the Axis2 MC. This is necessary because the Axis2 response MC + // will be created from this MC and must have the correct message. + this.placeMessageOnAxis2MessageContext(jaxwsMC); + + // Now close the handlers callCloseHandlers(newStart_inclusive, newEnd, newDirection); + if (log.isDebugEnabled()) { + log.debug("Handler returned false...End running the handlers in reverse"); + } } else if (result == PROTOCOL_EXCEPTION) { try { + if (log.isDebugEnabled()) { + log.debug("Handler threw ProtocolException...Start running the handlers in reverse"); + } // the message context is now an outbound message context, // and should be marked as such so the SOAPHeadersAdapter will // "install" with the correct property key. - mepCtx.getMessageContext().setOutbound(newDirection == Direction.OUT); - SOAPHeadersAdapter.install(mepCtx.getMessageContext()); + + // Get the MessageContext and switch its direction + MessageContext jaxwsMC = mepCtx.getMessageContext(); + jaxwsMC.setOutbound(newDirection == Direction.OUT); + SOAPHeadersAdapter.install(jaxwsMC); + + // Call the handlerFault methods on the handlers and also close the handlers callGenericHandleFault(newStart, newEnd, newDirection); + + // Now we need to place the Message (which is the one edited by the handler) + // onto the Axis2 MC. This is necessary because the Axis2 response MC + // will be created from this MC and must have the correct message. + this.placeMessageOnAxis2MessageContext(jaxwsMC); + + // Now close the handlers callCloseHandlers(newStart_inclusive, newEnd, newDirection); + if (log.isDebugEnabled()) { + log.debug("Handler threw ProtocolException...End running the handlers in reverse"); + } } catch (RuntimeException re) { callCloseHandlers(newStart_inclusive, newEnd, newDirection); throw re; } } else if (result == OTHER_EXCEPTION) { + if (log.isDebugEnabled()) { + log.debug("Handler threw unanticipated Exception...Start handler closure"); + } callCloseHandlers(newStart_inclusive, newEnd, newDirection); // savedException initialized in HandlerChainProcessor.handleMessage + if (log.isDebugEnabled()) { + log.debug("Handler threw unanticipated Exception...End handler closure"); + } throw savedException; } } else { // everything was successful OR finished processing handlers @@ -354,6 +402,42 @@ public class HandlerChainProcessor { return true; } + /** + * Called during reversal and exception processing to place the + * current Message (edited by the Handlers) onto the MessageContext + * @param jaxwsMC + */ + private void placeMessageOnAxis2MessageContext(MessageContext jaxwsMC) { + if (log.isDebugEnabled()) { + log.debug("start placeMessageOnAxis2MessageContext"); + } + try { + org.apache.axis2.context.MessageContext mc = jaxwsMC.getAxisMessageContext(); + if (mc != null) { + Message message = jaxwsMC.getMessage(); + if (message != null) { + if (log.isDebugEnabled()) { + log.debug("Place the (perhaps new or edited) Message on the MessageContext"); + } + MessageUtils.putMessageOnMessageContext(message, mc); + } else { + if (log.isDebugEnabled()) { + log.debug("There is no Message. Message is not copied to the Axis2 MessageContext."); + } + } + } else { + if (log.isDebugEnabled()) { + log.debug("Axis2 MessageContext is not available. Message is not copied"); + } + } + } catch (AxisFault e) { + throw ExceptionFactory.makeWebServiceException(e); + } + if (log.isDebugEnabled()) { + log.debug("end placeMessageOnAxis2MessageContext"); + } + } + /* * callGenericHandlers_avoidRecursion should ONLY be called from one place. * TODO: We cannot necessarily assume no false returns and no exceptions will be @@ -437,7 +521,7 @@ public class HandlerChainProcessor { currentMC.put(javax.xml.ws.handler.MessageContext.MESSAGE_OUTBOUND_PROPERTY, (direction != Direction.OUT)); if (ProtocolException.class.isAssignableFrom(re.getClass())) { - convertToFaultMessage(mepCtx, re, proto); + convertToFaultMessage(mepCtx, re, proto, true); // just re-initialize the current handler message context since // that will pick up the now-changed message return PROTOCOL_EXCEPTION; @@ -526,7 +610,9 @@ public class HandlerChainProcessor { callGenericHandleFault(handlers.size() - 1, 0, direction); } } catch (RuntimeException re) { - // TODO: log it + if (log.isDebugEnabled()) { + log.debug("An exception occurred during handleFault chain processing", re); + } throw re; } finally { // we can close all the Handlers in reverse order @@ -583,20 +669,43 @@ public class HandlerChainProcessor { } } - public static void convertToFaultMessage(MEPContext mepCtx, Exception e, Protocol protocol) { + convertToFaultMessage(mepCtx, e, protocol, false); + } + + /** + * Converts the Exception into an XML Fault Message that is stored on the MEPContext. + * Note that if the forceConversion flag is true, this conversion will always occur. + * If the checkMsg flag is true, this conversion only occurs if the Message is not already + * a Fault (per 9,3,2.1 of the JAX-WS specification) + * + * @param mepCtx MEPContext + * @param e Exception + * @param protocol Protocol + * @param forceConversion If true, the Exception is always converted to a Message + */ + public static void convertToFaultMessage(MEPContext mepCtx, + Exception e, + Protocol protocol, + boolean checkMsg) { // need to check if message is already a fault message or not, // probably by way of a flag (isFault) in the MessageContext or Message if (log.isDebugEnabled()) { - log.debug("Creating a fault Message object for the exception: " + e.getClass().getName()); + log.debug("start convertToFaultMessge with exception: " + e.getClass().getName()); } try { - /* TODO TODO TODO - * There has GOT to be a better way to do this. - */ - if (protocol == Protocol.soap11 || protocol == Protocol.soap12) { + // According to the 9.3.2.1, The message is converted into a fault only if it is not already a Fault + Message messageFromHandler = mepCtx.getMessageContext().getMessage(); + if (messageFromHandler != null && messageFromHandler.isFault()) { + if (log.isDebugEnabled()) { + log.debug("The Message is already a SOAPFault. The exception is not converted into a Message"); + } + } else if (protocol == Protocol.soap11 || protocol == Protocol.soap12) { + if (log.isDebugEnabled()) { + log.debug("Converting Exception into a Message"); + } String protocolNS = (protocol == Protocol.soap11) ? SOAPConstants.URI_NS_SOAP_1_1_ENVELOPE : SOAPConstants.URI_NS_SOAP_1_2_ENVELOPE; @@ -609,19 +718,24 @@ public class HandlerChainProcessor { SOAPBody body = message.getSOAPBody(); SOAPFault soapFault = XMLFaultUtils.createSAAJFault(xmlFault, body); - // TODO something is wrong here. The message should be a response message, not - // a request message. I don't see how to change that. (see the debugger...) - // TODO probably also need to turn on message.WRITE_XML_DECLARATION MessageFactory msgFactory = (MessageFactory) FactoryRegistry.getFactory(MessageFactory.class); Message msg = msgFactory.createFrom(message); mepCtx.setMessage(msg); } else { - throw ExceptionFactory.makeWebServiceException(Messages.getMessage("cFaultMsgErr")); + WebServiceException wse = ExceptionFactory.makeWebServiceException(Messages.getMessage("cFaultMsgErr")); + if (log.isDebugEnabled()) { + log.debug("end convertToFaultMessge due to error ", wse); + } + throw wse; } } catch (Exception ex) { - throw ExceptionFactory.makeWebServiceException(ex); + WebServiceException wse =ExceptionFactory.makeWebServiceException(ex); + if (log.isDebugEnabled()) { + log.debug("end convertToFaultMessge due to error ", wse); + } + throw wse; } }