/* * 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 org.apache.axis2.description.AxisService; import org.apache.axis2.description.Parameter; import org.apache.axis2.java.security.AccessController; import org.apache.axis2.jaxws.ExceptionFactory; import org.apache.axis2.jaxws.description.EndpointDescription; import org.apache.axis2.jaxws.description.EndpointInterfaceDescription; import org.apache.axis2.jaxws.description.FaultDescription; import org.apache.axis2.jaxws.description.OperationDescription; import org.apache.axis2.jaxws.description.ParameterDescription; import org.apache.axis2.jaxws.description.ServiceDescription; import org.apache.axis2.jaxws.i18n.Messages; 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.axis2.util.JavaUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import javax.xml.bind.JAXBElement; import java.lang.reflect.Method; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; import java.util.HashMap; import java.util.Map; /** * Walks the ServiceDescription and its child *Description classes to find all of the types. An * AnnotationDesc is built for each of the types */ public class AnnotationBuilder { private static Log log = LogFactory.getLog(AnnotationBuilder.class); /** This is a static utility class. The constructor is intentionally private */ private AnnotationBuilder() { } /** * @param serviceDescription ServiceDescription * @param ap ArtifactProcessor which found/produced artifact classes * @return AnnotationDesc Map */ public static Map<String, AnnotationDesc> getAnnotationDescs(ServiceDescription serviceDesc, ArtifactProcessor ap) { Map<String, AnnotationDesc> map = new HashMap<String, AnnotationDesc>(); EndpointDescription[] endpointDescs = serviceDesc.getEndpointDescriptions(); // Build a set of packages from all of the endpoints if (endpointDescs != null) { for (int i = 0; i < endpointDescs.length; i++) { getAnnotationDescs(endpointDescs[i], ap, map); } } return map; } /** * @param endpointDesc * @param ap ArtifactProcessor which found/produced artifact classes * @param map */ private static void getAnnotationDescs(EndpointDescription endpointDesc, ArtifactProcessor ap, Map<String, AnnotationDesc> map) { String implClassName = getServiceImplClassName(endpointDesc); if (implClassName != null) { Class clz; clz = loadClass(implClassName); if(clz == null){ clz = loadClass(implClassName, endpointDesc.getAxisService().getClassLoader()); } if (clz != null) { addAnnotation(endpointDesc, clz, map); } } EndpointInterfaceDescription endpointInterfaceDesc = endpointDesc.getEndpointInterfaceDescription(); if (endpointInterfaceDesc != null) { getAnnotationDescs(endpointDesc, endpointInterfaceDesc, ap, map); } } /** * @param endpointInterfaceDesc * @param ap ArtifactProcessor which found/produced artifact classes * @param map */ private static void getAnnotationDescs(EndpointDescription endpointDesc, EndpointInterfaceDescription endpointInterfaceDesc, ArtifactProcessor ap, Map<String, AnnotationDesc> map) { Class clz = endpointInterfaceDesc.getSEIClass(); if (clz != null) { addAnnotation(endpointDesc, clz, map); } // Don't dig into the async operations OperationDescription[] opDescs = endpointInterfaceDesc.getDispatchableOperations(); // Build a set of packages from all of the opertions if (opDescs != null) { for (int i = 0; i < opDescs.length; i++) { getAnnotationDescs(endpointDesc, opDescs[i], ap, map); } } } /** * Get annotations for this operation * * @param opDesc * @param ap ArtifactProcessor which found/produced artifact classes * @param map */ private static void getAnnotationDescs(EndpointDescription endpointDesc, OperationDescription opDesc, ArtifactProcessor ap, Map<String, AnnotationDesc> map) { // Walk the parameter information ParameterDescription[] parameterDescs = opDesc.getParameterDescriptions(); if (parameterDescs != null) { for (int i = 0; i < parameterDescs.length; i++) { getAnnotationDescs(endpointDesc, parameterDescs[i], map); } } // Walk the fault information FaultDescription[] faultDescs = opDesc.getFaultDescriptions(); if (faultDescs != null) { for (int i = 0; i < faultDescs.length; i++) { getAnnotationDescs(endpointDesc, faultDescs[i], ap, map); } } // Also consider the request and response wrappers String wrapperName = ap.getRequestWrapperMap().get(opDesc); if (wrapperName != null) { addAnnotation(endpointDesc, wrapperName, map); } wrapperName = ap.getResponseWrapperMap().get(opDesc); if (wrapperName != null) { addAnnotation(endpointDesc, wrapperName, map); } // Finally consider the result type Class cls = opDesc.getResultActualType(); if (cls != null && cls != void.class && cls != Void.class) { addAnnotation(endpointDesc, cls, map); } } private static void getAnnotationDescs(EndpointDescription endpointDesc, ParameterDescription paramDesc, Map<String, AnnotationDesc> map) { // Get the type that defines the actual data. (this is never a holder ) Class paramClass = paramDesc.getParameterActualType(); if (paramClass != null) { getTypeAnnotationDescs(endpointDesc, paramClass, map); } } /** * Update the package set with the packages referenced by this FaultDescription * * @param faultDesc FaultDescription * @param set Set<Package> that is updated */ private static void getAnnotationDescs(EndpointDescription endpointDesc, FaultDescription faultDesc, ArtifactProcessor ap, Map<String, AnnotationDesc> map) { FaultBeanDesc faultBeanDesc = ap.getFaultBeanDescMap().get(faultDesc); Class faultBean = loadClass(faultBeanDesc.getFaultBeanClassName()); if(faultBean == null) { faultBean = loadClass(faultBeanDesc.getFaultBeanClassName(), endpointDesc.getAxisService().getClassLoader()); } if (faultBean != null) { getTypeAnnotationDescs(endpointDesc, faultBean, map); } } private final static Class[] noClass = new Class[] { }; /** * Get the annotations for this type * * @param cls */ private static void getTypeAnnotationDescs(EndpointDescription endpointDesc, Class cls, Map<String, AnnotationDesc> map) { if (JAXBElement.class.isAssignableFrom(cls)) { try { Method m = cls.getMethod("getValue", noClass); Class cls2 = m.getReturnType(); addAnnotation(endpointDesc, cls2, map); } catch (Exception e) { // We should never get here if (log.isDebugEnabled()) { log.debug("Cannot find JAXBElement.getValue method."); } } } else { addAnnotation(endpointDesc, cls, map); } } private static void addAnnotation(EndpointDescription endpointDesc, String className, Map<String, AnnotationDesc> map) { if (map.get(className) == null) { Class clz = loadClass(className); if (clz == null) { clz = loadClass(className, endpointDesc.getAxisService().getClassLoader()); } if (clz != null) { addAnnotation(endpointDesc, clz, map); } } } private static void addAnnotation(EndpointDescription endpointDesc, Class cls, Map<String, AnnotationDesc> map) { String className = cls.getCanonicalName(); if (map.get(className) == null) { AnnotationDesc desc = AnnotationDescImpl.create(cls); map.put(className, desc); if (cls.isPrimitive()) { Class class2 = ClassUtils.getWrapperClass(cls); AnnotationDesc desc2 = AnnotationDescImpl.create(class2); map.put(class2.getCanonicalName(), desc2); } else { Class class2 = ClassUtils.getPrimitiveClass(cls); if (class2 != null) { AnnotationDesc desc2 = AnnotationDescImpl.create(class2); map.put(class2.getCanonicalName(), desc2); } } // Inspect the interfaces. This is done to pick up other // @XmlSeeAlso usages. Class[] interfaces = cls.getInterfaces(); if (interfaces != null) { for (int i=0; i<interfaces.length; i++) { addAnnotation(endpointDesc, interfaces[i], map); } } } } /** * Loads the class * * @param className * @return Class (or null if the class cannot be loaded) */ private static Class loadClass(String className, ClassLoader loader) { // Don't make this public, its a security exposure if (className == null || className.length() == 0) { return null; } try { return forName(className, true, loader); //Catch Throwable as ClassLoader can throw an NoClassDefFoundError that //does not extend Exception, so lets catch everything that extends Throwable //rather than just Exception. } catch (Throwable e) { // Depending on the jaxws scenario, it is normal that some classes are not available. if (log.isDebugEnabled()) { log.debug("AnnotationBuilder did not find the following class:" + className); } } return null; } /** * Loads the class * * @param className * @return Class (or null if the class cannot be loaded) */ private static Class loadClass(String className) { // Don't make this public, its a security exposure if (className == null || className.length() == 0) { return null; } try { return forName(className, true, getContextClassLoader()); //Catch Throwable as ClassLoader can throw an NoClassDefFoundError that //does not extend Exception, so lets catch everything that extends Throwable //rather than just Exception. } catch (Throwable e) { // Depending on the jaxws scenario, it is normal that some classes are not available. if (log.isDebugEnabled()) { log.debug("AnnotationBuilder did not find the following class:" + className); } } return null; } /** * Return the class for this name * * @return Class */ 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); if (cls == null) { cls = Class.forName(className, initialize, classloader); } return cls; } } ); } catch (PrivilegedActionException e) { /* An exception should NOT be logged. Depending on the JAXWS scenario, certain classes * may or may not exist. Putting an exception in the log will confuse programmers who * are servicing the product * if (log.isDebugEnabled()) { log.debug("Exception thrown from AccessController: " + e); } */ throw (ClassNotFoundException)e.getException(); } return cl; } /** @return ClassLoader */ 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; } /** * Get the Serivce Impl Class by looking at the AxisService * @param endpointDescription * @return class name or null */ static private String getServiceImplClassName(EndpointDescription endpointDescription) { String result = null; if (endpointDescription != null) { AxisService as = endpointDescription.getAxisService(); if (as != null) { Parameter param = as.getParameter(org.apache.axis2.Constants.SERVICE_CLASS); // If there was no implementation class, we should not go any further if (param != null) { result = ((String)param.getValue()).trim(); } } } return result; } }