| /* |
| * 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); |
| } |
| } |