org.apache.axis2.jaxws.runtime.description.marshal.impl.ArtifactProcessor.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.axis2.jaxws.runtime.description.marshal.impl.ArtifactProcessor.java

Source

/*
 * 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.runtime.description.marshal.impl;

import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.security.PrivilegedAction;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.HashMap;
import java.util.Map;

import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

import org.apache.axis2.java.security.AccessController;
import org.apache.axis2.jaxws.Constants;
import org.apache.axis2.jaxws.ExceptionFactory;
import org.apache.axis2.jaxws.description.EndpointDescription;
import org.apache.axis2.jaxws.description.FaultDescription;
import org.apache.axis2.jaxws.description.OperationDescription;
import org.apache.axis2.jaxws.description.ServiceDescription;
import org.apache.axis2.jaxws.message.databinding.ClassFinder;
import org.apache.axis2.jaxws.message.factory.ClassFinderFactory;
import org.apache.axis2.jaxws.registry.FactoryRegistry;
import org.apache.axis2.jaxws.runtime.description.marshal.AnnotationDesc;
import org.apache.axis2.jaxws.runtime.description.marshal.FaultBeanDesc;
import org.apache.axis2.jaxws.utility.ClassUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * Examines a ServiceDesc and locates and/or builds the JAX-WS artifacts. The JAX-WS artifacts are:
 * - request wrapper classes - response wrapper classes - fault beans for non-JAX-WS compliant
 * exceptions
 */
class ArtifactProcessor {

    private static final Log log = LogFactory.getLog(ArtifactProcessor.class);

    private ServiceDescription serviceDesc;
    private Map<OperationDescription, String> requestWrapperMap = new HashMap<OperationDescription, String>();
    private Map<OperationDescription, String> responseWrapperMap = new HashMap<OperationDescription, String>();
    private Map<OperationDescription, Method> methodMap = new HashMap<OperationDescription, Method>();
    private Map<FaultDescription, FaultBeanDesc> faultBeanDescMap = new HashMap<FaultDescription, FaultBeanDesc>();

    static final String JAXWS_SUBPACKAGE = "jaxws";

    /**
     * Artifact Processor
     *
     * @param serviceDesc
     */
    ArtifactProcessor(ServiceDescription serviceDesc) {
        this.serviceDesc = serviceDesc;
    }

    Map<OperationDescription, String> getRequestWrapperMap() {
        return requestWrapperMap;
    }

    Map<OperationDescription, String> getResponseWrapperMap() {
        return responseWrapperMap;
    }

    Map<FaultDescription, FaultBeanDesc> getFaultBeanDescMap() {
        return faultBeanDescMap;
    }

    Map<OperationDescription, Method> getMethodMap() {
        return methodMap;
    }

    void build() {
        for (EndpointDescription ed : serviceDesc.getEndpointDescriptions()) {
            if (ed.getEndpointInterfaceDescription() != null) {
                for (OperationDescription opDesc : ed.getEndpointInterfaceDescription().getOperations()) {

                    String declaringClassName = opDesc.getJavaDeclaringClassName();
                    String packageName = getPackageName(declaringClassName);
                    String simpleName = getSimpleClassName(declaringClassName);
                    String methodName = opDesc.getJavaMethodName();

                    // There is no default for @RequestWrapper/@ResponseWrapper classname  None is listed in Sec. 7.3 on p. 80 of
                    // the JAX-WS spec, BUT Conformance(Using javax.xml.ws.RequestWrapper) in Sec 2.3.1.2 on p. 13
                    // says the entire annotation "...MAY be omitted if all its properties would have default values."
                    // We will assume that this statement gives us the liberty to find a wrapper class/build a wrapper class or 
                    // implement an engine w/o the wrapper class.

                    // @RequestWrapper className processing
                    String requestWrapperName = opDesc.getRequestWrapperClassName();
                    String foundRequestWrapperName = getWrapperClass("@RequestWrapper", requestWrapperName,
                            packageName, javaMethodToClassName(methodName), ed.getAxisService().getClassLoader(),
                            serviceDesc);

                    if (foundRequestWrapperName != null) {
                        requestWrapperMap.put(opDesc, foundRequestWrapperName);
                    }

                    // @ResponseWrapper className processing
                    String responseWrapperName = opDesc.getResponseWrapperClassName();
                    String foundResponseWrapperName = getWrapperClass("@ResponseWrapper", responseWrapperName,
                            packageName, javaMethodToClassName(methodName) + "Response",
                            ed.getAxisService().getClassLoader(), serviceDesc);

                    if (foundResponseWrapperName != null) {
                        responseWrapperMap.put(opDesc, foundResponseWrapperName);
                    }

                    for (FaultDescription faultDesc : opDesc.getFaultDescriptions()) {
                        FaultBeanDesc faultBeanDesc = create(ed, faultDesc, opDesc);
                        faultBeanDescMap.put(faultDesc, faultBeanDesc);
                    }

                    // Get the Method
                    Class cls = null;
                    try {
                        cls = loadClass(declaringClassName, getContextClassLoader());
                    } catch (Exception e) {
                        if (log.isDebugEnabled()) {
                            log.debug("Class " + declaringClassName + " was not found by the Context ClassLoader.  "
                                    + "Will use the ClassLoader associated with the service.  The exception is: "
                                    + e);
                        }
                    }

                    if (cls == null) {
                        try {
                            cls = loadClass(declaringClassName, ed.getAxisService().getClassLoader());
                        } catch (Exception e) {
                            if (log.isDebugEnabled()) {
                                log.debug("Class " + declaringClassName
                                        + " was not found by the AxisService ClassLoader.  "
                                        + "Processing continues.  The exception is:" + e);
                            }

                        }
                    }
                    if (cls != null) {
                        Method method = getMethod(opDesc.getJavaMethodName(), cls);
                        if (method != null) {
                            methodMap.put(opDesc, method);
                        }
                    }

                }
            }
        }
    }

    /**
     * @param type "@RequestWrapper", "@ResponseWrapper", and "@WebFault"
     * @param providedValue String name of the Wrapper or Fault Bean from annotations
     * @param defaultPkg String name of the package to use for defaulting
     * @param defaultClassName name of the class to use if defaulting
     * @param altClassLoader name of the alternative classloader
     * @return
     */
    static private String getWrapperClass(String type, String providedValue, String defaultPkg,
            String defaultClassName, ClassLoader altClassLoader, ServiceDescription serviceDesc) {

        if (log.isDebugEnabled()) {
            log.debug("getWrapperClass for " + type + " with value (" + providedValue + ")");
        }

        String wrapperClass = null;
        try {

            ClassLoader cl = getContextClassLoader();
            if (providedValue != null && providedValue.length() > 0) {
                Class cls = null;
                // If a className is provided try to load it with the context classloader
                // and then the alternate classloader.
                // If the class still cannot be loaded, then try inserting the
                // jaxws sub-package.

                if (log.isDebugEnabled()) {
                    log.debug("Try finding the class with the name provided = " + providedValue);
                }
                cls = loadClassOrNull(providedValue, cl);
                if (cls != null) {
                    wrapperClass = providedValue;
                } else {
                    cls = loadClassOrNull(providedValue, altClassLoader);
                    if (cls != null) {
                        wrapperClass = providedValue;
                    }
                }
                // Legacy
                if (cls == null) {
                    String origPackage = getPackageName(providedValue);
                    if (origPackage.length() > 0) {
                        String newPackage = origPackage + "." + JAXWS_SUBPACKAGE;
                        String clsName = getSimpleClassName(providedValue);
                        String newValue = newPackage + "." + clsName;
                        if (log.isDebugEnabled()) {
                            log.debug("Did not find the name provided.  Now trying " + newValue);
                        }
                        cls = loadClassOrNull(newValue, cl);
                        if (cls != null) {
                            wrapperClass = newValue;
                        } else {
                            cls = loadClassOrNull(newValue, altClassLoader);
                            if (cls != null) {
                                wrapperClass = newValue;
                            }
                        }

                        if (cls == null && (type.equals("@RequestWrapper") || type.equals("@ResponseWrapper")
                                || type.equals("@WebFault") || type.equals("faultInfo"))) {

                            //Support for Fault Bean Generation
                            //As per JAX-WS 2.2 Specification section 3.7 an application programmer can choose not to
                            //package the faultBeans, if we have reached this point in the code then user has choosen
                            //not to package the fault bean. If there is a cache of generated artifacts available then 
                            //lets look for the missing faultBean there.

                            //Support for Wrapper Bean Generation
                            //As per JAX-WS 2.2 Specificaiton section 3.6.2.1 pg 41 an application programmer does not use
                            //the wrapper bean classes, so the application need not package these classes. If we have reached
                            //this point in the code then user has choosen not to package these beans.

                            //NOTE:If we find Generated artifacts from cache this guarantees that we will not use
                            //DocLitWrappedMinimum marshaller code. The advantage of normal DocLitWrappedMarshaller is
                            //that it is very robust and has support of lot more datatypes than in DocLitWrappedMinimum.
                            if (log.isDebugEnabled()) {
                                log.debug("Adding cache to classpath");
                            }
                            ClassFinderFactory cff = (ClassFinderFactory) FactoryRegistry
                                    .getFactory(ClassFinderFactory.class);
                            ClassFinder cf = cff.getClassFinder();
                            String cachePath = (String) serviceDesc.getAxisConfigContext()
                                    .getProperty(Constants.WS_CACHE);
                            if (cachePath != null) {
                                //lets add the cache to classpath and retry loading missing artifacts.
                                if (log.isDebugEnabled()) {
                                    log.debug("updating classpath with cache location");
                                }
                                cf.updateClassPath(cachePath, cl);
                                if (log.isDebugEnabled()) {
                                    log.debug("trying to load class " + newValue + " from cache.");
                                }
                                cls = loadClassOrNull(newValue, cl);
                                if (cls != null) {
                                    wrapperClass = newValue;
                                }
                            }
                        }
                    }
                }
            } else {
                // If no value is provided by the annotation, then the we try default values.
                // The wsgen tool generates classes in the jaxws subpackage.
                // The wsimport tool generates classes in the same package as the SEI.
                // Note that from reading the JAX-WS spec, it seems that WSGen is doing that
                // correctly; See the conformance requirement in JAX-WS 2.0 Spec Section 3.6.2.1 Document
                // Wrapped on page 36: Conformance (Default wrapper bean package): In the absence of
                // customizations, the wrapper beans package MUST be a generated jaxws subpackage of the SEI
                // package.
                // However, if the class is in both places the runtime should prefer the one
                // in the non-jaxws package.  Why ?
                // The other classes in the non-jaxws package will cause the non-jaxws 
                // wrapper to get pulled in first....thus the jaxws wrapper will cause a collision.
                // 
                // Thus the following algorithm with check the non-jaxws package first

                Class cls1 = null; // Class from the non-JAXWS package
                Class cls2 = null; // Class from the JAX-WS package
                boolean cls1IsJAXB = false;
                boolean cls2IsJAXB = false;

                // Look for the class in the non-jaxws package first
                String defaultValue = null;
                if (defaultPkg.length() > 0) {
                    defaultValue = defaultPkg + "." + defaultClassName;
                } else {
                    defaultValue = defaultClassName;
                }
                if (log.isDebugEnabled()) {
                    log.debug("No provided value.  Try the default class name =  " + defaultValue);
                }
                cls1 = loadClassOrNull(defaultValue, cl);

                if (cls1 == null) {
                    cls1 = loadClassOrNull(defaultValue, altClassLoader);
                }
                if (cls1 != null) {
                    cls1IsJAXB = isJAXB(cls1);
                }

                // Now try the one in the jaxws subpackage (if cls1 is missing or perhaps not a JAXB class)
                if (cls1 == null || !cls1IsJAXB) {
                    if (defaultPkg.length() > 0) {
                        defaultValue = defaultPkg + "." + JAXWS_SUBPACKAGE + "." + defaultClassName;
                        if (log.isDebugEnabled()) {
                            log.debug("Did not find the default name.  Try a different default class name =  "
                                    + defaultValue);
                        }
                        cls2 = loadClassOrNull(defaultValue, cl);
                        if (cls2 == null) {
                            cls2 = loadClassOrNull(defaultValue, altClassLoader);
                        }
                        if (cls2 == null && (type.equals("@RequestWrapper") || type.equals("@ResponseWrapper")
                                || type.equals("@WebFault") || type.equals("faultInfo"))) {

                            //Support for Fault Bean Generation
                            //As per JAX-WS 2.2 Specification section 3.7 an application programmer can choose not to
                            //package the faultBeans, if we have reached this point in the code then user has choosen
                            //not to package the fault bean. If there is a cache of generated artifacts available then 
                            //lets look for the missing faultBean there.

                            //Support for Wrapper Bean Generation
                            //As per JAX-WS 2.2 Specificaiton section 3.6.2.1 pg 41 an application programmer does not use
                            //the wrapper bean classes, so the application need not package these classes. If we have reached
                            //this point in the code then user has choosen not to package these beans.

                            //NOTE:If we find Generated artifacts from cache this guarantees that we will not use
                            //DocLitWrappedMinimum marshaller code. The advantage of normal DocLitWrappedMarshaller is
                            //that it is very robust and has support of lot more datatypes than in DocLitWrappedMinimum.
                            if (log.isDebugEnabled()) {
                                log.debug("Adding cache to classpath");
                            }
                            if (log.isDebugEnabled()) {
                                log.debug("Adding cache to classpath");
                            }
                            ClassFinderFactory cff = (ClassFinderFactory) FactoryRegistry
                                    .getFactory(ClassFinderFactory.class);
                            ClassFinder cf = cff.getClassFinder();
                            String cachePath = (String) serviceDesc.getAxisConfigContext()
                                    .getProperty(Constants.WS_CACHE);
                            if (log.isDebugEnabled()) {
                                log.debug("cachePath = " + cachePath);
                            }
                            if (cachePath != null) {
                                //lets add the cache to classpath and retry loading missing artifacts.
                                if (log.isDebugEnabled()) {
                                    log.debug("updating classpath with cache location");
                                }
                                cf.updateClassPath(cachePath, cl);
                                if (log.isDebugEnabled()) {
                                    log.debug("trying to load class " + defaultValue + " from cache.");
                                }
                                cls2 = loadClassOrNull(defaultValue, cl);
                            }
                        }
                    }
                }

                if (cls2 != null) {
                    cls2IsJAXB = isJAXB(cls2);
                }

                // Choose the wrapper class
                if (cls1 == null && cls2 == null) {
                    if (log.isDebugEnabled()) {
                        log.debug("Could not find a wrapper class");
                    }
                    wrapperClass = null;
                } else if (cls1 == null) {
                    wrapperClass = cls2.getCanonicalName();
                    if (log.isDebugEnabled()) {
                        log.debug("Choosing " + wrapperClass);
                    }
                } else if (cls2 == null) {
                    wrapperClass = cls1.getCanonicalName();
                    if (log.isDebugEnabled()) {
                        log.debug("Choosing " + wrapperClass);
                    }
                } else {
                    if (log.isDebugEnabled()) {
                        log.debug("There are two classes that are present " + cls1 + " and " + cls2);
                    }
                    // Choose the one that is JAXB enabled
                    if (!cls1IsJAXB && !cls2IsJAXB) {
                        // If neither is JAXB enabled.  Choose the one in the jaxws package.
                        // This is the one most likely provided by tooling.
                        if (log.isDebugEnabled()) {
                            log.debug("Neither are JAXB enabled. Choosing " + cls2);
                        }
                        wrapperClass = cls2.getCanonicalName();
                    } else if (cls1IsJAXB && cls2IsJAXB) {
                        // If both are JAXB enabled, choose the one in the non-JAXWS package.
                        // This generally means that multiple tools generated the packages.
                        // Choosing the one in the non-JAXWS package will avoid a JAXBContext collision.
                        if (log.isDebugEnabled()) {
                            log.debug("Both are JAXB enabled. Choosing " + cls1);
                        }
                        wrapperClass = cls1.getCanonicalName();
                    } else if (cls1IsJAXB) {
                        if (log.isDebugEnabled()) {
                            log.debug("Choosing " + cls1 + " because it is JAXB enabled");
                        }
                        wrapperClass = cls1.getCanonicalName();
                    } else {
                        if (log.isDebugEnabled()) {
                            log.debug("Choosing " + cls2 + " because it is JAXB enabled");
                        }
                        wrapperClass = cls2.getCanonicalName();
                    }
                }

            }
        } catch (Throwable t) {
            if (log.isDebugEnabled()) {
                log.debug("Unexpected error.  Processing continues. ", t);
            }
        }
        if (log.isDebugEnabled()) {
            log.debug("exit getWrapperClass with " + wrapperClass);
        }
        return wrapperClass;

    }

    private FaultBeanDesc create(EndpointDescription ed, FaultDescription faultDesc, OperationDescription opDesc) {
        /* FaultBeanClass algorithm
         *   1) The class defined on @WebFault of the exception
         *   2) If not present or invalid, the class defined by getFaultInfo.
         *   3) If not present, the class is found using the default name and location
         */
        String declaringClassName = opDesc.getJavaDeclaringClassName();

        String type = "@WebFault";
        String faultBeanClassName = faultDesc.getFaultBean();
        if (faultBeanClassName == null || faultBeanClassName.length() == 0) {
            type = "faultInfo";
            faultBeanClassName = faultDesc.getFaultInfo();
        }
        String foundClassName = getWrapperClass(type, faultBeanClassName, getPackageName(declaringClassName),
                getSimpleClassName(faultDesc.getExceptionClassName()) + "Bean",
                ed.getAxisService().getClassLoader(), serviceDesc);
        if (foundClassName == null) {
            faultBeanClassName = missingArtifact(faultBeanClassName);
        }
        if (foundClassName != null) {
            faultBeanClassName = foundClassName;
        }

        /* Local NameAlgorithm:
         *   1) The name defined on the @WebFault of the exception.
         *   2) If not present, the name defined via the @XmlRootElement of the fault bean class.
         *   3) If not present, the <exceptionName>Bean
         */
        String faultBeanLocalName = faultDesc.getName();
        if (faultBeanLocalName == null || faultBeanLocalName.length() == 0) {
            if (faultBeanClassName != null && faultBeanClassName.length() > 0) {
                try {
                    Class faultBean;
                    try {
                        faultBean = loadClass(faultBeanClassName, getContextClassLoader());
                    } catch (ClassNotFoundException e) {
                        faultBean = loadClass(faultBeanClassName, ed.getAxisService().getClassLoader());
                    }
                    AnnotationDesc aDesc = AnnotationDescImpl.create(faultBean);
                    if (aDesc.hasXmlRootElement()) {
                        faultBeanLocalName = aDesc.getXmlRootElementName();
                    }
                } catch (Throwable t) {
                    throw ExceptionFactory.makeWebServiceException(t);
                }
            }
        }
        if (faultBeanLocalName == null || faultBeanLocalName.length() == 0) {
            faultBeanLocalName = getSimpleClassName(faultDesc.getExceptionClassName()) + "Bean";
        }

        /* Algorithm for fault bean namespace
         *   1) The namespace defined on the @WebFault of the exception.
         *   2) If not present, the namespace defined via the @XmlRootElement of the class name.
         *   3) If not present, the namespace of the method's declared class + "/jaxws"
         */
        String faultBeanNamespace = faultDesc.getTargetNamespace();
        if (faultBeanNamespace == null || faultBeanNamespace.length() == 0) {
            if (faultBeanClassName != null && faultBeanClassName.length() > 0) {
                try {
                    Class faultBean;
                    try {
                        faultBean = loadClass(faultBeanClassName, getContextClassLoader());
                    } catch (ClassNotFoundException e) {
                        faultBean = loadClass(faultBeanClassName, ed.getAxisService().getClassLoader());
                    }
                    AnnotationDesc aDesc = AnnotationDescImpl.create(faultBean);
                    if (aDesc.hasXmlRootElement()) {
                        faultBeanNamespace = aDesc.getXmlRootElementNamespace();
                    }
                } catch (Throwable t) {
                    throw ExceptionFactory.makeWebServiceException(t);
                }
            }
        }
        if (faultBeanNamespace == null || faultBeanNamespace.length() == 0) {
            faultBeanNamespace = opDesc.getEndpointInterfaceDescription().getTargetNamespace();
        }

        return new FaultBeanDescImpl(faultBeanClassName, faultBeanLocalName, faultBeanNamespace);
    }

    /**
     * @param className
     * @return package name
     */
    private static String getPackageName(String className) {
        int index = className.lastIndexOf(".");
        if (index <= 0) {
            return "";
        } else {
            return className.substring(0, index);
        }
    }

    /**
     * @param className
     * @return simple class name
     */
    private static String getSimpleClassName(String className) {
        int index = className.lastIndexOf(".");
        if (index <= 0) {
            return className;
        } else {
            return className.substring(index + 1);
        }
    }

    /**
     * @param methodName
     * @return method name converted into a class name
     */
    private static String javaMethodToClassName(String methodName) {
        String className = null;
        if (methodName != null) {
            StringBuffer buildClassName = new StringBuffer(methodName);
            buildClassName.replace(0, 1, methodName.substring(0, 1).toUpperCase());
            className = buildClassName.toString();
        }
        return className;
    }

    /**
     * This method is invoked if the artifact is missing
     *
     * @param artifactName
     * @return newly constructed name or null
     */
    private String missingArtifact(String artifactName) {

        if (log.isDebugEnabled()) {
            log.debug("The following class was not found: " + artifactName
                    + " Processing continues without this class.");
        }
        return null;
    }

    /**
     * @param className
     * @param classLoader
     * @return Class or Null
     */
    private static Class loadClassOrNull(String className, ClassLoader classLoader) {
        try {
            return loadClass(className, classLoader);
        } catch (Throwable t) {
            return null;
        }
    }

    private static Class loadClass(String className, ClassLoader classLoader) throws ClassNotFoundException {
        // Don't make this public, its a security exposure
        return forName(className, true, classLoader);
    }

    /**
     * Return the class for this name
     *
     * @return Class
     */
    private static Class forName(final String className, final boolean initialize, final ClassLoader classloader)
            throws ClassNotFoundException {
        // NOTE: This method must remain protected because it uses AccessController
        Class cl = null;
        try {
            cl = (Class) AccessController.doPrivileged(new PrivilegedExceptionAction() {
                public Object run() throws ClassNotFoundException {
                    // Class.forName does not support primitives
                    Class cls = ClassUtils.getPrimitiveClass(className);
                    try {
                        if (cls == null) {
                            cls = Class.forName(className, initialize, classloader);
                        }
                        return cls;
                        //Lets catch NoClassDefFoundError as its part of Throwable
                        //Any Exception that extends Exception will be handled by doPriv method.    
                    } catch (NoClassDefFoundError e) {
                        /**
                         * In different jaxws scenarios, some classes may be missing.  So it is normal behavior
                         * to get to this point.  The exception is swallowed and a null is returned.  
                         * The exception is not logged...as this would give servicability folks the idea that a problem occurred.
                         */
                    }
                    return cls;
                }
            });
        } catch (PrivilegedActionException e) {
            /**
             * In different jaxws scenarios, some classes may be missing.  So it is normal behavior
             * to get to this point. 
             * The exception is not logged...as this would give servicability folks the idea that a problem occurred.
             */
            throw (ClassNotFoundException) e.getException();
        }

        return cl;
    }

    /**
     * Return the Method matching the method name or null
     * @param methodName String containing method name
     * @param cls Class of the class that declares the method
     *
     * @return Method or null
     */
    private static Method getMethod(final String methodName, final Class cls) {
        // NOTE: This method must remain protected because it uses AccessController
        Method method = null;
        try {
            method = (Method) AccessController.doPrivileged(new PrivilegedExceptionAction() {
                public Object run() {
                    Method[] methods = cls.getMethods();
                    if (methods != null) {
                        for (int i = 0; i < methods.length; i++) {
                            if (methods[i].getName().equals(methodName)) {
                                return methods[i];
                            }
                        }
                    }
                    return null;
                }
            });
        } catch (PrivilegedActionException e) {

        }

        return method;
    }

    /** @return ClassLoader */
    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 (RuntimeException) e.getException();
        }

        return cl;
    }

    /**
     * @param cls
     * @return true if cls appears to be a JAXB enabled class
     */
    private static boolean isJAXB(Class cls) {
        // See if the object represents a root element
        XmlRootElement root = (XmlRootElement) getAnnotation(cls, XmlRootElement.class);
        if (root != null) {
            if (log.isDebugEnabled()) {
                log.debug("isJAXB returns true due to presence of @XmlRootElement on " + cls);
            }
            return true;
        }

        // See if the object represents an type
        XmlType type = (XmlType) getAnnotation(cls, XmlType.class);
        if (type != null) {
            if (log.isDebugEnabled()) {
                log.debug("isJAXB returns true due to presence of @XmlType on " + cls);
            }
            return true;
        }
        if (log.isDebugEnabled()) {
            log.debug("isJAXB returns false for" + cls);
        }
        return false;
    }

    /**
     * Get an annotation.  This is wrappered to avoid a Java2Security violation.
     * @param cls Class that contains annotation 
     * @param annotation Class of requested Annotation
     * @return annotation or null
     */
    private static Annotation getAnnotation(final AnnotatedElement element, final Class annotation) {
        Annotation anno = null;
        try {
            anno = (Annotation) AccessController.doPrivileged(new PrivilegedAction() {
                public Object run() {
                    return element.getAnnotation(annotation);
                }
            });
        } catch (Throwable t) {
            if (log.isDebugEnabled()) {
                log.debug("Problem occurred.  Continuing.  The problem is " + t);
            }
        }
        return anno;
    }
}