All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.axis2.jaxws.server.dispatcher.ProviderDispatcher Maven / Gradle / Ivy

There is a newer version: 5.0.22
Show newest version
/*
 * 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.dispatcher;

import org.apache.axis2.Constants;
import org.apache.axis2.jaxws.ExceptionFactory;
import org.apache.axis2.jaxws.context.utils.ContextUtils;
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.i18n.Messages;
import org.apache.axis2.jaxws.marshaller.impl.alt.MethodMarshallerUtils;
import org.apache.axis2.jaxws.message.Block;
import org.apache.axis2.jaxws.message.Message;
import org.apache.axis2.jaxws.message.Protocol;
import org.apache.axis2.jaxws.message.XMLFault;
import org.apache.axis2.jaxws.message.databinding.OMBlock;
import org.apache.axis2.jaxws.message.factory.BlockFactory;
import org.apache.axis2.jaxws.message.factory.DataSourceBlockFactory;
import org.apache.axis2.jaxws.message.factory.MessageFactory;
import org.apache.axis2.jaxws.message.factory.OMBlockFactory;
import org.apache.axis2.jaxws.message.factory.SOAPEnvelopeBlockFactory;
import org.apache.axis2.jaxws.message.factory.SourceBlockFactory;
import org.apache.axis2.jaxws.message.factory.XMLStringBlockFactory;
import org.apache.axis2.jaxws.message.util.XMLFaultUtils;
import org.apache.axis2.jaxws.registry.FactoryRegistry;
import org.apache.axis2.jaxws.server.EndpointCallback;
import org.apache.axis2.jaxws.server.EndpointInvocationContext;
import org.apache.axis2.jaxws.server.InvocationHelper;
import org.apache.axis2.jaxws.server.ServerConstants;
import org.apache.axis2.jaxws.utility.ClassUtils;
import org.apache.axis2.jaxws.utility.DataSourceFormatter;
import org.apache.axis2.jaxws.utility.ExecutorFactory;
import org.apache.axis2.jaxws.utility.SingleThreadedExecutor;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.soap.SOAPEnvelope;

import javax.activation.DataSource;
import javax.xml.soap.SOAPMessage;
import javax.xml.stream.XMLStreamException;
import javax.xml.transform.Source;
import javax.xml.ws.Provider;
import javax.xml.ws.Service;
import javax.xml.ws.WebServiceException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.concurrent.Executor;
import java.util.concurrent.FutureTask;

/**
 * The ProviderDispatcher is used to invoke instances of a target endpoint that implement the {@link
 * javax.xml.ws.Provider} interface.
 * 

* The Provider is a generic class, with certain restrictions on the parameterized type T. This * implementation supports the following types: *

* java.lang.String javax.activation.DataSource javax.xml.soap.SOAPMessage * javax.xml.transform.Source */ public class ProviderDispatcher extends JavaDispatcher { private static Log log = LogFactory.getLog(ProviderDispatcher.class); private BlockFactory _blockFactory = null; // Cache the block factory private Class _providerType = null; // Cache the provider type private Provider providerInstance = null; private Message message = null; private EndpointDescription endpointDesc = null; /** * Constructor * * @param _class * @param serviceInstance */ public ProviderDispatcher(Class _class, Object serviceInstance) { super(_class, serviceInstance); } /* (non-Javadoc) * @see org.apache.axis2.jaxws.server.EndpointDispatcher#execute() */ public MessageContext invoke(MessageContext request) throws Exception { if (log.isDebugEnabled()) { log.debug("Preparing to invoke javax.xml.ws.Provider based endpoint"); log.debug("Invocation pattern: two way, sync"); } initialize(request); providerInstance = getProviderInstance(); Object param = createRequestParameters(request); if (log.isDebugEnabled()) { Class providerType = getProviderType(); final Object input = providerType.cast(param); log.debug("Invoking Provider<" + providerType.getName() + ">"); if (input != null) { log.debug("Parameter type: " + input.getClass().getName()); } else { log.debug("Parameter is NULL"); } } // Invoke the actual Provider.invoke() method boolean faultThrown = false; Throwable fault = null; Object[] input = new Object[] {param}; Object responseParamValue = null; Method target = null; try { target = getJavaMethod(); responseParamValue = invokeTargetOperation(target, input); } catch (Throwable e) { fault = ClassUtils.getRootCause(e); faultThrown = true; } // Create the response MessageContext MessageContext responseMsgCtx = null; if (faultThrown) { // If a fault was thrown, we need to create a slightly different // MessageContext, than in the response path. responseMsgCtx = createFaultResponse(request, fault); setExceptionProperties(responseMsgCtx, target, fault); } else if(responseParamValue == null && MessageContextUtils.getJaxwsProviderInterpretNullOneway(request) && !request.getAxisMessageContext().getAxisService().isWsdlFound()) { // If the Provider returned null, we will interpret this as a one-way op if // - the custom property jaxws.provider.interpretNullAsOneway is true AND // - the operation was NOT wsdl defined. if (log.isDebugEnabled()) { log.debug("detected null return from Provider, " + target + "and operation is not wsdl defined and " + "interpretNullAsOneway property is true."); } // JAXWS 2.2 If Provider returns null, an empty response is given (no SOAPEnvelope) responseMsgCtx = null; // return null, JAXWSMessageReceiver will interpret as one-way } else { responseMsgCtx = createResponse(request, input, responseParamValue); } return responseMsgCtx; } public void invokeOneWay(MessageContext request) { if (log.isDebugEnabled()) { log.debug("Preparing to invoke javax.xml.ws.Provider based endpoint"); log.debug("Invocation pattern: one way"); } initialize(request); providerInstance = getProviderInstance(); Object param = createRequestParameters(request); if (log.isDebugEnabled()) { Class providerType = getProviderType(); final Object input = providerType.cast(param); log.debug("Invoking Provider<" + providerType.getName() + ">"); if (input != null) { log.debug("Parameter type: " + input.getClass().getName()); } else { log.debug("Parameter is NULL"); } } ExecutorFactory ef = (ExecutorFactory) FactoryRegistry.getFactory(ExecutorFactory.class); Executor executor = ef.getExecutorInstance(ExecutorFactory.SERVER_EXECUTOR); // If the property has been set to disable thread switching, then we can // do so by using a SingleThreadedExecutor instance to continue processing // work on the existing thread. Boolean disable = (Boolean) request.getProperty(ServerConstants.SERVER_DISABLE_THREAD_SWITCH); if (disable != null && disable.booleanValue()) { if (log.isDebugEnabled()) { log.debug("Server side thread switch disabled. Setting Executor to the SingleThreadedExecutor."); } executor = new SingleThreadedExecutor(); } Method m = getJavaMethod(); Object[] params = new Object[] {param}; EndpointInvocationContext eic = (EndpointInvocationContext) request.getInvocationContext(); ClassLoader cl = Thread.currentThread().getContextClassLoader(); AsyncInvocationWorker worker = new AsyncInvocationWorker(m, params, cl, eic); FutureTask task = new FutureTask(worker); executor.execute(task); return; } public void invokeAsync(MessageContext request, EndpointCallback callback) { if (log.isDebugEnabled()) { log.debug("Preparing to invoke javax.xml.ws.Provider based endpoint"); log.debug("Invocation pattern: two way, async"); } initialize(request); providerInstance = getProviderInstance(); Object param = createRequestParameters(request); if (log.isDebugEnabled()) { Class providerType = getProviderType(); final Object input = providerType.cast(param); log.debug("Invoking Provider<" + providerType.getName() + ">"); if (input != null) { log.debug("Parameter type: " + input.getClass().getName()); } else { log.debug("Parameter is NULL"); } } ExecutorFactory ef = (ExecutorFactory) FactoryRegistry.getFactory(ExecutorFactory.class); Executor executor = ef.getExecutorInstance(ExecutorFactory.SERVER_EXECUTOR); // If the property has been set to disable thread switching, then we can // do so by using a SingleThreadedExecutor instance to continue processing // work on the existing thread. Boolean disable = (Boolean) request.getProperty(ServerConstants.SERVER_DISABLE_THREAD_SWITCH); if (disable != null && disable.booleanValue()) { if (log.isDebugEnabled()) { log.debug("Server side thread switch disabled. Setting Executor to the SingleThreadedExecutor."); } executor = new SingleThreadedExecutor(); } Method m = getJavaMethod(); Object[] params = new Object[] {param}; EndpointInvocationContext eic = (EndpointInvocationContext) request.getInvocationContext(); ClassLoader cl = Thread.currentThread().getContextClassLoader(); AsyncInvocationWorker worker = new AsyncInvocationWorker(m, params, cl, eic); FutureTask task = new FutureTask(worker); executor.execute(task); return; } public Object createRequestParameters(MessageContext request) { // First we need to know what kind of Provider instance we're going // to be invoking against Class providerType = getProviderType(); // REVIEW: This assumes there is only one endpoint description on the service. Is that always the case? EndpointDescription endpointDesc = request.getEndpointDescription(); // Now that we know what kind of Provider we have, we can create the // right type of Block for the request parameter data Object requestParamValue = null; Message message = request.getMessage(); if (message != null) { // Enable MTOM if indicated if (endpointDesc.isMTOMEnabled()) { message.setMTOMEnabled(true); } // Save off the protocol info so we can use it when creating the response message. Protocol messageProtocol = message.getProtocol(); // Determine what type blocks we want to create (String, Source, etc) based on Provider Type BlockFactory factory = createBlockFactory(providerType); Service.Mode providerServiceMode = endpointDesc.getServiceMode(); if (providerServiceMode != null && providerServiceMode == Service.Mode.MESSAGE) { if (log.isDebugEnabled()) { log.debug("Provider type is " + providerType.getClass().getName()); } if (providerType.equals(SOAPMessage.class)) { // We can get the SOAPMessage directly from the message itself if (log.isDebugEnabled()) { log.debug("Number Message attachments=" + message.getAttachmentIDs().size()); } } if (providerType.equals(OMElement.class)) { // TODO avoid call to message.getValue due to // current unnecessary message transformation in // message.getValue. Once message.getValue is fixed, // this code block is unnecessary, though no harm // will come if it remains. rott requestParamValue = message.getAsOMElement(); } else { requestParamValue = message.getValue(null, factory); } if (requestParamValue == null) { if (log.isDebugEnabled()) { log.debug( "There are no elements to unmarshal. ProviderDispatch will pass a null as input"); } } } else { // If it is not MESSAGE, then it is PAYLOAD (which is the default); only work with the body Block block = message.getBodyBlock(null, factory); if (block != null) { try { requestParamValue = block.getBusinessObject(true); if (requestParamValue instanceof OMBlock) { // Provider is not supported, so we need to get the OMElement out if (log.isDebugEnabled()) { log.debug("request parameter business object is OMBlock. Now retrieving OMElement from OMBlock."); } requestParamValue = ((OMBlock)requestParamValue).getOMElement(); } } catch (WebServiceException e) { throw ExceptionFactory.makeWebServiceException(e); } catch (XMLStreamException e) { throw ExceptionFactory.makeWebServiceException(e); } } else { if (log.isDebugEnabled()) { log.debug( "No body blocks in SOAPMessage, Calling provider method with null input parameters"); } requestParamValue = null; } } } return requestParamValue; } /** * Create a MessageContext for the response. This could be a normal response * or a fault response depending on the characteristics of output * @param request MessageContext * @param input[] input Objects * @param output Object representing output of Provider * @return MessageContext for normal or fault path */ public MessageContext createResponse(MessageContext request, Object[] input, Object output) { if (log.isDebugEnabled()) { log.debug("Start createResponse"); } Message m; EndpointDescription endpointDesc = null; try { endpointDesc = request.getEndpointDescription(); Service.Mode mode = endpointDesc.getServiceMode(); m = createMessageFromValue(output, request.getMessage().getProtocol(), mode); } catch (Throwable t) { if (log.isDebugEnabled()) { log.debug("Throwable caught"); log.debug("Throwable=" + t); } throw ExceptionFactory.makeWebServiceException(t); } MessageContext response = null; try { // Enable MTOM if indicated if (endpointDesc.isMTOMEnabled()) { m.setMTOMEnabled(true); } if (!m.isFault()) { if (log.isDebugEnabled()) { log.debug("Non-Fault Response MessageContext is created."); } response = MessageContextUtils.createResponseMessageContext(request); } else { if (log.isDebugEnabled()) { log.debug("Fault Response MessageContext is created."); } response = MessageContextUtils.createFaultMessageContext(request); } initMessageContext(response, m, output); } catch (RuntimeException e) { if (log.isDebugEnabled()) { log.debug("Throwable caught creating Response MessageContext"); log.debug("Throwable=" + e); } } finally { if (log.isDebugEnabled()) { log.debug("End createResponse"); } } return response; } protected void initMessageContext(MessageContext responseMsgCtx, Message m, Object output) { responseMsgCtx.setMessage(m); if(output instanceof DataSource){ responseMsgCtx.setProperty(Constants.Configuration.MESSAGE_FORMATTER, new DataSourceFormatter(((DataSource)output).getContentType())); } } public MessageContext createFaultResponse(MessageContext request, Throwable fault) { if (log.isDebugEnabled()) { log.debug("Create XMLFault for createFaultResponse"); } // call the InvocationListener instances before marshalling // the fault into a message Throwable faultMessage = InvocationHelper.determineMappedException(fault, request); if(faultMessage != null) { fault = faultMessage; } Message m; try { EndpointDescription endpointDesc = request.getEndpointDescription(); Service.Mode mode = endpointDesc.getServiceMode(); XMLFault xmlFault = MethodMarshallerUtils.createXMLFaultFromSystemException(fault); m = createMessageFromValue(xmlFault, request.getMessage().getProtocol(), mode); } catch (Exception e) { throw ExceptionFactory.makeWebServiceException(e); } MessageContext response = MessageContextUtils.createFaultMessageContext(request); response.setMessage(m); return response; } /** * Get the endpoint provider instance * * @return Provider * @throws Exception */ public Provider getProvider() throws Exception { Provider p = getProviderInstance(); setProvider(p); return p; } /** * Set the endpoint provider instance * * @param _provider */ public void setProvider(Provider _provider) { this.providerInstance = _provider; } /** * Get the parameter for a given endpoint invocation * * @return * @throws Exception */ public Message getMessage() throws Exception { return message; } /** * Set the parameter for a given endpoint invocation * * @param msg */ public void setMessage(Message msg) { this.message = msg; } /* * Create a Message object out of the value object that was returned. */ private Message createMessageFromValue(Object value, Protocol protocol, Service.Mode mode) throws Exception { MessageFactory msgFactory = (MessageFactory)FactoryRegistry.getFactory(MessageFactory.class); Message message = null; if (value != null) { Class providerType = getProviderType(); BlockFactory factory = createBlockFactory(providerType); if (value instanceof XMLFault) { if (log.isDebugEnabled()) { log.debug("Creating message from XMLFault"); } message = msgFactory.create(protocol); message.setXMLFault((XMLFault)value); } else if (mode != null && mode == Service.Mode.MESSAGE) { // For MESSAGE mode, work with the entire message, Headers and Body // This is based on logic in org.apache.axis2.jaxws.client.XMLDispatch.createMessageFromBundle() if (value instanceof SOAPMessage) { if (log.isDebugEnabled()) { log.debug("Creating message from SOAPMessage"); } message = msgFactory.createFrom((SOAPMessage)value); } else if (value instanceof SOAPEnvelope) { // The value from the provider is already an SOAPEnvelope OMElement, so // it doesn't need to be parsed into one. if (log.isDebugEnabled()) { log.debug("Creating message from OMElement"); } message = msgFactory.createFrom((SOAPEnvelope) value, protocol); } else { if (log.isDebugEnabled()) { log.debug("Creating message using " + factory); } Block block = factory.createFrom(value, null, null); message = msgFactory.createFrom(block, null, protocol); } } else { // PAYLOAD mode deals only with the body of the message. if (log.isDebugEnabled()) { log.debug("Creating message (payload) using " + factory); } Block block = factory.createFrom(value, null, null); message = msgFactory.create(protocol); if (XMLFaultUtils.containsFault(block)) { if (log.isDebugEnabled()) { log.debug("The response block created contained a fault. Converting to an XMLFault object."); } // If the Provider returned a fault, then let's correct the output and // put an XMLFault on the Message. This makes it easier for downstream // consumers to get the SOAPFault from the OM SOAPEnvelope. XMLFault fault = XMLFaultUtils.createXMLFault(block, message.getProtocol()); message.setXMLFault(fault); } else { message.setBodyBlock(block); } } } if (message == null) { // If we didn't create a message above (because there was no value), create one here message = msgFactory.create(protocol); } return message; } /* * Determine the Provider type for this instance */ private Provider getProviderInstance() { Class clazz = getProviderType(); if (!isValidProviderType(clazz)) { // TODO This will change once deployment code it in place throw ExceptionFactory.makeWebServiceException(Messages.getMessage("InvalidProvider", clazz.getName())); } Provider provider = null; if (clazz == String.class) { provider = (Provider)serviceInstance; } else if (clazz == Source.class) { provider = (Provider)serviceInstance; } else if (clazz == SOAPMessage.class) { provider = (Provider)serviceInstance; } else if (clazz == DataSource.class) { provider = (Provider)serviceInstance; } else if (clazz == OMElement.class) { provider = (Provider)serviceInstance; } if (provider == null) { throw ExceptionFactory.makeWebServiceException( Messages.getMessage("InvalidProviderCreate", clazz.getName())); } return provider; } /* * Get the provider type from a given implemention class instance */ private Class getProviderType() { if (_providerType != null) { return _providerType; } Class providerType = null; Type[] giTypes = serviceImplClass.getGenericInterfaces(); for (Type giType : giTypes) { ParameterizedType paramType = null; try { paramType = (ParameterizedType)giType; } catch (ClassCastException e) { // this may not be a parameterized interface continue; } Class interfaceName = (Class)paramType.getRawType(); if (interfaceName == javax.xml.ws.Provider.class) { if (paramType.getActualTypeArguments().length > 1) { throw ExceptionFactory.makeWebServiceException(Messages.getMessage("pTypeErr")); } providerType = (Class)paramType.getActualTypeArguments()[0]; } } _providerType = providerType; return providerType; } /* * Validate whether or not the Class passed in is a valid type of * javax.xml.ws.Provider. Per the JAX-WS 2.0 specification, the * parameterized type of a Provider can only be: * * javax.xml.transform.Source * javax.xml.soap.SOAPMessage * javax.activation.DataSource * org.apache.axiom.om.OMElement * * We've also added support for String types which is NOT dictated * by the spec. */ private boolean isValidProviderType(Class clazz) { boolean valid = clazz == String.class || clazz == SOAPMessage.class || clazz == Source.class || clazz == DataSource.class || clazz == OMElement.class; if (!valid) { if (log.isDebugEnabled()) { log.debug("Class " + clazz.getName() + " is not a valid Provider type"); } } return valid; } /* * Given a target class type for a payload, load the appropriate BlockFactory. */ private BlockFactory createBlockFactory(Class type) { if (_blockFactory != null) { return _blockFactory; } if (type.equals(String.class)) { _blockFactory = (XMLStringBlockFactory)FactoryRegistry.getFactory( XMLStringBlockFactory.class); } else if (type.equals(DataSource.class)) { _blockFactory = (DataSourceBlockFactory)FactoryRegistry.getFactory( DataSourceBlockFactory.class); } else if (type.equals(Source.class)) { _blockFactory = (SourceBlockFactory)FactoryRegistry.getFactory( SourceBlockFactory.class); } else if (type.equals(SOAPMessage.class)) { _blockFactory = (SOAPEnvelopeBlockFactory)FactoryRegistry.getFactory( SOAPEnvelopeBlockFactory.class); } else if (type.equals(OMElement.class)) { _blockFactory = (OMBlockFactory)FactoryRegistry.getFactory( OMBlockFactory.class); } else { throw ExceptionFactory.makeWebServiceException( Messages.getMessage("bFactoryErr",type.getName())); } return _blockFactory; } protected Method getJavaMethod() { Method m = null; try { m = providerInstance.getClass().getMethod("invoke", new Class[] {getProviderType()}); } catch (SecurityException e) { throw ExceptionFactory.makeWebServiceException(e); } catch (NoSuchMethodException e) { throw ExceptionFactory.makeWebServiceException(e); } return m; } protected void initialize(MessageContext mc) { mc.setOperationName(mc.getAxisMessageContext().getAxisOperation().getName()); endpointDesc = mc.getEndpointDescription(); if (endpointDesc.isMTOMEnabled()) { mc.getMessage().setMTOMEnabled(true); } //Set SOAP Operation Related properties in SOAPMessageContext. ContextUtils.addWSDLProperties_provider(mc); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy