org.apache.axis2.jaxws.client.proxy.JAXWSProxyHandler Maven / Gradle / Ivy
Show all versions of axis2-jaxws Show documentation
/*
* 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.proxy;
import org.apache.axis2.addressing.AddressingConstants;
import org.apache.axis2.addressing.EndpointReference;
import org.apache.axis2.addressing.AddressingConstants.Final;
import org.apache.axis2.java.security.AccessController;
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.OperationDescription;
import org.apache.axis2.jaxws.description.ServiceDescription;
import org.apache.axis2.jaxws.description.validator.EndpointDescriptionValidator;
import org.apache.axis2.jaxws.i18n.Messages;
import org.apache.axis2.jaxws.marshaller.factory.MethodMarshallerFactory;
import org.apache.axis2.jaxws.message.Message;
import org.apache.axis2.jaxws.message.databinding.JAXBUtils;
import org.apache.axis2.jaxws.registry.FactoryRegistry;
import org.apache.axis2.jaxws.runtime.description.marshal.MarshalServiceRuntimeDescription;
import org.apache.axis2.jaxws.runtime.description.marshal.MarshalServiceRuntimeDescriptionFactory;
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.jaxws.util.WSDLExtensionUtils;
import org.apache.axis2.transport.http.HTTPConstants;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import javax.xml.bind.JAXBContext;
import javax.xml.ws.AsyncHandler;
import javax.xml.ws.Holder;
import javax.xml.ws.Response;
import javax.xml.ws.WebServiceException;
import javax.xml.ws.WebServiceFeature;
import javax.xml.ws.soap.SOAPBinding;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
/**
* ProxyHandler is the java.lang.reflect.InvocationHandler implementation. When a JAX-WS client
* calls the method on a proxy object, created by calling the ServiceDelegate.getPort(...) method,
* the inovke method on the ProxyHandler is called.
*
* ProxyHandler uses EndpointInterfaceDescriptor and finds out if 1) The client call is Document
* Literal or Rpc Literal 2) The WSDL is wrapped or unWrapped.
*
* ProxyHandler then reads OperationDescription using Method name called by Client From
* OperationDescription it does the following 1) if the wsdl isWrapped() reads RequestWrapper Class
* and responseWrapperClass 2) then reads the webParams for the Operation.
*
* isWrapped() = true and DocLiteral then ProxyHandler then uses WrapperTool to create Request that
* is a Wrapped JAXBObject. Creates JAXBBlock using JAXBBlockFactory Creates MessageContext->Message
* and sets JAXBBlock to xmlPart as RequestMsgCtx in InvocationContext. Makes call to
* InvocationController. Reads ResponseMsgCtx ->MessageCtx->Message->XMLPart. Converts that to
* JAXBlock using JAXBBlockFactory and returns the BO from this JAXBBlock.
*
* isWrapped() != true and DocLiteral then ProxyHandler creates the JAXBBlock for the input request
* creates a MessageContext that is then used by IbvocationController to invoke. Response is read
* and return object is derived using @Webresult annotation. A JAXBBlock is created from the
* Response and the BO from JAXBBlock is returned.
*/
public class JAXWSProxyHandler extends BindingProvider implements
InvocationHandler {
private static Log log = LogFactory.getLog(JAXWSProxyHandler.class);
private Class seiClazz = null;
private Method method = null;
//Reference to ServiceDelegate instance that was used to create the Proxy
protected ServiceDescription serviceDesc = null;
protected InvocationController controller;
public JAXWSProxyHandler(ServiceDelegate delegate,
Class seiClazz,
EndpointDescription epDesc,
WebServiceFeature... features) {
this(delegate, seiClazz, epDesc, null, null, features);
}
public JAXWSProxyHandler(ServiceDelegate delegate,
Class seiClazz,
EndpointDescription epDesc,
EndpointReference epr,
String addressingNamespace,
WebServiceFeature... features) {
super(delegate, epDesc, epr, addressingNamespace, features);
this.seiClazz = seiClazz;
this.serviceDesc = delegate.getServiceDescription();
}
/* (non-Javadoc)
* @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
*
* Invokes the method that was called on the java.lang.reflect.Proxy instance.
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
boolean debug = log.isDebugEnabled();
if (debug) {
log.debug("Attemping to invoke Method: " + method.getName());
}
this.method = method;
if (!isValidMethodCall(method)) {
throw ExceptionFactory.makeWebServiceException(
Messages.getMessage("proxyErr1", method.getName(), seiClazz.getName()));
}
if (!isPublic(method)) {
throw ExceptionFactory.makeWebServiceException(
Messages.getMessage("proxyPrivateMethod", method.getName()));
}
if (isBindingProviderInvoked(method)) {
// Since the JAX-WS proxy instance must also implement the javax.xml.ws.BindingProvider
// interface, this object must handle those invocations as well. In that case, we'll
// delegate those calls to the BindingProvider object.
if (debug) {
log.debug(
"Invoking a public method on the javax.xml.ws.BindingProvider interface.");
}
try {
return method.invoke(this, args);
}
catch (Throwable e) {
if (debug) {
log.debug("An error occured while invoking the method: " + e.getMessage());
}
throw ExceptionFactory.makeWebServiceException(e);
}
} else {
OperationDescription operationDesc =
endpointDesc.getEndpointInterfaceDescription().getOperation(method);
if (isMethodExcluded(operationDesc)) {
throw ExceptionFactory.makeWebServiceException(
Messages.getMessage("proxyExcludedMethod", method.getName()));
}
return invokeSEIMethod(method, args);
}
}
/*
* 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()
*
* Performs the invocation of the method defined on the Service Endpoint
* Interface.
*/
private Object invokeSEIMethod(Method method, Object[] args) throws Throwable {
if (log.isDebugEnabled()) {
log.debug("Attempting to invoke SEI Method " + method.getName());
}
OperationDescription operationDesc =
endpointDesc.getEndpointInterfaceDescription().getOperation(method);
// Create and configure the request MessageContext
InvocationContext requestIC = InvocationContextFactory.createInvocationContext(null);
MessageContext request = createRequest(method, args);
request.getAxisMessageContext().setProperty(BINDING_PROVIDER, this);
request.setEndpointDescription(getEndpointDescription());
request.setOperationDescription(operationDesc);
// Enable MTOM on the Message if the property was set on the SOAPBinding.
Binding bnd = (Binding) getBinding();
if (bnd != null && bnd instanceof SOAPBinding) {
if (((SOAPBinding)bnd).isMTOMEnabled()) {
Message requestMsg = request.getMessage();
requestMsg.setMTOMEnabled(true);
int threshold = ((org.apache.axis2.jaxws.binding.SOAPBinding)bnd).getMTOMThreshold();
request.setProperty(org.apache.axis2.Constants.Configuration.MTOM_THRESHOLD,
new Integer(threshold));
}
if (((org.apache.axis2.jaxws.binding.SOAPBinding)bnd).isRespectBindingEnabled()) {
//lets invoke Utility to configure RespectBinding.
EndpointDescription endpointDescription = getEndpointDescription();
endpointDescription.setRespectBinding(true);
WSDLExtensionUtils.processExtensions(endpointDescription);
//We have build up set of extensions from wsdl
//let go ahead and validate these extensions now.
EndpointDescriptionValidator endpointValidator = new EndpointDescriptionValidator(endpointDescription);
boolean isEndpointValid = endpointValidator.validate(true);
//throw Exception if extensions are not understood by Engine.
if (!isEndpointValid) {
String msg = Messages.getMessage("endpointDescriptionValidationErrors",
endpointValidator.toString());
throw ExceptionFactory.makeWebServiceException(msg);
}
}
}
/*
* 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
requestIC.setHandlers(bnd.getHandlerChain());
requestIC.setRequestMessageContext(request);
requestIC.setServiceClient(serviceDelegate.getServiceClient(endpointDesc.getPortQName()));
/*
* 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.");
}
}
}
}
// Migrate the properties from the client request context bag to
// the request MessageContext.
ApplicationContextMigratorUtil.performMigrationToMessageContext(
Constants.APPLICATION_CONTEXT_MIGRATOR_LIST_ID,
getRequestContext(), request);
// Note that configuring the MessageContext for addressing based on the metadata and for any
// WebService Features needs to be done after the application context migration since it will move properties
// from the JAXWS RequestContext onto the Axis2 Message context, overwritting any that are already set.
configureAddressing(request, this);
// Perform the WebServiceFeature configuration requested by the user.
bnd.configure(request, this);
// We'll need an InvocationController instance to send the request.
InvocationControllerFactory icf = (InvocationControllerFactory) FactoryRegistry.getFactory(InvocationControllerFactory.class);
controller = icf.getInvocationController();
if (controller == null) {
throw new WebServiceException(Messages.getMessage("missingInvocationController"));
}
// Check if the call is OneWay, Async or Sync
if (operationDesc.isOneWay()) {
if (log.isDebugEnabled()) {
log.debug("OneWay Call");
}
controller.invokeOneWay(requestIC);
// Check to see if we need to maintain session state
checkMaintainSessionState(request, requestIC);
}
if (method.getReturnType() == Future.class) {
if (log.isDebugEnabled()) {
log.debug("Async Callback");
}
//Get AsyncHandler from Objects and sent that to InvokeAsync
AsyncHandler asyncHandler = null;
for (Object obj : args) {
if (obj != null && AsyncHandler.class.isAssignableFrom(obj.getClass())) {
asyncHandler = (AsyncHandler)obj;
break;
}
}
// Don't allow the invocation to continue if the invocation requires a callback
// object, but none was supplied.
if (asyncHandler == null) {
throw ExceptionFactory
.makeWebServiceException(Messages.getMessage("proxyNullCallback"));
}
AsyncResponse listener = createProxyListener(args, operationDesc);
requestIC.setAsyncResponseListener(listener);
if ((serviceDelegate.getExecutor() != null) &&
(serviceDelegate.getExecutor() instanceof ExecutorService)) {
ExecutorService es = (ExecutorService)serviceDelegate.getExecutor();
if (es.isShutdown()) {
// the executor service is shutdown and won't accept new tasks
// so return an error back to the client
throw ExceptionFactory
.makeWebServiceException(Messages.getMessage("ExecutorShutdown"));
}
}
requestIC.setExecutor(serviceDelegate.getExecutor());
Future> future = controller.invokeAsync(requestIC, asyncHandler);
//Check to see if we need to maintain session state
checkMaintainSessionState(request, requestIC);
if (log.isDebugEnabled()) {
log.debug("Exiting the method invokeSEIMethod() - Async Callback ");
}
return future;
}
if (method.getReturnType() == Response.class) {
if (log.isDebugEnabled()) {
log.debug("Async Polling");
}
AsyncResponse listener = createProxyListener(args, operationDesc);
requestIC.setAsyncResponseListener(listener);
requestIC.setExecutor(serviceDelegate.getExecutor());
Response response = controller.invokeAsync(requestIC);
//Check to see if we need to maintain session state
checkMaintainSessionState(request, requestIC);
if (log.isDebugEnabled()) {
log.debug("Exiting the method invokeSEIMethod() - Async Polling ");
}
return response;
}
if (!operationDesc.isOneWay()) {
InvocationContext responseIC = controller.invoke(requestIC);
//Check to see if we need to maintain session state
checkMaintainSessionState(request, requestIC);
MessageContext responseContext = responseIC.getResponseMessageContext();
// Migrate the properties from the response MessageContext back
// to the client response context bag.
ApplicationContextMigratorUtil.performMigrationFromMessageContext(
Constants.APPLICATION_CONTEXT_MIGRATOR_LIST_ID,
getResponseContext(), responseContext);
Object responseObj = createResponse(method, args, responseContext, operationDesc);
if (log.isDebugEnabled()) {
log.debug("Exiting the method invokeSEIMethod() - Sync");
}
return responseObj;
}
if (log.isDebugEnabled()) {
log.debug("Exiting the method invokeSEIMethod() - One Way ");
}
return null;
}
/**
* For a SOAP Binding, configure the Addressing-related properties on the message context based on the
* addressing configuration specified via metadata (such as a deployment descriptor). Note that if
* addressing was not explicitly configured, then the Addressing-related propertes will not be set on the
* message context.
*
* This code is similar to the client-side Addressing configurator what the properties on the message context
* are set to.
* @see org.apache.axis2.jaxws.client.config.AddressingConfigurator
* @param messageContext The message context on which Addressing properties will be set
* @param bindingProvider Instance of the binding provider for which property values will be determined
*/
private void configureAddressing(MessageContext messageContext, BindingProvider bindingProvider) {
Binding binding = (Binding) bindingProvider.getBinding();
if (binding != null && binding instanceof SOAPBinding) {
SOAPBinding soapBinding = (SOAPBinding) binding;
org.apache.axis2.jaxws.binding.SOAPBinding implBinding = (org.apache.axis2.jaxws.binding.SOAPBinding) soapBinding;
if (implBinding.isAddressingConfigured()) {
String addressingNamespace = implBinding.getAddressingNamespace();
Boolean disableAddressing = new Boolean(true);
String addressingRequired = AddressingConstants.ADDRESSING_UNSPECIFIED;
if (implBinding.isAddressingEnabled()) {
addressingNamespace = Final.WSA_NAMESPACE;
disableAddressing = new Boolean(false);
if (implBinding.isAddressingRequired()) {
addressingRequired = AddressingConstants.ADDRESSING_REQUIRED;
}
}
messageContext.setProperty(AddressingConstants.WS_ADDRESSING_VERSION, addressingNamespace);
messageContext.setProperty(AddressingConstants.DISABLE_ADDRESSING_FOR_OUT_MESSAGES, disableAddressing);
messageContext.setProperty(AddressingConstants.ADDRESSING_REQUIREMENT_PARAMETER, addressingRequired);
// Get the responses value and map to the value the addressing handler expects
messageContext.setProperty(AddressingConstants.WSAM_INVOCATION_PATTERN_PARAMETER_NAME,
org.apache.axis2.jaxws.server.config.AddressingConfigurator.mapResponseAttributeToAddressing(implBinding.getAddressingResponses()));
}
}
}
private AsyncResponse createProxyListener(Object[] args, OperationDescription operationDesc) {
ProxyAsyncListener listener = new ProxyAsyncListener(operationDesc);
listener.setHandler(this);
listener.setInputArgs(args);
return listener;
}
protected boolean isAsync() {
String methodName = method.getName();
Class returnType = method.getReturnType();
return methodName.endsWith("Async") && (returnType.isAssignableFrom(Response.class) ||
returnType.isAssignableFrom(Future.class));
}
/**
* Creates a request MessageContext for the method call. This request context will be used by
* InvocationController to route the method call to axis engine.
*
* @param method - The method invoked on the proxy object.
* @param args - The parameter list
* @return A MessageContext that can be used for the invocation
*/
protected MessageContext createRequest(Method method, Object[] args) throws Throwable {
if (log.isDebugEnabled()) {
log.debug("Creating a new Message using the request parameters.");
}
OperationDescription operationDesc =
endpointDesc.getEndpointInterfaceDescription().getOperation(method);
MessageContext request = new MessageContext();
// Select a Classloader to use for marshaling
ClassLoader cl = chooseClassLoader(seiClazz, serviceDesc);
// Make sure the same classloader is used on the response
request.setProperty(Constants.CACHE_CLASSLOADER, cl);
Message message = MethodMarshallerFactory.getMarshaller(operationDesc, true, null)
.marshalRequest(args, operationDesc, this.getRequestContext());
if (log.isDebugEnabled()) {
log.debug("Request Message created successfully.");
}
request.setMessage(message);
if (log.isDebugEnabled()) {
log.debug("Request MessageContext created successfully.");
}
return request;
}
/**
* Creates a response MessageContext for the method call. This response context will be used to
* create response result to the client call.
*
* @param method - The method invoked on the proxy object.
* @param args - The parameter list.
* @param responseContext - The MessageContext to be used for the response.
* @param operationDesc - The OperationDescription that for the invoked method.
* @return
*/
protected Object createResponse(Method method, Object[] args, MessageContext responseContext,
OperationDescription operationDesc) throws Throwable {
Message responseMsg = responseContext.getMessage();
try {
if (log.isDebugEnabled()) {
log.debug("Processing the response Message to create the return value(s).");
}
// Find out if there was a fault on the response and create the appropriate
// exception type.
if (hasFaultResponse(responseContext)) {
Throwable t = getFaultResponse(responseContext, operationDesc);
throw t;
}
// Get the classloader that was used for the request processing
ClassLoader cl = (ClassLoader) responseContext.getProperty(Constants.CACHE_CLASSLOADER);
if (cl == null) {
InvocationContext ic = responseContext.getInvocationContext();
if (ic != null) {
MessageContext requestMC = ic.getRequestMessageContext();
if (requestMC != null) {
cl = (ClassLoader) responseContext.getProperty(Constants.CACHE_CLASSLOADER);
if (cl != null) {
if (log.isDebugEnabled()) {
log.debug("Obtained ClassLoader for the request context: " + cl);
}
}
}
}
} else {
if (log.isDebugEnabled()) {
log.debug("Obtained ClassLoader for the response context: " + cl);
}
}
Object object =
MethodMarshallerFactory.getMarshaller(operationDesc, true, cl)
.demarshalResponse(responseMsg, args, operationDesc);
if (log.isDebugEnabled()) {
log.debug("The response was processed and the return value created successfully.");
}
return object;
} finally {
responseMsg.close();
// Free incoming stream
try {
responseContext.freeInputStream();
}
catch (Throwable t) {
throw ExceptionFactory.makeWebServiceException(t);
}
}
}
protected static Throwable getFaultResponse(MessageContext msgCtx,
OperationDescription opDesc) {
Message msg = msgCtx.getMessage();
//Operation Description for Async method does not store the fault description as Asyc operation
//will never have throws clause in the method signature.
//we will fetch the OperationDescription of the sync method and this should give us the
//correct fault description so we can throw the right user defined exception.
try {
if (opDesc.isJAXWSAsyncClientMethod()) {
opDesc = opDesc.getSyncOperation();
}
if (msg != null && msg.isFault()) {
ClassLoader cl = (ClassLoader) msgCtx.getProperty(Constants.CACHE_CLASSLOADER);
Object object = MethodMarshallerFactory.getMarshaller(opDesc, true, cl)
.demarshalFaultResponse(msg, opDesc);
if (log.isDebugEnabled() && object != null) {
log.debug("A fault was found and processed.");
log.debug("Throwing a fault of type: " + object.getClass().getName() +
" back to the clent.");
}
if (msgCtx.getLocalException() != null) {
// If a local exception occured, set it as the initial cause of the
// exception that will be returned
ExceptionFactory.setInitialCause((Throwable) object, msgCtx.getLocalException());
}
return (Throwable)object;
} else if (msgCtx.getLocalException() != null) {
// use the factory, it'll throw the right thing:
return ExceptionFactory.makeWebServiceException(msgCtx.getLocalException());
}
} finally {
try {
msgCtx.freeInputStream();
}
catch (Throwable t) {
throw ExceptionFactory.makeWebServiceException(t);
}
}
return null;
}
protected static boolean hasFaultResponse(MessageContext mc) {
if (mc.getMessage() != null && mc.getMessage().isFault())
return true;
else if (mc.getLocalException() != null)
return true;
else
return false;
}
private boolean isBindingProviderInvoked(Method method) {
Class methodsClass = method.getDeclaringClass();
return (seiClazz == methodsClass) ? false : true;
}
private boolean isValidMethodCall(Method method) {
Class clazz = method.getDeclaringClass();
if (clazz.isAssignableFrom(seiClazz) ||
clazz.isAssignableFrom(org.apache.axis2.jaxws.spi.BindingProvider.class) ||
clazz.isAssignableFrom(javax.xml.ws.BindingProvider.class)) {
return true;
}
return false;
}
private boolean isPublic(Method method) {
return Modifier.isPublic(method.getModifiers());
}
private boolean isMethodExcluded(OperationDescription operationDesc) {
return operationDesc.isExcluded();
}
public Class getSeiClazz() {
return seiClazz;
}
public void setSeiClazz(Class seiClazz) {
this.seiClazz = seiClazz;
}
/**
* Choose a classloader most likely to marshal the message
* successfully
* @param cls
* @return ClassLoader
*/
private static ClassLoader chooseClassLoader(Class cls, ServiceDescription serviceDesc) {
if (log.isDebugEnabled()) {
log.debug("Choose Classloader for " + cls);
}
ClassLoader cl = null;
ClassLoader contextCL = getContextClassLoader();
ClassLoader classCL = getClassLoader(cls);
if (log.isDebugEnabled()) {
log.debug("Context ClassLoader is " + contextCL);
log.debug("Class ClassLoader is " + classCL);
}
if (classCL == null ||
contextCL == classCL) {
// Normal case: Use the context ClassLoader
cl = contextCL;
} else {
// Choose the better of the JAXBContexts
MarshalServiceRuntimeDescription marshalDesc =
MarshalServiceRuntimeDescriptionFactory.get(serviceDesc);
// Get the JAXBContext for the context classloader
Holder holder_contextCL = new Holder();
JAXBContext jbc_contextCL = null;
try {
jbc_contextCL = JAXBUtils.getJAXBContext(marshalDesc.getPackages(),
holder_contextCL,
marshalDesc.getPackagesKey(),
contextCL,
null);
} catch (Throwable t) {
if (log.isDebugEnabled()) {
log.debug("Error occured..Processing continues " + t);
}
}
// Get the JAXBContext using the class's ClassLoader
Holder holder_classCL = new Holder();
JAXBContext jbc_classCL = null;
try {
jbc_classCL = JAXBUtils.getJAXBContext(marshalDesc.getPackages(),
holder_classCL,
marshalDesc.getPackagesKey(),
classCL,
null);
} catch (Throwable t) {
if (log.isDebugEnabled()) {
log.debug("Error occured..Processing continues " + t);
}
}
// Heuristic to choose the better classloader to marshal the
// data. Slight priority given to the classloader that loaded
// the sei class.
if (jbc_classCL == null) {
// A JAXBContext could not be loaded for the class's classlaoder,
// choose the context ClassLoader
if (log.isDebugEnabled()) {
log.debug("Could not load JAXBContext for Class ClassLoader");
}
cl = contextCL;
} else if (jbc_contextCL == null) {
// A JAXBContext could not be loaded for the context's classloader,
// choose the class ClassLoader
if (log.isDebugEnabled()) {
log.debug("Could not load JAXBContext for Context ClassLoader");
}
cl = classCL;
} else if (holder_contextCL.value == JAXBUtils.CONSTRUCTION_TYPE.BY_CONTEXT_PATH &&
holder_classCL.value == JAXBUtils.CONSTRUCTION_TYPE.BY_CONTEXT_PATH) {
// Both were successfully built with the context path.
// Choose the one associated with the proxy class
if (log.isDebugEnabled()) {
log.debug("Loaded both JAXBContexts with BY_CONTEXT_PATH. Choose Class ClassLoader");
}
cl = classCL;
} else if (holder_contextCL.value == JAXBUtils.CONSTRUCTION_TYPE.BY_CONTEXT_PATH) {
// Successfully found all classes with classloader, use this one
if (log.isDebugEnabled()) {
log.debug("Successfully loaded JAXBContext with Contxst ClassLoader. Choose Context ClassLoader");
}
cl = contextCL;
} else if (holder_classCL.value == JAXBUtils.CONSTRUCTION_TYPE.BY_CONTEXT_PATH) {
// Successfully found all classes with classloader, use this one
if (log.isDebugEnabled()) {
log.debug("Successfully loaded JAXBContext with Class ClassLoader. Choose Class ClassLoader");
}
cl = classCL;
} else {
if (log.isDebugEnabled()) {
log.debug("Default to Class ClassLoader");
}
cl = classCL;
}
}
if (log.isDebugEnabled()) {
log.debug("Chosen ClassLoader is " + cls);
}
return cl;
}
private static ClassLoader getContextClassLoader() {
// NOTE: This method must remain private because it uses AccessController
ClassLoader cl = null;
try {
cl = (ClassLoader)AccessController.doPrivileged(
new PrivilegedExceptionAction() {
public Object run() throws ClassNotFoundException {
return Thread.currentThread().getContextClassLoader();
}
}
);
} catch (PrivilegedActionException e) {
if (log.isDebugEnabled()) {
log.debug("Exception thrown from AccessController: " + e);
}
throw ExceptionFactory.makeWebServiceException(e.getException());
}
return cl;
}
/**
* @param cls
* @return ClassLoader or null if cannot be obtained
*/
private static ClassLoader getClassLoader(final Class cls) {
// NOTE: This method must remain private because it uses AccessController
if (cls == null) {
return null;
}
ClassLoader cl = null;
try {
cl = (ClassLoader)AccessController.doPrivileged(
new PrivilegedExceptionAction() {
public Object run() throws ClassNotFoundException {
return cls.getClassLoader();
}
}
);
} catch (PrivilegedActionException e) {
if (log.isDebugEnabled()) {
log.debug("Exception thrown from AccessController: " + e);
}
}
return cl;
}
}