org.apache.axis2.jaxws.server.dispatcher.JavaDispatcher Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of axis2-jaxws Show documentation
Show all versions of axis2-jaxws Show documentation
Axis2 JAXWS Implementation
/*
* 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.AxisFault;
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.context.utils.ContextUtils;
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.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
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 {
if (log.isDebugEnabled()) {
log.debug(logContextClassLoader("Before invocation"));
}
output = method.invoke(serviceInstance, args);
if (log.isDebugEnabled()) {
log.debug(logContextClassLoader("After invocation"));
}
} 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);
if (log.isDebugEnabled()) {
logContextClassLoader("After invocation caught exception " + t.toString());
}
throw t;
}
return output;
}
/**
* Return a string with the current context class loader on the thread. The string is intended
* to be used in debug logging statements.
* @param logString appended to the string to be returned
* @return a string to be logged into debug logging trace.
*/
String logContextClassLoader(String appendString) {
String logMessage = null;
try {
logMessage = "Current ThreadContextClassLoader";
if (appendString != null) {
logMessage += ": " + appendString;
}
logMessage += ": " + getCurrentContextClassLoader();
} catch (Throwable t) {
// We don't want any exceptions in logging to cause trouble for the application
logMessage = "Unable to log current thread context classloader due to Throwable: " + t.toString();
}
return logMessage;
}
/**
* @return ClassLoader
*/
private static ClassLoader getCurrentContextClassLoader() {
// NOTE: This method must remain private because it uses AccessController
ClassLoader cl = null;
try {
cl = (ClassLoader) org.apache.axis2.java.security.AccessController.doPrivileged(new PrivilegedExceptionAction() {
public Object run() throws ClassNotFoundException {
return Thread.currentThread().getContextClassLoader();
}
});
} catch (PrivilegedActionException e) {
// The privileged method will throw a PriviledgedActionException which
// contains the actual exception.
if (log.isDebugEnabled()) {
log.debug("Exception thrown from AccessController: " + e);
}
Exception wrappedE = e.getException();
if (wrappedE instanceof RuntimeException) {
throw (RuntimeException) wrappedE;
} else {
throw new RuntimeException(wrappedE);
}
}
return cl;
}
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 = getCurrentContextClassLoader();
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("Completed invoke of one-way operation");
log.debug("Release resources");
}
ContextUtils.releaseWebServiceContextResources(eic.getRequestMessageContext());
if (log.isDebugEnabled()) {
log.debug("Indicate Response ready");
}
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 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 className = exception.getClass().getName();
String action = operation.getFaultAction(className);
if (action == null) {
className = className.substring((className.lastIndexOf('.'))+1);
action = operation.getFaultAction(className);
}
if (log.isDebugEnabled()) {
for(String faultActionName : operation.getFaultActionNames())
log.debug("Fault action map entry: key = " + faultActionName + ", value = " + operation.getFaultAction(faultActionName));
}
if (action != null) {
if (log.isDebugEnabled()) {
log.debug("Setting fault action " + action + " for Exception: "+className);
}
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) {
if (log.isDebugEnabled()) {
log.debug("Entered JavaDispatcher.setCheckedExceptionProperty(), t=" + 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) {
if (log.isDebugEnabled()) {
log.debug("The exception is a checked exception: " + checkedException.getCanonicalName());
}
response.setProperty(Constants.CHECKED_EXCEPTION, checkedException.getCanonicalName());
// Also set the AxisFault so that it's an "application" fault.
AxisFault fault = response.getCausedByException();
if (fault != null) {
fault.setFaultType(org.apache.axis2.Constants.APPLICATION_FAULT);
if (log.isDebugEnabled()) {
log.debug("Setting AxisFault's fault type to 'APPLICATION_FAULT': " + fault);
}
}
}
}
/**
* 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) {
if (log.isDebugEnabled()) {
log.debug("Entering JavaDispatcher.setExceptionProperties().");
}
setCheckedExceptionProperty(response, m, t);
setWebMethodExceptionProperty(response, t);
if (log.isDebugEnabled()) {
log.debug("Leaving JavaDispatcher.setExceptionProperties().");
}
}
}