blob: c846c4b5f66ae933572523bb0c1f838a770595ee [file] [log] [blame]
/*
* 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);
}
}