/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.axis2.jaxws.server; import org.apache.axis2.AxisFault; import org.apache.axis2.addressing.AddressingConstants; import org.apache.axis2.context.OperationContext; import org.apache.axis2.description.AxisOperation; import org.apache.axis2.description.AxisService; import org.apache.axis2.description.Parameter; import org.apache.axis2.description.WSDL2Constants; import org.apache.axis2.engine.AxisEngine; import org.apache.axis2.engine.MessageReceiver; import org.apache.axis2.java.security.AccessController; import org.apache.axis2.jaxws.ExceptionFactory; import org.apache.axis2.jaxws.core.InvocationContextFactory; import org.apache.axis2.jaxws.core.MessageContext; import org.apache.axis2.jaxws.core.util.MessageContextUtils; import org.apache.axis2.jaxws.description.EndpointDescription; import org.apache.axis2.jaxws.handler.AttachmentsAdapter; import org.apache.axis2.jaxws.handler.MEPContext; import org.apache.axis2.jaxws.handler.SOAPHeadersAdapter; import org.apache.axis2.jaxws.handler.TransportHeadersAdapter; import org.apache.axis2.jaxws.i18n.Messages; import org.apache.axis2.jaxws.message.util.MessageUtils; import org.apache.axis2.jaxws.registry.InvocationListenerRegistry; import org.apache.axis2.jaxws.util.Constants; import org.apache.axis2.transport.RequestResponseTransport; import org.apache.axis2.util.JavaUtils; import org.apache.axis2.util.ThreadContextMigratorUtil; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import javax.xml.ws.Binding; import javax.xml.ws.WebServiceException; import java.security.PrivilegedAction; /** * The JAXWSMessageReceiver is the entry point, from the server's perspective, to the JAX-WS code. * This will be called by the Axis Engine and is the end of the chain from an Axis2 perspective. */ public class JAXWSMessageReceiver implements MessageReceiver { private static final Log log = LogFactory.getLog(JAXWSMessageReceiver.class); private static String PARAM_SERVICE_CLASS = "ServiceClass"; public static String PARAM_BINDING = "Binding"; /** * We should have already determined which AxisService we're targetting at this point. So now, * just get the service implementation and invoke the appropriate method. * @param axisRequestMsgCtx * @throws org.apache.axis2.AxisFault */ public void receive(org.apache.axis2.context.MessageContext axisRequestMsgCtx) throws AxisFault { AxisFault faultToReturn = null; if (log.isDebugEnabled()) { log.debug("new request received"); } //Get the name of the service impl that was stored as a parameter // inside of the services.xml. AxisService service = axisRequestMsgCtx.getAxisService(); // we need to set the deployment class loader as the TCCL. This is because, in JAX-WS // services, there can be situations where we have to load classes from the deployment // artifact (JAX-WS jar file) in the message flow. Ex: Handler classes in the service // artifact. Adding this as a fix for AXIS2-4930. setContextClassLoader(service.getClassLoader()); org.apache.axis2.description.Parameter svcClassParam = service.getParameter(PARAM_SERVICE_CLASS); if (svcClassParam == null) { throw new RuntimeException( Messages.getMessage("JAXWSMessageReceiverNoServiceClass")); } Parameter endpointDescParam = service.getParameter(EndpointDescription.AXIS_SERVICE_PARAMETER); if (endpointDescParam == null) { throw new RuntimeException(Messages.getMessage("JAXWSMessageReceiverNoServiceClass")); } AxisOperation operation = axisRequestMsgCtx.getAxisOperation(); String mep = operation.getMessageExchangePattern(); if (log.isDebugEnabled()) { log.debug("MEP: " + mep); } try { //This assumes that we are on the ultimate execution thread ThreadContextMigratorUtil.performMigrationToThread( Constants.THREAD_CONTEXT_MIGRATOR_LIST_ID, axisRequestMsgCtx); //We'll need an instance of the EndpointController to actually //drive the invocation. //TODO: More work needed to determine the lifecycle of this thing EndpointController endpointCtlr = new EndpointController(); MessageContext requestMsgCtx = new MessageContext(axisRequestMsgCtx); requestMsgCtx.setServer(true); requestMsgCtx.setMEPContext(new MEPContext(requestMsgCtx)); ClassLoader loader = getCachedClassLoader(axisRequestMsgCtx); if (loader != null) { requestMsgCtx.setProperty(org.apache.axis2.jaxws.spi.Constants.CACHE_CLASSLOADER, loader); } // The adapters need to be installed on the new request Message Context AttachmentsAdapter.install(requestMsgCtx); TransportHeadersAdapter.install(requestMsgCtx); SOAPHeadersAdapter.install(requestMsgCtx); Binding binding = (Binding)axisRequestMsgCtx.getProperty(PARAM_BINDING); EndpointInvocationContext eic = InvocationContextFactory.createEndpointInvocationContext(binding); addInvocationListenerFactories(eic); eic.setRequestMessageContext(requestMsgCtx); // WARNING: This should be left disabled for now. This locks the server side // into a single threaded invocation. eic.getRequestMessageContext().setProperty(ServerConstants.SERVER_DISABLE_THREAD_SWITCH, true); if (isMepInOnly(mep)) { if (log.isDebugEnabled()) { log.debug("Detected a one way invocation."); } eic.setIsOneWay(true); endpointCtlr.invokeOneWay(eic); } else if (JavaUtils.isTrueExplicitly(axisRequestMsgCtx.getProperty( AddressingConstants.IS_ADDR_INFO_ALREADY_PROCESSED)) && (axisRequestMsgCtx.getReplyTo() != null && !axisRequestMsgCtx.getReplyTo().hasAnonymousAddress())) { if (log.isDebugEnabled()) { log.debug("Detected an async invocation."); } EndpointCallback ecb = new EndpointCallback(); eic.setCallback(ecb); endpointCtlr.invokeAsync(eic); } else { if (log.isDebugEnabled()) { log.debug("Detected a sync invocation."); } eic = endpointCtlr.invoke(eic); // If this is a two-way exchange, there should already be a // JAX-WS MessageContext for the response. We need to pull // the Message data out of there and set it on the Axis2 // MessageContext. MessageContext responseMsgCtx = eic.getResponseMessageContext(); // Note that responseMsgCtx may be null if the Provider returned null // and no wsdl was specified. // In JAX-WS 2.2 for Providers that return null we should send back // an empty payload, not a SOAPEnvelope. if (responseMsgCtx == null && MessageContextUtils.getJaxwsProviderInterpretNullOneway(requestMsgCtx)) { if (log.isDebugEnabled()) { log.debug("Detected a null return from a Provider, sending back an ack instead of a response."); } sendAckBack(axisRequestMsgCtx); } else { org.apache.axis2.context.MessageContext axisResponseMsgCtx = responseMsgCtx.getAxisMessageContext(); if (loader != null) { responseMsgCtx.setProperty(org.apache.axis2.jaxws.spi.Constants.CACHE_CLASSLOADER, loader); } MessageUtils.putMessageOnMessageContext(responseMsgCtx.getMessage(), axisResponseMsgCtx); OperationContext opCtx = axisResponseMsgCtx.getOperationContext(); opCtx.addMessageContext(axisResponseMsgCtx); // If this is a fault message, we want to throw it as an // exception so that the transport can do the appropriate things if (responseMsgCtx.getMessage().isFault()) { //Rather than create a new AxisFault, we should use the AxisFault that was //created at the causedBy if (responseMsgCtx.getCausedByException() != null) { faultToReturn = responseMsgCtx.getCausedByException(); if (log.isDebugEnabled()) { log.debug("Setting causedByException from response MessageContext"); } } else if (requestMsgCtx.getCausedByException() != null) { faultToReturn = requestMsgCtx.getCausedByException(); if (log.isDebugEnabled()) { log.debug("Setting causedByException from request MessageContext..which indicates an exception occured in the inbound handler processing"); } } else { faultToReturn = new AxisFault("An error was detected during JAXWS processing", axisResponseMsgCtx); if (log.isDebugEnabled()) { log.debug("No causedByException detected"); } } } else { //This assumes that we are on the ultimate execution thread ThreadContextMigratorUtil.performMigrationToContext( Constants.THREAD_CONTEXT_MIGRATOR_LIST_ID, axisResponseMsgCtx); //Create the AxisEngine for the reponse and send it. AxisEngine.send(axisResponseMsgCtx); //This assumes that we are on the ultimate execution thread ThreadContextMigratorUtil.performContextCleanup( Constants.THREAD_CONTEXT_MIGRATOR_LIST_ID, axisResponseMsgCtx); } } } } catch (AxisFault af) { throw af; } catch (Exception e) { ThreadContextMigratorUtil.performThreadCleanup( Constants.THREAD_CONTEXT_MIGRATOR_LIST_ID, axisRequestMsgCtx); //e.printStackTrace(); // TODO. This is throwing a client exception ? // TODO Why are we preserving the stack information ? // Make a webservice exception (which will strip out a unnecessary stuff) WebServiceException wse = ExceptionFactory.makeWebServiceException(e); // The AxisEngine expects an AxisFault throw AxisFault.makeFault(wse); } //This assumes that we are on the ultimate execution thread ThreadContextMigratorUtil .performThreadCleanup(Constants.THREAD_CONTEXT_MIGRATOR_LIST_ID, axisRequestMsgCtx); if (faultToReturn != null) { throw faultToReturn; } } /** * Set context class loader of the current thread. * * @param cl the context ClassLoader for the Thread */ private void setContextClassLoader(final ClassLoader cl) { AccessController.doPrivileged(new PrivilegedAction() { public Object run() { Thread.currentThread().setContextClassLoader(cl); return null; } }); } private void sendAckBack(org.apache.axis2.context.MessageContext axisMsgCtx){ if (log.isDebugEnabled()) { log.debug("sendAckBack entry"); } try { Object requestResponseTransport = axisMsgCtx.getProperty(RequestResponseTransport.TRANSPORT_CONTROL); if (requestResponseTransport != null) { ((RequestResponseTransport) requestResponseTransport).acknowledgeMessage(axisMsgCtx); } }catch (Exception e) { if (log.isDebugEnabled()) { log.debug("Ignoring exception from acknowledgeMessage.", e); } } if (log.isDebugEnabled()) { log.debug("sendAckBack exit"); } } private boolean isMepInOnly(String mep) { boolean inOnly = mep.equals(WSDL2Constants.MEP_URI_ROBUST_IN_ONLY) || mep.equals(WSDL2Constants.MEP_URI_IN_ONLY) || mep.equals(WSDL2Constants.MEP_URI_IN_ONLY) || mep.equals(WSDL2Constants.MEP_URI_ROBUST_IN_ONLY) || mep.equals(WSDL2Constants.MEP_URI_ROBUST_IN_ONLY) || mep.equals(WSDL2Constants.MEP_URI_IN_ONLY); return inOnly; } /** * Retrieves the registered InvocationListenerFactory instances and sets them * on the current EndpointInvocationContext. * @param eic */ void addInvocationListenerFactories(EndpointInvocationContext eic) { eic.setInvocationListenerFactories(InvocationListenerRegistry.getFactories()); } public ClassLoader getCachedClassLoader(org.apache.axis2.context.MessageContext msgContext) { return (ClassLoader) msgContext.getAxisService().getParameterValue(org.apache.axis2.jaxws.spi.Constants.CACHE_CLASSLOADER); } }