| /* |
| * 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.description.AxisOperation; |
| import org.apache.axis2.jaxws.Constants; |
| import org.apache.axis2.jaxws.WebServiceExceptionLogger; |
| import org.apache.axis2.jaxws.ExceptionFactory; |
| import org.apache.axis2.jaxws.core.MessageContext; |
| 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.InvocationListener; |
| import org.apache.axis2.jaxws.server.InvocationListenerBean; |
| import org.apache.axis2.jaxws.utility.ClassUtils; |
| import org.apache.axis2.jaxws.utility.JavaUtils; |
| import org.apache.axis2.transport.TransportUtils; |
| import org.apache.commons.logging.Log; |
| import org.apache.commons.logging.LogFactory; |
| |
| import java.lang.reflect.InvocationTargetException; |
| import java.lang.reflect.Method; |
| import java.util.List; |
| import java.util.concurrent.Callable; |
| /** |
| * JavaDispatcher is an abstract class that can be extended to implement an EndpointDispatcher to a |
| * Java object. |
| */ |
| public abstract class JavaDispatcher implements EndpointDispatcher { |
| |
| private static final Log log = LogFactory.getLog(JavaDispatcher.class); |
| |
| protected Class serviceImplClass; |
| protected Object serviceInstance; |
| |
| protected JavaDispatcher(Class impl, Object serviceInstance) { |
| this.serviceImplClass = impl; |
| this.serviceInstance = serviceInstance; |
| } |
| |
| public abstract MessageContext invoke(MessageContext request) throws Exception; |
| |
| public abstract void invokeOneWay(MessageContext request); |
| |
| public abstract void invokeAsync(MessageContext request, EndpointCallback callback); |
| |
| protected abstract MessageContext createResponse(MessageContext request, Object[] input, Object output); |
| |
| protected abstract MessageContext createFaultResponse(MessageContext request, Throwable fault); |
| |
| |
| public Class getServiceImplementationClass() { |
| return serviceImplClass; |
| } |
| |
| protected Object invokeTargetOperation(Method method, Object[] args) throws Throwable { |
| Object output = null; |
| try { |
| output = method.invoke(serviceInstance, args); |
| } catch (Throwable t) { |
| |
| // Delegate logging the exception to the WebServiceExceptionLogger. |
| // Users can specifiy debug tracing of the WebServiceExceptionLogger to see |
| // all exceptions. |
| // Otherwise the WebServiceExceptionLogger only logs errors for non-checked exceptions |
| WebServiceExceptionLogger.log(method, |
| t, |
| false, |
| serviceImplClass, |
| serviceInstance, |
| args); |
| |
| throw t; |
| } |
| |
| return output; |
| } |
| |
| |
| protected class AsyncInvocationWorker implements Callable { |
| |
| private Method method; |
| private Object[] params; |
| private ClassLoader classLoader; |
| private EndpointInvocationContext eic; |
| |
| public AsyncInvocationWorker(Method m, Object[] p, ClassLoader cl, EndpointInvocationContext ctx) { |
| method = m; |
| params = p; |
| classLoader = cl; |
| eic = ctx; |
| } |
| |
| public Object call() throws Exception { |
| try { |
| if (log.isDebugEnabled()) { |
| log.debug("Invoking target endpoint via the async worker."); |
| } |
| |
| // Set the proper class loader so that we can properly marshall the |
| // outbound response. |
| ClassLoader currentLoader = Thread.currentThread().getContextClassLoader(); |
| if (classLoader != null && (classLoader != currentLoader)) { |
| Thread.currentThread().setContextClassLoader(classLoader); |
| if (log.isDebugEnabled()) { |
| log.debug("Context ClassLoader set to:" + classLoader); |
| } |
| } |
| |
| // We have the method that is going to be invoked and the parameter data to invoke it |
| // with, so just invoke the operation. |
| Object output = null; |
| boolean faultThrown = false; |
| Throwable fault = null; |
| try { |
| output = invokeTargetOperation(method, params); |
| } |
| catch (Exception e) { |
| fault = ClassUtils.getRootCause(e); |
| Throwable newFault = InvocationHelper.determineMappedException(fault, eic); |
| if(newFault != null) { |
| fault = newFault; |
| } |
| faultThrown = true; |
| } |
| |
| // If this is a one way invocation, we are done and just need to return. |
| if (eic.isOneWay()) { |
| if (log.isDebugEnabled()) { |
| log.debug("Invocation pattern was one way, work complete."); |
| } |
| |
| responseReady(eic); |
| return null; |
| } |
| |
| // Create the response MessageContext |
| MessageContext request = eic.getRequestMessageContext(); |
| MessageContext response = null; |
| if (faultThrown) { |
| // If a fault was thrown, we need to create a slightly different |
| // MessageContext, than in the response path. |
| response = createFaultResponse(request, fault); |
| setExceptionProperties(response, method, fault); |
| } else { |
| if (log.isDebugEnabled()) { |
| log.debug("Async invocation of the endpoint was successful. Creating response message."); |
| } |
| response = createResponse(request, params, output); |
| } |
| |
| EndpointInvocationContext eic = null; |
| if (request.getInvocationContext() != null) { |
| eic = (EndpointInvocationContext) request.getInvocationContext(); |
| eic.setResponseMessageContext(response); |
| } |
| |
| EndpointCallback callback = eic.getCallback(); |
| boolean handleFault = response.getMessage().isFault(); |
| if (!handleFault) { |
| if (log.isDebugEnabled()) { |
| log.debug("No fault detected in response message, sending back application response."); |
| } |
| callback.handleResponse(eic); |
| } |
| else { |
| if (log.isDebugEnabled()) { |
| log.debug("A fault was detected. Sending back a fault response."); |
| } |
| callback.handleFaultResponse(eic); |
| } |
| |
| // Set the thread's ClassLoader back to what it originally was. |
| Thread.currentThread().setContextClassLoader(currentLoader); |
| |
| // Clean up the cached attachments from the request and the response. |
| TransportUtils.deleteAttachments(eic.getRequestMessageContext().getAxisMessageContext()); |
| TransportUtils.deleteAttachments(eic.getResponseMessageContext().getAxisMessageContext()); |
| } catch (Throwable e) { |
| // Exceptions are swallowed, there is no reason to rethrow them |
| if (log.isDebugEnabled()) { |
| log.debug("AN UNEXPECTED ERROR OCCURRED IN THE ASYNC WORKER THREAD"); |
| log.debug("Exception is:" + e, e); |
| } |
| } |
| |
| return null; |
| } |
| } |
| |
| /** |
| * This will call the InvocationListener instances that were called during |
| * the request processing for this message. |
| */ |
| protected void responseReady(EndpointInvocationContext eic) { |
| List<InvocationListener> listenerList = eic.getInvocationListeners(); |
| if(listenerList != null) { |
| InvocationListenerBean bean = new InvocationListenerBean(eic, InvocationListenerBean.State.RESPONSE); |
| for(InvocationListener listener : listenerList) { |
| try { |
| listener.notify(bean); |
| } |
| catch(Exception e) { |
| throw ExceptionFactory.makeWebServiceException(e); |
| } |
| } |
| } |
| } |
| |
| protected static void setFaultResponseAction(Throwable exception, |
| MessageContext request, |
| MessageContext response) { |
| AxisOperation operation = request.getOperationDescription().getAxisOperation(); |
| if (operation != null) { |
| exception = ClassUtils.getRootCause(exception); |
| String action = operation.getFaultAction(exception.getClass().getName()); |
| |
| if (action != null) |
| response.getAxisMessageContext().setWSAAction(action); |
| } |
| } |
| |
| /** |
| * Determine if the thrown exception is a checked exception. |
| * If so, then set the name of the checked exception on the response context |
| * @param response MessageContext |
| * @param m Method |
| * @param t Throwable |
| */ |
| protected static void setCheckedExceptionProperty(MessageContext response, Method m, Throwable t) { |
| // Get the root of the exception |
| if (t instanceof InvocationTargetException) { |
| t = ((InvocationTargetException) t).getTargetException(); |
| } |
| |
| // Determine if the thrown exception is checked |
| Class checkedException = JavaUtils.getCheckedException(t, m); |
| |
| // Add the property |
| if (checkedException != null) { |
| response.setProperty(Constants.CHECKED_EXCEPTION, checkedException.getCanonicalName()); |
| } |
| } |
| |
| /** |
| * Store the actual exception on the response context |
| * @param response MessageContext |
| * @param t Throwable |
| */ |
| protected static void setWebMethodExceptionProperty(MessageContext response, |
| Throwable t) { |
| // Get the root of the exception |
| if (t instanceof InvocationTargetException) { |
| t = ((InvocationTargetException) t).getTargetException(); |
| } |
| |
| // Add the property |
| if (t != null) { |
| response.setProperty(Constants.JAXWS_WEBMETHOD_EXCEPTION, t); |
| } |
| } |
| |
| /** |
| * Information about the exception is stored on the outbound response context |
| * @param response MessageContext |
| * @param m Method |
| * @param t Throwable |
| */ |
| protected static void setExceptionProperties(MessageContext response, |
| Method m, |
| Throwable t) { |
| setCheckedExceptionProperty(response, m, t); |
| setWebMethodExceptionProperty(response, t); |
| } |
| |
| } |