| /* |
| * 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.client.dispatch; |
| |
| import org.apache.axis2.addressing.EndpointReference; |
| import org.apache.axis2.client.ServiceClient; |
| import org.apache.axis2.description.AxisOperation; |
| import org.apache.axis2.description.AxisService; |
| import org.apache.axis2.description.Parameter; |
| import org.apache.axis2.engine.AxisConfiguration; |
| import org.apache.axis2.jaxws.BindingProvider; |
| import org.apache.axis2.jaxws.ExceptionFactory; |
| import org.apache.axis2.jaxws.client.async.AsyncResponse; |
| import org.apache.axis2.jaxws.core.InvocationContext; |
| import org.apache.axis2.jaxws.core.InvocationContextFactory; |
| import org.apache.axis2.jaxws.core.MessageContext; |
| import org.apache.axis2.jaxws.core.controller.InvocationController; |
| import org.apache.axis2.jaxws.core.controller.InvocationControllerFactory; |
| import org.apache.axis2.jaxws.description.EndpointDescription; |
| import org.apache.axis2.jaxws.description.EndpointInterfaceDescription; |
| import org.apache.axis2.jaxws.description.OperationDescription; |
| import org.apache.axis2.jaxws.i18n.Messages; |
| import org.apache.axis2.jaxws.marshaller.impl.alt.MethodMarshallerUtils; |
| import org.apache.axis2.jaxws.message.Message; |
| import org.apache.axis2.jaxws.registry.FactoryRegistry; |
| import org.apache.axis2.jaxws.spi.Binding; |
| import org.apache.axis2.jaxws.spi.Constants; |
| import org.apache.axis2.jaxws.spi.ServiceDelegate; |
| import org.apache.axis2.jaxws.spi.migrator.ApplicationContextMigratorUtil; |
| import org.apache.axis2.transport.http.HTTPConstants; |
| import org.apache.commons.logging.Log; |
| import org.apache.commons.logging.LogFactory; |
| import org.w3c.dom.Node; |
| |
| import javax.xml.namespace.QName; |
| import javax.xml.soap.SOAPBody; |
| import javax.xml.soap.SOAPException; |
| import javax.xml.soap.SOAPMessage; |
| import javax.xml.transform.dom.DOMSource; |
| import javax.xml.ws.AsyncHandler; |
| import javax.xml.ws.ProtocolException; |
| import javax.xml.ws.Response; |
| import javax.xml.ws.Service.Mode; |
| import javax.xml.ws.WebServiceException; |
| import javax.xml.ws.WebServiceFeature; |
| import javax.xml.ws.http.HTTPBinding; |
| import javax.xml.ws.soap.SOAPBinding; |
| |
| import java.io.IOException; |
| import java.util.concurrent.Executor; |
| import java.util.concurrent.Future; |
| |
| public abstract class BaseDispatch<T> extends BindingProvider |
| implements javax.xml.ws.Dispatch { |
| |
| private static Log log = LogFactory.getLog(BaseDispatch.class); |
| |
| protected InvocationController ic; |
| |
| protected ServiceClient serviceClient; |
| |
| protected Mode mode; |
| |
| protected BaseDispatch(ServiceDelegate svcDelgate, |
| EndpointDescription epDesc, |
| EndpointReference epr, |
| String addressingNamespace, |
| WebServiceFeature... features) { |
| super(svcDelgate, epDesc, epr, addressingNamespace, features); |
| |
| InvocationControllerFactory icf = (InvocationControllerFactory) FactoryRegistry.getFactory(InvocationControllerFactory.class); |
| ic = icf.getInvocationController(); |
| |
| if (ic == null) { |
| throw new WebServiceException(Messages.getMessage("missingInvocationController")); |
| } |
| } |
| |
| /** |
| * Take the input object and turn it into an OMElement so that it can be sent. |
| * |
| * @param value |
| * @return |
| */ |
| protected abstract Message createMessageFromValue(Object value); |
| |
| /** |
| * Given a message, return the business object based on the requestor's required format (PAYLOAD |
| * vs. MESSAGE) and datatype. |
| * |
| * @param message |
| * @return |
| */ |
| protected abstract Object getValueFromMessage(Message message); |
| |
| /** |
| * Creates an instance of the AsyncListener that is to be used for waiting for async responses. |
| * |
| * @return a configured AsyncListener instance |
| */ |
| protected abstract AsyncResponse createAsyncResponseListener(); |
| |
| |
| /** |
| * Note to developer: When making a change or fix to this method, please consider |
| * all 5 Proxy/Dispatch "invoke" methods now available in JAX-WS. For Dispatch, |
| * these are: |
| * 1) Synchronous invoke() |
| * 2) invokeOneWay() |
| * 3) invokeAsynch (Future) |
| * 4) invokeAsynch (Callback) |
| * |
| * For Proxy: |
| * 5) invokeSEIMethod() |
| * |
| */ |
| public Object invoke(Object obj) throws WebServiceException { |
| |
| // Catch all exceptions and rethrow an appropriate WebService Exception |
| try { |
| if (log.isDebugEnabled()) { |
| log.debug("Entered synchronous invocation: BaseDispatch.invoke()"); |
| } |
| |
| // Create the InvocationContext instance for this request/response flow. |
| InvocationContext invocationContext = |
| InvocationContextFactory.createInvocationContext(null); |
| invocationContext.setServiceClient(serviceClient); |
| |
| // Create the MessageContext to hold the actual request message and its |
| // associated properties |
| MessageContext requestMsgCtx = new MessageContext(); |
| requestMsgCtx.getAxisMessageContext().setProperty(BINDING_PROVIDER, this); |
| requestMsgCtx.setEndpointDescription(getEndpointDescription()); |
| invocationContext.setRequestMessageContext(requestMsgCtx); |
| |
| /* |
| * TODO: review: make sure the handlers are set on the InvocationContext |
| * This implementation of the JAXWS runtime does not use Endpoint, which |
| * would normally be the place to initialize and store the handler list. |
| * In lieu of that, we will have to intialize and store them on the |
| * InvocationContext. also see the InvocationContextFactory. On the client |
| * side, the binding is not yet set when we call into that factory, so the |
| * handler list doesn't get set on the InvocationContext object there. Thus |
| * we gotta do it here. |
| */ |
| |
| // be sure to use whatever handlerresolver is registered on the Service |
| Binding binding = (Binding) getBinding(); |
| invocationContext.setHandlers(binding.getHandlerChain()); |
| |
| initMessageContext(obj, requestMsgCtx); |
| |
| // call common init method for all invoke* paths |
| preInvokeInit(invocationContext); |
| |
| // Migrate the properties from the client request context bag to |
| // the request MessageContext. |
| ApplicationContextMigratorUtil.performMigrationToMessageContext( |
| Constants.APPLICATION_CONTEXT_MIGRATOR_LIST_ID, |
| getRequestContext(), requestMsgCtx); |
| |
| // Perform the WebServiceFeature configuration requested by the user. |
| binding.configure(requestMsgCtx, this); |
| |
| // Initializing the message context above will put the outbound message onto the messageContext |
| // Determine the operation if possible from the outbound message. If it can not be determined |
| // it will be set to null. In this case, an anonymous operation will be used. Note that determining |
| // the operation will mean deserializing the message. That means that any WebServiceFeatures must have |
| // been configured first so that any relevant configurations (such as MTOM) have been initialized prior to |
| // the message being deserialized. This is particularly true for Dispatch<JAXB Element>. |
| requestMsgCtx.setOperationDescription(getOperationDescriptionForDispatch(requestMsgCtx)); |
| |
| // Send the request using the InvocationController |
| ic.invoke(invocationContext); |
| |
| MessageContext responseMsgCtx = invocationContext.getResponseMessageContext(); |
| responseMsgCtx.setEndpointDescription(requestMsgCtx.getEndpointDescription()); |
| |
| // Migrate the properties from the response MessageContext back |
| // to the client response context bag. |
| ApplicationContextMigratorUtil.performMigrationFromMessageContext( |
| Constants.APPLICATION_CONTEXT_MIGRATOR_LIST_ID, |
| getResponseContext(), responseMsgCtx); |
| |
| if (hasFaultResponse(responseMsgCtx)) { |
| WebServiceException wse = BaseDispatch.getFaultResponse(responseMsgCtx); |
| throw wse; |
| } |
| |
| // Get the return object |
| Object returnObj = null; |
| try { |
| Message responseMsg = responseMsgCtx.getMessage(); |
| returnObj = getValueFromMessage(responseMsg); |
| } |
| finally { |
| // Free the incoming input stream |
| try { |
| responseMsgCtx.freeInputStream(); |
| } |
| catch (Throwable t) { |
| throw ExceptionFactory.makeWebServiceException(t); |
| } |
| } |
| |
| //Check to see if we need to maintain session state |
| checkMaintainSessionState(requestMsgCtx, invocationContext); |
| |
| if (log.isDebugEnabled()) { |
| log.debug("Synchronous invocation completed: BaseDispatch.invoke()"); |
| } |
| |
| return returnObj; |
| } catch (WebServiceException e) { |
| if (log.isDebugEnabled()) { |
| log.debug("BaseDispatch.invoke(): Synchronous invocation failed, " |
| + "caught a WebServiceException: ", e); |
| } |
| throw e; |
| } catch (Exception e) { |
| // All exceptions are caught and rethrown as a WebServiceException |
| if (log.isDebugEnabled()) { |
| log.debug("BaseDispatch.invoke(): Synchronous invocation failed, caught an Exception, " + |
| "wrapping into a WebServiceException. Exception caught: ", e); |
| } |
| throw ExceptionFactory.makeWebServiceException(e); |
| } |
| } |
| |
| /** |
| * Given a JAXWS Message Context which contains an outbound service-requester Message for a Dispatch client, |
| * determine the OperationDescription for the operation contained in that Dispatch message. |
| * |
| * Note that operation resolution can be disabled by a property setting. |
| * @see org.apache.axis2.jaxws.Constants.DISPATCH_CLIENT_OUTBOUND_RESOLUTION |
| * |
| * @param requestMessageCtx JAXWS Message Context containing the outbound Dispatch message |
| * @return the OperationDescription corresponding to the operation contained in the Dispatch message, or null |
| * if it can not be determined or if dispatch operation resolution is disabled via a property. |
| */ |
| private OperationDescription getOperationDescriptionForDispatch(MessageContext requestMessageCtx) { |
| OperationDescription operationDesc = null; |
| if (dispatchOperationResolutionEnabled()) { |
| EndpointInterfaceDescription endpointInterfaceDesc = getEndpointDescription().getEndpointInterfaceDescription(); |
| // The SEI interface could be null (for example if there was no SEI and all the ports were dynamically added). |
| // If there is an SEI, then try to determine the operation for the outbound dispatch message. |
| if (endpointInterfaceDesc != null) { |
| QName bodyElementQName = getBodyElementQNameFromDispatchMessage(requestMessageCtx); |
| operationDesc = determineOperationDescFromBodyElementQName(endpointInterfaceDesc, bodyElementQName); |
| } |
| } |
| return operationDesc; |
| } |
| |
| /** |
| * Returns the OperationDescription corresponding to the bodyElementQName passed in. What that body element corresponds to |
| * depends on the type of the message: |
| * - For Doc/Lit/Wrapped, the body element is the operation name |
| * - For Doc/Lit/Bare, the body element is the element name contained in the wsdl:message wsdl:part |
| * - For RPC, the body element is effectively the operation name. |
| * |
| * @param endpointInterfaceDesc The interface (i.e. SEI) on which to search for the operation |
| * @param bodyElementQName the QName of the first body element for which to find the operation |
| * |
| * @return The OperationDescription corresponding to the body element QName or null if one can not be found. |
| */ |
| private OperationDescription determineOperationDescFromBodyElementQName(EndpointInterfaceDescription endpointInterfaceDesc, |
| QName bodyElementQName) { |
| OperationDescription operationDesc = null; |
| |
| // If there's no bodyElementQName for us to work with, there's nothing more we can do. |
| if (bodyElementQName != null) { |
| // This logic mimics the code in SOAPMessageBodyBasedOperationDispatcher.findOperation. We will look for |
| // the AxisOperation corresponding to the body element name. Note that we are searching for the AxisOperation instead |
| // of searching through the OperationDescriptions so that we can use the getOperationByMessageElementQName |
| // for the Doc/Lit/Bare case. Once we have the AxisOperation, we'll use that to find the Operation Description. |
| AxisService axisService = endpointInterfaceDesc.getEndpointDescription().getAxisService(); |
| AxisOperation axisOperation = null; |
| |
| // Doc/Lit/Wrapped and RPC, the operation name is the first body element qname |
| axisOperation = axisService.getOperation(new QName(bodyElementQName.getLocalPart())); |
| |
| if (axisOperation == null) { |
| // Doc/Lit/Bare, the first body element qname is the element name contained in the wsdl:message part |
| axisOperation = axisService.getOperationByMessageElementQName(bodyElementQName); |
| } |
| |
| if (axisOperation == null) { |
| // Not sure why we wouldn't have found the operation above using just the localPart rather than the full QName used here, |
| // but this is what SOAPMessageBodyBasedOperationDispatcher.findOperation does. |
| axisOperation = axisService.getOperation(bodyElementQName); |
| } |
| |
| // If we found an axis operation, then find the operation description that corresponds to it |
| if (axisOperation != null) { |
| OperationDescription allOpDescs[] = endpointInterfaceDesc.getDispatchableOperations(); |
| for (OperationDescription checkOpDesc : allOpDescs ) { |
| AxisOperation checkAxisOperation = checkOpDesc.getAxisOperation(); |
| if (checkAxisOperation == axisOperation) { |
| operationDesc = checkOpDesc; |
| break; |
| } |
| } |
| } |
| } |
| return operationDesc; |
| } |
| |
| /** |
| * Answer if operation resolution on outbound messages for dispatch clients should be done. The default value |
| * is TRUE, enabling operation resolution. Resolution can be disabled via a property on the AxisConfiguration |
| * or on the RequestContext. |
| * |
| * Operation resolution is also disabled if a non-null value is specified on the request context for the Action |
| * |
| * @see org.apache.axis2.jaxws.Constants.DISPATCH_CLIENT_OUTBOUND_RESOLUTION |
| * @see javax.xml.ws.BindingProvider.SOAPACTION_USE_PROPERTY |
| * @see javax.xml.ws.BindingProvider.SOAPACTION_URI_PROPERTY |
| * |
| * @return true if operation resolution should be performed on outbound |
| */ |
| private boolean dispatchOperationResolutionEnabled() { |
| boolean resolutionEnabled = true; |
| |
| // See if any properties disabled operation resolution |
| // Check for System property setting |
| String flagValue = getProperty(org.apache.axis2.jaxws.Constants.DISPATCH_CLIENT_OUTBOUND_RESOLUTION); |
| |
| // If no System property was set, see if one was set on this request context. |
| if (flagValue == null) { |
| flagValue = (String) getRequestContext().get(org.apache.axis2.jaxws.Constants.DISPATCH_CLIENT_OUTBOUND_RESOLUTION); |
| } |
| |
| // If any property was set, check the value. |
| if (flagValue != null) { |
| if ("false".equalsIgnoreCase(flagValue)) { |
| resolutionEnabled = false; |
| } else if ("true".equalsIgnoreCase(flagValue)) { |
| resolutionEnabled = true; |
| } |
| } |
| |
| // If a property didn't disable resolution, then see if a URI value was specified. |
| // If so, we'll use that later and there's no need to do operation resolution. |
| if (resolutionEnabled) { |
| Boolean useSoapAction = (Boolean) getRequestContext().get(SOAPACTION_USE_PROPERTY); |
| if (useSoapAction != null && useSoapAction.booleanValue()) { |
| String soapAction = (String) getRequestContext().get(SOAPACTION_URI_PROPERTY); |
| if (soapAction != null) { |
| resolutionEnabled = false; |
| } |
| } |
| } |
| return resolutionEnabled; |
| } |
| |
| /** |
| * Retrieve the specified property from the AxisConfiguration. |
| * |
| * @param key The property to retrieve from the AxisConfiguration |
| * @return the value associated with the property or null if the property did not exist on the configuration. |
| */ |
| private String getProperty(String key) { |
| String propertyValue = null; |
| AxisConfiguration axisConfig = serviceDelegate.getServiceDescription().getAxisConfigContext().getAxisConfiguration(); |
| Parameter parameter = axisConfig.getParameter(key); |
| if (parameter != null) { |
| propertyValue = (String) parameter.getValue(); |
| } |
| return propertyValue; |
| } |
| |
| |
| /** |
| * Given a JAXWS Message Context which contains an outbound service-requester Message for a Dispatch client, |
| * determine the QName of the first body element contained in that message. |
| * |
| * @param requestMessageCtx requestMessageCtx JAXWS Message Context containing the outbound Dispatch message |
| * @return the QName of the first body element contained in the outbound Dispatch message, or null if it |
| * can not be determined. |
| */ |
| QName getBodyElementQNameFromDispatchMessage(MessageContext requestMessageCtx) { |
| QName bodyElementQName = null; |
| Message dispatchMessage = requestMessageCtx.getMessage(); |
| SOAPMessage soapMessage = dispatchMessage.getAsSOAPMessage(); |
| try { |
| SOAPBody soapBody = soapMessage.getSOAPBody(); |
| Node firstElement = soapBody.getFirstChild(); |
| // A Doc/Lit/Bare message may not have a firsElement. The soap:Body element may be empty if there |
| // are no arguments to the operation. |
| if (firstElement != null) { |
| String ns = firstElement.getNamespaceURI(); |
| String lp= firstElement.getLocalName(); |
| // A Doc/Lit/Bare message may not have a localPart on the element. That can happen if the first element |
| // is the argument value and there is no wrapper element surrounding it. |
| if (lp != null) { |
| bodyElementQName = new QName(ns, lp); |
| } |
| } |
| } catch (SOAPException e) { |
| if (log.isDebugEnabled()) { |
| log.debug("Unabled to get the first body element from the outbound dispatch message", e); |
| } |
| } |
| return bodyElementQName; |
| } |
| |
| protected void initMessageContext(Object obj, MessageContext requestMsgCtx) { |
| Message requestMsg = createRequestMessage(obj); |
| setupMessageProperties(requestMsg); |
| requestMsgCtx.setMessage(requestMsg); |
| // handle HTTP_REQUEST_METHOD property |
| String method = (String)requestContext.get(javax.xml.ws.handler.MessageContext.HTTP_REQUEST_METHOD); |
| if (method != null) { |
| requestMsgCtx.setProperty(org.apache.axis2.Constants.Configuration.HTTP_METHOD, method); |
| } |
| } |
| |
| /** |
| * Note to developer: When making a change or fix to this method, please consider |
| * all 5 Proxy/Dispatch "invoke" methods now available in JAX-WS. For Dispatch, |
| * these are: |
| * 1) Synchronous invoke() |
| * 2) invokeOneWay() |
| * 3) invokeAsynch (Future) |
| * 4) invokeAsynch (Callback) |
| * |
| * For Proxy: |
| * 5) invokeSEIMethod() |
| * |
| */ |
| public void invokeOneWay(Object obj) throws WebServiceException { |
| |
| // All exceptions are caught and rethrown as a WebServiceException |
| MessageContext requestMsgCtx = null; |
| try { |
| if (log.isDebugEnabled()) { |
| log.debug("Entered one-way invocation: BaseDispatch.invokeOneWay()"); |
| } |
| |
| // Create the InvocationContext instance for this request/response flow. |
| InvocationContext invocationContext = |
| InvocationContextFactory.createInvocationContext(null); |
| invocationContext.setServiceClient(serviceClient); |
| |
| // Create the MessageContext to hold the actual request message and its |
| // associated properties |
| requestMsgCtx = new MessageContext(); |
| requestMsgCtx.getAxisMessageContext().setProperty(BINDING_PROVIDER, this); |
| requestMsgCtx.setEndpointDescription(getEndpointDescription()); |
| invocationContext.setRequestMessageContext(requestMsgCtx); |
| |
| /* |
| * TODO: review: make sure the handlers are set on the InvocationContext |
| * This implementation of the JAXWS runtime does not use Endpoint, which |
| * would normally be the place to initialize and store the handler list. |
| * In lieu of that, we will have to intialize and store them on the |
| * InvocationContext. also see the InvocationContextFactory. On the client |
| * side, the binding is not yet set when we call into that factory, so the |
| * handler list doesn't get set on the InvocationContext object there. Thus |
| * we gotta do it here. |
| */ |
| |
| // be sure to use whatever handlerresolver is registered on the Service |
| Binding binding = (Binding) getBinding(); |
| invocationContext.setHandlers(binding.getHandlerChain()); |
| |
| initMessageContext(obj, requestMsgCtx); |
| |
| /* |
| * if SESSION_MAINTAIN_PROPERTY is true, and the client app has explicitly set a HEADER_COOKIE on the request context, assume the client |
| * app is expecting the HEADER_COOKIE to be the session id. If we were establishing a new session, no cookie would be sent, and the |
| * server would reply with a "Set-Cookie" header, which is copied as a "Cookie"-keyed property to the service context during response. |
| * In this case, if we succeed in using an existing server session, no "Set-Cookie" header will be returned, and therefore no |
| * "Cookie"-keyed property would be set on the service context. So, let's copy our request context HEADER_COOKIE key to the service |
| * context now to prevent the "no cookie" exception in BindingProvider.setupSessionContext. It is possible the server does not support |
| * sessions, in which case no error occurs, but the client app would assume it is participating in a session. |
| */ |
| if ((requestContext.containsKey(BindingProvider.SESSION_MAINTAIN_PROPERTY)) && ((Boolean)requestContext.get(BindingProvider.SESSION_MAINTAIN_PROPERTY))) { |
| if ((requestContext.containsKey(HTTPConstants.HEADER_COOKIE)) && (requestContext.get(HTTPConstants.HEADER_COOKIE) != null)) { |
| if (invocationContext.getServiceClient().getServiceContext().getProperty(HTTPConstants.HEADER_COOKIE) == null) { |
| invocationContext.getServiceClient().getServiceContext().setProperty(HTTPConstants.HEADER_COOKIE, requestContext.get(HTTPConstants.HEADER_COOKIE)); |
| if (log.isDebugEnabled()) { |
| log.debug("Client-app defined Cookie property (assume to be session cookie) on request context copied to service context." + |
| " Caution: server may or may not support sessions, but client app will not be informed when not supported."); |
| } |
| } |
| } |
| } |
| |
| // call common init method for all invoke* paths |
| preInvokeInit(invocationContext); |
| |
| // Migrate the properties from the client request context bag to |
| // the request MessageContext. |
| ApplicationContextMigratorUtil.performMigrationToMessageContext( |
| Constants.APPLICATION_CONTEXT_MIGRATOR_LIST_ID, |
| getRequestContext(), requestMsgCtx); |
| |
| // Perform the WebServiceFeature configuration requested by the user. |
| binding.configure(requestMsgCtx, this); |
| |
| // Initializing the message context above will put the outbound message onto the messageContext |
| // Determine the operation if possible from the outbound message. If it can not be determined |
| // it will be set to null. In this case, an anonymous operation will be used. Note that determining |
| // the operation will mean deserializing the message. That means that any WebServiceFeatures must have |
| // been configured first so that any relevant configurations (such as MTOM) have been initialized prior to |
| // the message being deserialized. This is particularly true for Dispatch<JAXB Element>. |
| requestMsgCtx.setOperationDescription(getOperationDescriptionForDispatch(requestMsgCtx)); |
| |
| // Send the request using the InvocationController |
| ic.invokeOneWay(invocationContext); |
| |
| //Check to see if we need to maintain session state |
| checkMaintainSessionState(requestMsgCtx, invocationContext); |
| |
| if (log.isDebugEnabled()) { |
| log.debug("One-way invocation completed: BaseDispatch.invokeOneWay()"); |
| } |
| |
| return; |
| } catch (WebServiceException e) { |
| if (log.isDebugEnabled()) { |
| log.debug("BaseDispatch.invokeOneWay(): One-way invocation failed, " + |
| "caught a WebServiceException: ", e); |
| } |
| throw e; |
| } catch (Exception e) { |
| // All exceptions are caught and rethrown as a WebServiceException |
| if (log.isDebugEnabled()) { |
| log.debug("BaseDispatch.invokeOneWay(): One-way invocation failed, " + |
| "caught an Exception, wrapping into a WebServicesException. " + |
| " Exception caught: ", e); |
| } |
| throw ExceptionFactory.makeWebServiceException(e); |
| } finally { |
| // In all other cases we rely on freeInputStream to perform the clean up. Since we don't expect |
| // a response in the invokeOneWay case, we need to perform call TransportSender#cleanup explicitly |
| try { |
| if (requestMsgCtx != null && requestMsgCtx.getAxisMessageContext() != null) { |
| org.apache.axis2.context.MessageContext axisMsgCtx = requestMsgCtx.getAxisMessageContext(); |
| if (axisMsgCtx.getTransportOut() != null && axisMsgCtx.getTransportOut().getSender() != null) { |
| axisMsgCtx.getTransportOut().getSender().cleanup(axisMsgCtx); |
| } |
| } |
| } catch (Exception ignore) { |
| } |
| } |
| } |
| |
| /** |
| * Note to developer: When making a change or fix to this method, please consider |
| * all 5 Proxy/Dispatch "invoke" methods now available in JAX-WS. For Dispatch, |
| * these are: |
| * 1) Synchronous invoke() |
| * 2) invokeOneWay() |
| * 3) invokeAsynch (Future) |
| * 4) invokeAsynch (Callback) |
| * |
| * For Proxy: |
| * 5) invokeSEIMethod() |
| * |
| */ |
| public Future<?> invokeAsync(Object obj, AsyncHandler asynchandler) throws WebServiceException { |
| |
| // All exceptions are caught and rethrown as a WebServiceException |
| try { |
| if (log.isDebugEnabled()) { |
| log.debug("Entered asynchronous (callback) invocation: BaseDispatch.invokeAsync()"); |
| } |
| |
| // Create the InvocationContext instance for this request/response flow. |
| InvocationContext invocationContext = |
| InvocationContextFactory.createInvocationContext(null); |
| invocationContext.setServiceClient(serviceClient); |
| |
| // Create the MessageContext to hold the actual request message and its |
| // associated properties |
| MessageContext requestMsgCtx = new MessageContext(); |
| requestMsgCtx.getAxisMessageContext().setProperty(BINDING_PROVIDER, this); |
| requestMsgCtx.setEndpointDescription(getEndpointDescription()); |
| invocationContext.setRequestMessageContext(requestMsgCtx); |
| |
| /* |
| * TODO: review: make sure the handlers are set on the InvocationContext |
| * This implementation of the JAXWS runtime does not use Endpoint, which |
| * would normally be the place to initialize and store the handler list. |
| * In lieu of that, we will have to intialize and store them on the |
| * InvocationContext. also see the InvocationContextFactory. On the client |
| * side, the binding is not yet set when we call into that factory, so the |
| * handler list doesn't get set on the InvocationContext object there. Thus |
| * we gotta do it here. |
| */ |
| |
| // be sure to use whatever handlerresolver is registered on the Service |
| Binding binding = (Binding) getBinding(); |
| invocationContext.setHandlers(binding.getHandlerChain()); |
| |
| initMessageContext(obj, requestMsgCtx); |
| /* |
| * if SESSION_MAINTAIN_PROPERTY is true, and the client app has explicitly set a HEADER_COOKIE on the request context, assume the client |
| * app is expecting the HEADER_COOKIE to be the session id. If we were establishing a new session, no cookie would be sent, and the |
| * server would reply with a "Set-Cookie" header, which is copied as a "Cookie"-keyed property to the service context during response. |
| * In this case, if we succeed in using an existing server session, no "Set-Cookie" header will be returned, and therefore no |
| * "Cookie"-keyed property would be set on the service context. So, let's copy our request context HEADER_COOKIE key to the service |
| * context now to prevent the "no cookie" exception in BindingProvider.setupSessionContext. It is possible the server does not support |
| * sessions, in which case no error occurs, but the client app would assume it is participating in a session. |
| */ |
| if ((requestContext.containsKey(BindingProvider.SESSION_MAINTAIN_PROPERTY)) && ((Boolean)requestContext.get(BindingProvider.SESSION_MAINTAIN_PROPERTY))) { |
| if ((requestContext.containsKey(HTTPConstants.HEADER_COOKIE)) && (requestContext.get(HTTPConstants.HEADER_COOKIE) != null)) { |
| if (invocationContext.getServiceClient().getServiceContext().getProperty(HTTPConstants.HEADER_COOKIE) == null) { |
| invocationContext.getServiceClient().getServiceContext().setProperty(HTTPConstants.HEADER_COOKIE, requestContext.get(HTTPConstants.HEADER_COOKIE)); |
| if (log.isDebugEnabled()) { |
| log.debug("Client-app defined Cookie property (assume to be session cookie) on request context copied to service context." + |
| " Caution: server may or may not support sessions, but client app will not be informed when not supported."); |
| } |
| } |
| } |
| } |
| |
| // call common init method for all invoke* paths |
| preInvokeInit(invocationContext); |
| |
| // Migrate the properties from the client request context bag to |
| // the request MessageContext. |
| ApplicationContextMigratorUtil.performMigrationToMessageContext( |
| Constants.APPLICATION_CONTEXT_MIGRATOR_LIST_ID, |
| getRequestContext(), requestMsgCtx); |
| |
| // Perform the WebServiceFeature configuration requested by the user. |
| binding.configure(requestMsgCtx, this); |
| |
| // Initializing the message context above will put the outbound message onto the messageContext |
| // Determine the operation if possible from the outbound message. If it can not be determined |
| // it will be set to null. In this case, an anonymous operation will be used. Note that determining |
| // the operation will mean deserializing the message. That means that any WebServiceFeatures must have |
| // been configured first so that any relevant configurations (such as MTOM) have been initialized prior to |
| // the message being deserialized. This is particularly true for Dispatch<JAXB Element>. |
| requestMsgCtx.setOperationDescription(getOperationDescriptionForDispatch(requestMsgCtx)); |
| |
| // Setup the Executor that will be used to drive async responses back to |
| // the client. |
| // FIXME: We shouldn't be getting this from the ServiceDelegate, rather each |
| // Dispatch object should have it's own. |
| Executor e = serviceDelegate.getExecutor(); |
| invocationContext.setExecutor(e); |
| |
| // Create the AsyncListener that is to be used by the InvocationController. |
| AsyncResponse listener = createAsyncResponseListener(); |
| invocationContext.setAsyncResponseListener(listener); |
| |
| // Send the request using the InvocationController |
| Future<?> asyncResponse = ic.invokeAsync(invocationContext, asynchandler); |
| |
| //Check to see if we need to maintain session state |
| checkMaintainSessionState(requestMsgCtx, invocationContext); |
| |
| if (log.isDebugEnabled()) { |
| log.debug("Asynchronous (callback) invocation sent: BaseDispatch.invokeAsync()"); |
| } |
| |
| return asyncResponse; |
| } catch (WebServiceException e) { |
| if (log.isDebugEnabled()) { |
| log.debug("BaseDispatch.invokeAsync() [Callback]: Asynchronous invocation failed, " + |
| "caught a WebServiceException: ", e); |
| } |
| throw e; |
| } catch (Exception e) { |
| if (log.isDebugEnabled()) { |
| log.debug("BaseDispatch.invokeAsync() [Callback]: Asynchronous invocation failed, " + |
| "caught an Exception, wrapping into a WebServiceException. Exception caught: ", e); |
| } |
| // All exceptions are caught and rethrown as a WebServiceException |
| throw ExceptionFactory.makeWebServiceException(e); |
| } |
| } |
| |
| /** |
| * Note to developer: When making a change or fix to this method, please consider |
| * all 5 Proxy/Dispatch "invoke" methods now available in JAX-WS. For Dispatch, |
| * these are: |
| * 1) Synchronous invoke() |
| * 2) invokeOneWay() |
| * 3) invokeAsynch (Future) |
| * 4) invokeAsynch (Callback) |
| * |
| * For Proxy: |
| * 5) invokeSEIMethod() |
| * |
| */ |
| public Response invokeAsync(Object obj) throws WebServiceException { |
| |
| // All exceptions are caught and rethrown as a WebServiceException |
| try { |
| if (log.isDebugEnabled()) { |
| log.debug("Entered asynchronous (polling) invocation: BaseDispatch.invokeAsync()"); |
| } |
| |
| // Create the InvocationContext instance for this request/response flow. |
| InvocationContext invocationContext = |
| InvocationContextFactory.createInvocationContext(null); |
| invocationContext.setServiceClient(serviceClient); |
| |
| // Create the MessageContext to hold the actual request message and its |
| // associated properties |
| MessageContext requestMsgCtx = new MessageContext(); |
| requestMsgCtx.getAxisMessageContext().setProperty(BINDING_PROVIDER, this); |
| requestMsgCtx.setEndpointDescription(getEndpointDescription()); |
| invocationContext.setRequestMessageContext(requestMsgCtx); |
| |
| /* |
| * TODO: review: make sure the handlers are set on the InvocationContext |
| * This implementation of the JAXWS runtime does not use Endpoint, which |
| * would normally be the place to initialize and store the handler list. |
| * In lieu of that, we will have to intialize and store them on the |
| * InvocationContext. also see the InvocationContextFactory. On the client |
| * side, the binding is not yet set when we call into that factory, so the |
| * handler list doesn't get set on the InvocationContext object there. Thus |
| * we gotta do it here. |
| */ |
| |
| // be sure to use whatever handlerresolver is registered on the Service |
| Binding binding = (Binding) getBinding(); |
| invocationContext.setHandlers(binding.getHandlerChain()); |
| |
| initMessageContext(obj, requestMsgCtx); |
| |
| /* |
| * if SESSION_MAINTAIN_PROPERTY is true, and the client app has explicitly set a HEADER_COOKIE on the request context, assume the client |
| * app is expecting the HEADER_COOKIE to be the session id. If we were establishing a new session, no cookie would be sent, and the |
| * server would reply with a "Set-Cookie" header, which is copied as a "Cookie"-keyed property to the service context during response. |
| * In this case, if we succeed in using an existing server session, no "Set-Cookie" header will be returned, and therefore no |
| * "Cookie"-keyed property would be set on the service context. So, let's copy our request context HEADER_COOKIE key to the service |
| * context now to prevent the "no cookie" exception in BindingProvider.setupSessionContext. It is possible the server does not support |
| * sessions, in which case no error occurs, but the client app would assume it is participating in a session. |
| */ |
| if ((requestContext.containsKey(BindingProvider.SESSION_MAINTAIN_PROPERTY)) && ((Boolean)requestContext.get(BindingProvider.SESSION_MAINTAIN_PROPERTY))) { |
| if ((requestContext.containsKey(HTTPConstants.HEADER_COOKIE)) && (requestContext.get(HTTPConstants.HEADER_COOKIE) != null)) { |
| if (invocationContext.getServiceClient().getServiceContext().getProperty(HTTPConstants.HEADER_COOKIE) == null) { |
| invocationContext.getServiceClient().getServiceContext().setProperty(HTTPConstants.HEADER_COOKIE, requestContext.get(HTTPConstants.HEADER_COOKIE)); |
| if (log.isDebugEnabled()) { |
| log.debug("Client-app defined Cookie property (assume to be session cookie) on request context copied to service context." + |
| " Caution: server may or may not support sessions, but client app will not be informed when not supported."); |
| } |
| } |
| } |
| } |
| |
| // call common init method for all invoke* paths |
| preInvokeInit(invocationContext); |
| |
| // Migrate the properties from the client request context bag to |
| // the request MessageContext. |
| ApplicationContextMigratorUtil.performMigrationToMessageContext( |
| Constants.APPLICATION_CONTEXT_MIGRATOR_LIST_ID, |
| getRequestContext(), requestMsgCtx); |
| |
| // Perform the WebServiceFeature configuration requested by the user. |
| binding.configure(requestMsgCtx, this); |
| |
| // Initializing the message context above will put the outbound message onto the messageContext |
| // Determine the operation if possible from the outbound message. If it can not be determined |
| // it will be set to null. In this case, an anonymous operation will be used. Note that determining |
| // the operation will mean deserializing the message. That means that any WebServiceFeatures must have |
| // been configured first so that any relevant configurations (such as MTOM) have been initialized prior to |
| // the message being deserialized. This is particularly true for Dispatch<JAXB Element>. |
| requestMsgCtx.setOperationDescription(getOperationDescriptionForDispatch(requestMsgCtx)); |
| |
| |
| // Setup the Executor that will be used to drive async responses back to |
| // the client. |
| // FIXME: We shouldn't be getting this from the ServiceDelegate, rather each |
| // Dispatch object should have it's own. |
| Executor e = serviceDelegate.getExecutor(); |
| invocationContext.setExecutor(e); |
| |
| // Create the AsyncListener that is to be used by the InvocationController. |
| AsyncResponse listener = createAsyncResponseListener(); |
| invocationContext.setAsyncResponseListener(listener); |
| |
| // Send the request using the InvocationController |
| Response asyncResponse = ic.invokeAsync(invocationContext); |
| |
| //Check to see if we need to maintain session state |
| checkMaintainSessionState(requestMsgCtx, invocationContext); |
| |
| if (log.isDebugEnabled()) { |
| log.debug("Asynchronous (polling) invocation sent: BaseDispatch.invokeAsync()"); |
| } |
| |
| return asyncResponse; |
| } catch (WebServiceException e) { |
| if (log.isDebugEnabled()) { |
| log.debug("BaseDispatch.invokeAsync() [Polling]: Asynchronous invocation failed, " + |
| "caught a WebServiceException: ", e); |
| } |
| throw e; |
| } catch (Exception e) { |
| if (log.isDebugEnabled()) { |
| log.debug("BaseDispatch.invokeAsync() [Polling]: Asynchronous invocation failed, " + |
| "caught an Exception, wrapping into a WebServiceException. Exception caught: ",e); |
| } |
| // All exceptions are caught and rethrown as a WebServiceException |
| throw ExceptionFactory.makeWebServiceException(e); |
| } |
| } |
| |
| public void setServiceClient(ServiceClient sc) { |
| serviceClient = sc; |
| } |
| |
| public Mode getMode() { |
| return mode; |
| } |
| |
| public void setMode(Mode m) { |
| mode = m; |
| } |
| |
| /** |
| * Returns the fault that is contained within the MessageContext for an invocation. If no fault |
| * exists, null will be returned. |
| * |
| * @param msgCtx |
| * @return |
| */ |
| public static WebServiceException getFaultResponse(MessageContext msgCtx) { |
| try { |
| Message msg = msgCtx.getMessage(); |
| if (msg != null && msg.isFault()) { |
| //XMLFault fault = msg.getXMLFault(); |
| // 4.3.2 conformance bullet 1 requires a ProtocolException here |
| ProtocolException pe = |
| MethodMarshallerUtils.createSystemException(msg.getXMLFault(), msg); |
| if (msgCtx.getLocalException() != null) { |
| // If a local exception occured, set it as the initial cause of the |
| // exception that will be returned |
| ExceptionFactory.setInitialCause(pe, msgCtx.getLocalException()); |
| } |
| return pe; |
| } else if (msgCtx.getLocalException() != null) { |
| // use the factory, it'll throw the right thing: |
| return ExceptionFactory.makeWebServiceException(msgCtx.getLocalException()); |
| } |
| } finally { |
| // Free the incoming input stream |
| try { |
| msgCtx.freeInputStream(); |
| } catch (IOException ioe) { |
| return ExceptionFactory.makeWebServiceException(ioe); |
| } |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Returns a boolean indicating whether or not the MessageContext contained a fault. |
| * |
| * @param msgCtx |
| * @return |
| */ |
| public boolean hasFaultResponse(MessageContext msgCtx) { |
| if(!msgCtx.getAxisMessageContext().getOptions().isExceptionToBeThrownOnSOAPFault()){ |
| if(log.isDebugEnabled()){ |
| log.debug("msgCtx.Options.isExceptionToBeThrownOnSOAPFault set to false; Exception will not be thrown on fault"); |
| } |
| return false; |
| } |
| if (msgCtx.getMessage() != null && msgCtx.getMessage().isFault()) |
| return true; |
| else if (msgCtx.getLocalException() != null) |
| return true; |
| else |
| return false; |
| } |
| |
| /* |
| * Configure any properties that will be needed on the Message |
| */ |
| private void setupMessageProperties(Message msg) { |
| // If the user has enabled MTOM on the SOAPBinding, we need |
| // to make sure that gets pushed to the Message object. |
| Binding binding = (Binding) getBinding(); |
| if (binding != null && binding instanceof SOAPBinding) { |
| SOAPBinding soapBinding = (SOAPBinding)binding; |
| if (soapBinding.isMTOMEnabled()) |
| msg.setMTOMEnabled(true); |
| } |
| } |
| |
| /* |
| * Checks to see if the parameter for the invocation is valid |
| * given the scenario that the client is operating in. There are |
| * some cases when nulls are allowed and others where it is |
| * an error. |
| */ |
| private boolean isValidInvocationParam(Object object) { |
| String bindingId = endpointDesc.getClientBindingID(); |
| |
| // If no bindingId was found, use the default. |
| if (bindingId == null) { |
| bindingId = SOAPBinding.SOAP11HTTP_BINDING; |
| } |
| |
| // If it's not an HTTP_BINDING, then we can allow for null params, |
| // but only in PAYLOAD mode per JAX-WS Section 4.3.2. |
| if (!bindingId.equals(HTTPBinding.HTTP_BINDING)) { |
| if (mode.equals(Mode.MESSAGE) && object == null) { |
| throw ExceptionFactory.makeWebServiceException(Messages.getMessage("dispatchNullParamMessageMode")); |
| } |
| } else { |
| // In all cases (PAYLOAD and MESSAGE) we must throw a WebServiceException |
| // if the parameter is null and request method is POST or PUT. |
| if (object == null && isPOSTorPUTRequest()) { |
| throw ExceptionFactory.makeWebServiceException(Messages.getMessage("dispatchNullParamHttpBinding")); |
| } |
| } |
| |
| if (object instanceof DOMSource) { |
| DOMSource ds = (DOMSource)object; |
| if (ds.getNode() == null && ds.getSystemId() == null) { |
| throw ExceptionFactory.makeWebServiceException(Messages.getMessage("dispatchBadDOMSource")); |
| } |
| } |
| |
| // If we've gotten this far, then all is good. |
| return true; |
| } |
| |
| private boolean isPOSTorPUTRequest() { |
| String method = (String)this.requestContext.get(javax.xml.ws.handler.MessageContext.HTTP_REQUEST_METHOD); |
| // if HTTP_REQUEST_METHOD is not specified, assume it is a POST method |
| return (method == null || |
| HTTPConstants.HEADER_POST.equalsIgnoreCase(method) || |
| HTTPConstants.HEADER_PUT.equalsIgnoreCase(method)); |
| } |
| |
| private Message createRequestMessage(Object obj) throws WebServiceException { |
| |
| // Check to see if the object is a valid invocation parameter. |
| // Then create the message from the object. |
| // If an exception occurs, it is local to the client and therefore is a |
| // WebServiceException (and not ProtocolExceptions). |
| // This code complies with JAX-WS 2.0 sections 4.3.2, 4.3.3 and 4.3.4. |
| if (!isValidInvocationParam(obj)) { |
| throw ExceptionFactory.makeWebServiceException(Messages.getMessage("dispatchInvalidParam")); |
| } |
| Message requestMsg = null; |
| try { |
| requestMsg = createMessageFromValue(obj); |
| } catch (Throwable t) { |
| // The webservice exception wraps the thrown exception. |
| throw ExceptionFactory.makeWebServiceException(t); |
| } |
| return requestMsg; |
| } |
| |
| private void preInvokeInit(InvocationContext requestIC) { |
| /* |
| * if SESSION_MAINTAIN_PROPERTY is true, and the client app has explicitly set a HEADER_COOKIE on the request context, assume the client |
| * app is expecting the HEADER_COOKIE to be the session id. If we were establishing a new session, no cookie would be sent, and the |
| * server would reply with a "Set-Cookie" header, which is copied as a "Cookie"-keyed property to the service context during response. |
| * In this case, if we succeed in using an existing server session, no "Set-Cookie" header will be returned, and therefore no |
| * "Cookie"-keyed property would be set on the service context. So, let's copy our request context HEADER_COOKIE key to the service |
| * context now to prevent the "no cookie" exception in BindingProvider.setupSessionContext. It is possible the server does not support |
| * sessions, in which case no error occurs, but the client app would assume it is participating in a session. |
| */ |
| if ((requestContext.containsKey(BindingProvider.SESSION_MAINTAIN_PROPERTY)) && ((Boolean)requestContext.get(BindingProvider.SESSION_MAINTAIN_PROPERTY))) { |
| if ((requestContext.containsKey(HTTPConstants.HEADER_COOKIE)) && (requestContext.get(HTTPConstants.HEADER_COOKIE) != null)) { |
| if (requestIC.getServiceClient().getServiceContext().getProperty(HTTPConstants.HEADER_COOKIE) == null) { |
| requestIC.getServiceClient().getServiceContext().setProperty(HTTPConstants.HEADER_COOKIE, requestContext.get(HTTPConstants.HEADER_COOKIE)); |
| if (log.isDebugEnabled()) { |
| log.debug("Client-app defined Cookie property (assume to be session cookie) on request context copied to service context." + |
| " Caution: server may or may not support sessions, but client app will not be informed when not supported."); |
| } |
| } |
| } |
| } |
| } |
| } |