blob: fa7346478d804504efabf07fec2556fc036885fe [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.axis2.jaxws.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;
}
}