| /* |
| * 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.EndpointDescriptionJava; |
| 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.description.ServiceDescriptionWSDL; |
| import org.apache.axis2.jaxws.runtime.description.marshal.AnnotationDesc; |
| import org.apache.axis2.jaxws.runtime.description.marshal.FaultBeanDesc; |
| import org.apache.axis2.jaxws.runtime.description.marshal.MarshalServiceRuntimeDescription; |
| import org.apache.axis2.jaxws.util.WSDL4JWrapper; |
| import org.apache.axis2.jaxws.util.WSDLWrapper; |
| import org.apache.axis2.jaxws.utility.ClassUtils; |
| import org.apache.axis2.jaxws.utility.JavaUtils; |
| import org.apache.axis2.jaxws.wsdl.SchemaReader; |
| import org.apache.axis2.jaxws.wsdl.SchemaReaderException; |
| import org.apache.axis2.jaxws.wsdl.impl.SchemaReaderImpl; |
| import org.apache.commons.logging.Log; |
| import org.apache.commons.logging.LogFactory; |
| |
| import javax.wsdl.Definition; |
| import javax.wsdl.WSDLException; |
| import javax.xml.bind.JAXBElement; |
| import java.io.File; |
| import java.io.IOException; |
| import java.lang.reflect.Method; |
| import java.net.MalformedURLException; |
| import java.net.URL; |
| import java.security.PrivilegedActionException; |
| import java.security.PrivilegedExceptionAction; |
| import java.util.Collection; |
| import java.util.Set; |
| import java.util.TreeSet; |
| |
| /** |
| * In order to marshal or unmarshal the user data, we need to know the set of packages involved. |
| * The set of packages is used to construct an appropriate JAXBContext object during the |
| * marshalling/unmarshalling. |
| * <p/> |
| * There are two ways to get this data. |
| * <p/> |
| * Schema Walk (preferred): Get the list of packages by walking the schemas that are referenced by |
| * the wsdl (or generated wsdl). Each schema represents a different package. The package is |
| * obtained using the jaxb customization or JAXB default ns<->package rule. |
| * <p/> |
| * Annotation Walk(secondary) : Walk the list of Endpoints, Operations, Parameters, etc. and build a |
| * list of packages by looking at the classes involved. |
| * <p/> |
| * The Schema Walk is faster and more complete, but relies on the presence of the schema or wsdl. |
| * <p/> |
| * The Annotation Walk is slower and is not complete. For example, the annotation walk may not |
| * discover the packages for derived types that are defined in a different schema than the formal |
| * parameter types. |
| */ |
| public class PackageSetBuilder { |
| |
| private static Log log = LogFactory.getLog(PackageSetBuilder.class); |
| |
| /** This is a static utility class. The constructor is intentionally private */ |
| private PackageSetBuilder() { |
| } |
| |
| /** |
| * Walks the schemas of the serviceDesc's wsdl (or generated wsdl) to determine the list of |
| * packages. This is the preferred algorithm for discovering the package set. |
| * |
| * @param serviceDesc ServiceDescription |
| * @return Set of Packages |
| */ |
| public static TreeSet<String> getPackagesFromSchema(ServiceDescription serviceDesc) { |
| |
| TreeSet<String> set = new TreeSet<String>(); |
| //If we are on client side we will get wsdl definition from ServiceDescription. If we are on server side we will have to |
| //read wsdlLocation from @WebService Annotation. |
| ServiceDescriptionWSDL sdw = (ServiceDescriptionWSDL)serviceDesc; |
| Definition wsdlDefinition = sdw.getWSDLDefinition(); |
| Collection<EndpointDescription> endpointDescs = serviceDesc.getEndpointDescriptions_AsCollection(); |
| if (endpointDescs != null) { |
| for (EndpointDescription ed:endpointDescs) { |
| |
| if (wsdlDefinition == null) { |
| // TODO I don't think we should be trying to load the wsdlDefinition here. |
| |
| //Let see if we can get wsdl definition from endpoint @WebService annotation. |
| if (ed instanceof EndpointDescriptionJava) { |
| String wsdlLocation = |
| ((EndpointDescriptionJava)ed).getAnnoWebServiceWSDLLocation(); |
| wsdlDefinition = getWSDLDefinition(wsdlLocation); |
| } |
| } |
| //So at this point either we got wsdl definition from ServiceDescription (which means we are running this code |
| //on client side) or we got it from the @WebService annotation (which means we are running this code on server side) |
| if (wsdlDefinition != null) { |
| SchemaReader sr = new SchemaReaderImpl(); |
| try { |
| Set<String> pkgSet = sr.readPackagesFromSchema(wsdlDefinition); |
| set.addAll(pkgSet); |
| } catch (SchemaReaderException e) { |
| throw ExceptionFactory.makeWebServiceException(e); |
| } |
| } |
| } |
| } |
| return set; |
| } |
| |
| /** |
| * @param serviceDescription ServiceDescription |
| * @return Set of Packages |
| */ |
| public static TreeSet<String> getPackagesFromAnnotations(ServiceDescription serviceDesc, |
| MarshalServiceRuntimeDescription msrd) { |
| TreeSet<String> set = new TreeSet<String>(); |
| Collection<EndpointDescription> endpointDescs = serviceDesc.getEndpointDescriptions_AsCollection(); |
| |
| // Build a set of packages from all of the endpoints |
| if (endpointDescs != null) { |
| for (EndpointDescription endpointDesc: endpointDescs) { |
| set.addAll(getPackagesFromAnnotations(endpointDesc, msrd)); |
| } |
| } |
| return set; |
| } |
| |
| /** |
| * @param endpointDesc EndpointDescription |
| * @return Set of Packages |
| */ |
| private static TreeSet<String> getPackagesFromAnnotations(EndpointDescription endpointDesc, |
| MarshalServiceRuntimeDescription msrd) { |
| |
| TreeSet<String> set = new TreeSet<String>(); |
| String implClassName = getServiceImplClassName(endpointDesc); |
| if (implClassName != null) { |
| Class clz = loadClass(implClassName); |
| if(clz == null){ |
| clz = loadClass(implClassName, endpointDesc.getAxisService().getClassLoader()); |
| } |
| if (clz != null) { |
| addXmlSeeAlsoPackages(clz, msrd, set); |
| } |
| } |
| EndpointInterfaceDescription endpointInterfaceDesc = |
| endpointDesc.getEndpointInterfaceDescription(); |
| if (endpointInterfaceDesc != null) { |
| getPackagesFromAnnotations(endpointDesc, endpointInterfaceDesc, set, msrd); |
| } |
| return set; |
| } |
| |
| /** |
| * @param endpointInterfaceDescription EndpointInterfaceDescription |
| * @param Set of Packages |
| * @param msrd |
| */ |
| private static void getPackagesFromAnnotations( |
| EndpointDescription ed, |
| EndpointInterfaceDescription endpointInterfaceDesc, |
| TreeSet<String> set, |
| MarshalServiceRuntimeDescription msrd) { |
| |
| OperationDescription[] opDescs = endpointInterfaceDesc.getDispatchableOperations(); |
| |
| // Inspect the @XmlSeeAlso classes on the interface |
| addXmlSeeAlsoPackages(endpointInterfaceDesc.getSEIClass(), msrd, set); |
| |
| |
| // Build a set of packages from all of the operations |
| if (opDescs != null) { |
| for (int i = 0; i < opDescs.length; i++) { |
| getPackagesFromAnnotations(ed, opDescs[i], set, msrd); |
| } |
| } |
| return; |
| } |
| |
| /** |
| * Update the package set with the packages referenced by this OperationDesc |
| * |
| * @param opDesc OperationDescription |
| * @param set Set<Package> that is updated |
| */ |
| private static void getPackagesFromAnnotations(EndpointDescription ed, OperationDescription opDesc, TreeSet<String> set, |
| MarshalServiceRuntimeDescription msrd) { |
| |
| // Walk the parameter information |
| ParameterDescription[] parameterDescs = opDesc.getParameterDescriptions(); |
| if (parameterDescs != null) { |
| for (int i = 0; i < parameterDescs.length; i++) { |
| getPackagesFromAnnotations(parameterDescs[i], set, msrd); |
| } |
| } |
| |
| // Walk the fault information |
| FaultDescription[] faultDescs = opDesc.getFaultDescriptions(); |
| if (faultDescs != null) { |
| for (int i = 0; i < faultDescs.length; i++) { |
| getPackagesFromAnnotations(ed, faultDescs[i], set, msrd); |
| } |
| } |
| |
| // Also consider the request and response wrappers |
| String pkg = getPackageFromClassName(msrd.getRequestWrapperClassName(opDesc)); |
| if (log.isDebugEnabled()) { |
| log.debug("Package from Request Wrapper annotation = " + pkg); |
| } |
| if (pkg != null) { |
| set.add(pkg); |
| } |
| pkg = getPackageFromClassName(msrd.getResponseWrapperClassName(opDesc)); |
| if (log.isDebugEnabled()) { |
| log.debug("Package from Response Wrapper annotation = " + pkg); |
| } |
| if (pkg != null) { |
| set.add(pkg); |
| } |
| |
| // Finally consider the result type |
| Class cls = opDesc.getResultActualType(); |
| if (cls != null && cls != void.class && cls != Void.class) { |
| Package returnTypePkg = cls.getPackage(); |
| if (log.isDebugEnabled()) { |
| log.debug("Package from Return Type = " + pkg); |
| } |
| if (returnTypePkg != null) { |
| pkg = returnTypePkg.getName(); |
| set.add(pkg); |
| } |
| } |
| } |
| |
| /** |
| * Update the package set with the packages referenced by this ParameterDescription |
| * |
| * @param paramDesc ParameterDesc |
| * @param set Set<Package> that is updated |
| */ |
| private static void getPackagesFromAnnotations(ParameterDescription paramDesc, |
| TreeSet<String> set, |
| MarshalServiceRuntimeDescription msrd) { |
| |
| // Get the type that defines the actual data. (this is never a holder ) |
| Class paramClass = paramDesc.getParameterActualType(); |
| |
| if (paramClass != null) { |
| setTypeAndElementPackages(paramClass, paramDesc.getTargetNamespace(), |
| paramDesc.getPartName(), set, msrd); |
| } |
| |
| } |
| |
| /** |
| * Update the package set with the packages referenced by this FaultDescription |
| * |
| * @param faultDesc FaultDescription |
| * @param set Set<Package> that is updated |
| */ |
| private static void getPackagesFromAnnotations(EndpointDescription ed, |
| FaultDescription faultDesc, TreeSet<String> set, |
| MarshalServiceRuntimeDescription msrd) { |
| |
| FaultBeanDesc faultBeanDesc = msrd.getFaultBeanDesc(faultDesc); |
| if(faultBeanDesc == null){ |
| if(log.isDebugEnabled()){ |
| log.debug("faultBeanDesc from MarshallServiceRuntimeDescription is null"); |
| } |
| //NO FaultBeanDesc found nothing we can do. |
| return; |
| } |
| String faultBeanName = faultBeanDesc.getFaultBeanClassName(); |
| if(faultBeanName == null){ |
| if(log.isDebugEnabled()){ |
| log.debug("FaultBeanName is null"); |
| } |
| //We cannot load the faultBeanName |
| return; |
| } |
| Class faultBean = loadClass(faultBeanName); |
| if(faultBean == null){ |
| faultBean = loadClass(faultBeanName, ed.getAxisService().getClassLoader()); |
| } |
| if (faultBean != null) { |
| setTypeAndElementPackages(faultBean, faultBeanDesc.getFaultBeanNamespace(), |
| faultBeanDesc.getFaultBeanLocalName(), set, msrd); |
| } |
| } |
| |
| /** |
| * For each data element, we need the package for both the element and its type. |
| * |
| * @param cls Class representing element, type or both |
| * @param namespace of the element |
| * @param localPart of the element |
| * @param set with both type and element packages set |
| */ |
| private static void setTypeAndElementPackages(Class cls, String namespace, String localPart, |
| TreeSet<String> set, |
| MarshalServiceRuntimeDescription msrd) { |
| |
| // Get the element and type classes |
| Class eClass = getElement(cls, msrd); |
| Class tClass = getType(cls); |
| |
| // Set the package for the type |
| if (tClass != null) { |
| Package typePkg = tClass.getPackage(); |
| //For primitive types there is no package |
| String pkg = (typePkg != null) ? typePkg.getName() : null; |
| |
| if (pkg != null) { |
| set.add(pkg); |
| } |
| addXmlSeeAlsoPackages(tClass, msrd, set); |
| } |
| |
| // Set the package for the element |
| if (tClass != eClass) { |
| if (eClass == null) { |
| // A null or empty namespace indicates that the element is |
| // unqualified. This can occur if the parameter is represented as a child element |
| // in doc/lit wrapped. The package is determined from the wrapper element in such casses. |
| if (namespace != null && namespace.length() > 0) { |
| // Use default namespace to package algorithm |
| String pkg = makePackage(namespace); |
| if (pkg != null) { |
| set.add(pkg); |
| } |
| } |
| } else { |
| Package elementPkg = eClass.getPackage(); |
| String pkg = (elementPkg != null) ? elementPkg.getName() : null; |
| if (pkg != null) { |
| set.add(pkg); |
| } |
| addXmlSeeAlsoPackages(tClass, msrd, set); |
| } |
| } |
| } |
| |
| /** |
| * If cls represents an xml element then cls is returned. Otherwise null is returned |
| * |
| * @param cls Class |
| * @return Class or null |
| */ |
| private static Class getElement(Class cls, MarshalServiceRuntimeDescription msrd) { |
| AnnotationDesc annotationDesc = msrd.getAnnotationDesc(cls); |
| if (annotationDesc == null) { |
| // This shouldn't happen |
| annotationDesc = AnnotationDescImpl.create(cls); |
| } |
| if (annotationDesc.hasXmlRootElement()) { |
| return cls; |
| } |
| return null; |
| } |
| |
| private final static Class[] noClass = new Class[] { }; |
| |
| /** |
| * Returns the class that defines the type. |
| * |
| * @param cls |
| * @return |
| */ |
| private static Class getType(Class cls) { |
| if (JAXBElement.class.isAssignableFrom(cls)) { |
| try { |
| Method m = cls.getMethod("getValue", noClass); |
| return m.getReturnType(); |
| } catch (Exception e) { |
| // We should never get here |
| if (log.isDebugEnabled()) { |
| log.debug("Cannot find JAXBElement.getValue method."); |
| } |
| return null; |
| } |
| } else { |
| return cls; |
| } |
| } |
| |
| /** |
| * Default Namespace to Package algorithm |
| * |
| * @param ns |
| * @return |
| */ |
| private static String makePackage(String ns) { |
| String pkgName = JavaUtils.getPackageFromNamespace(ns); |
| return pkgName; |
| } |
| |
| /** |
| * Return the package associated with the class name. The className may not be specified (in |
| * which case a null Package is returned). if class has unnamed package return "" |
| * |
| * @param className String (may be null or empty) |
| * @return Package or null if problems occur |
| */ |
| private static String getPackageFromClassName(String className) { |
| Class clz = loadClass(className); |
| String pkg = |
| (clz == null) ? null : (clz.getPackage() == null) ? "" : clz.getPackage().getName(); |
| return pkg; |
| } |
| |
| private static void addXmlSeeAlsoPackages(Class clz, |
| MarshalServiceRuntimeDescription msrd, |
| TreeSet<String> set) { |
| if (clz != null) { |
| AnnotationDesc aDesc = msrd.getAnnotationDesc(clz); |
| if (aDesc != null) { |
| Class[] seeAlso = aDesc.getXmlSeeAlsoClasses(); |
| if (seeAlso != null) { |
| for (int i=0; i<seeAlso.length; i++) { |
| String pkg = |
| (seeAlso[i] == null) ? null : |
| (seeAlso[i].getPackage() == null) ? "" : |
| seeAlso[i].getPackage().getName(); |
| if (pkg != null) { |
| set.add(pkg); |
| } |
| } |
| } |
| } |
| |
| Class[] interfaces = clz.getInterfaces(); |
| if (interfaces != null) { |
| for (int i=0; i<interfaces.length; i++) { |
| addXmlSeeAlsoPackages(interfaces[i], msrd, set); |
| } |
| } |
| } |
| } |
| /** |
| * 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 { |
| // Class.forName does not support primitives |
| Class cls = ClassUtils.getPrimitiveClass(className); |
| if (cls == null) { |
| cls = Class.forName(className, true, getContextClassLoader()); |
| } |
| return cls; |
| //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) { |
| // TODO Should the exception be swallowed ? |
| if (log.isDebugEnabled()) { |
| log.debug("PackageSetBuilder cannot load 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, ClassLoader loader) { |
| // Don't make this public, its a security exposure |
| if (className == null || className.length() == 0) { |
| return null; |
| } |
| try { |
| // Class.forName does not support primitives |
| Class cls = ClassUtils.getPrimitiveClass(className); |
| if (cls == null) { |
| cls = Class.forName(className, true, loader); |
| } |
| return cls; |
| //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) { |
| // TODO Should the exception be swallowed ? |
| if (log.isDebugEnabled()) { |
| log.debug("PackageSetBuilder cannot load the following class:" + className); |
| } |
| } |
| return null; |
| } |
| |
| private static Definition getWSDLDefinition(String wsdlLoc){ |
| Definition wsdlDefinition = null; |
| final String wsdlLocation = wsdlLoc; |
| if (wsdlLocation != null && wsdlLocation.trim().length() > 0) { |
| try { |
| wsdlDefinition = (Definition) AccessController.doPrivileged( |
| new PrivilegedExceptionAction() { |
| public Object run() throws MalformedURLException, IOException, WSDLException { |
| String baseDir = new File(System.getProperty("basedir",".")).getCanonicalPath(); |
| String wsdlLocationPath = new File(baseDir +File.separator+ wsdlLocation).getAbsolutePath(); |
| File file = new File(wsdlLocationPath); |
| URL url = file.toURL(); |
| if(log.isDebugEnabled()){ |
| log.debug("Reading WSDL from URL:" +url.toString()); |
| } |
| // This is a temporary wsdl and we use it to dig into the schemas, |
| // Thus the memory limit is set to false. It will be discarded after it is used |
| // by the PackageSetBuilder implementation. |
| WSDLWrapper wsdlWrapper = new WSDL4JWrapper(url, false, 0); |
| return wsdlWrapper.getDefinition(); |
| } |
| }); |
| } catch (PrivilegedActionException e) { |
| // Swallow and continue |
| if (log.isDebugEnabled()) { |
| log.debug("Exception getting wsdlLocation: " +e.getException()); |
| } |
| } |
| } |
| |
| return wsdlDefinition; |
| } |
| |
| /** |
| * 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) { |
| 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; |
| } |
| } |