blob: a9e78abb00dfa7b4497f2afc376f4e4eace6f1df [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.java.security.AccessController;
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.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;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.HashMap;
import java.util.Map;
/**
* 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<FaultDescription, FaultBeanDesc> faultBeanDescMap =
new HashMap<FaultDescription, FaultBeanDesc>();
/**
* 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;
}
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 vaules."
// 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();
if (requestWrapperName == null) {
if (packageName.length() > 0) {
requestWrapperName =
packageName + "." + javaMethodToClassName(methodName);
} else {
requestWrapperName = javaMethodToClassName(methodName);
}
}
String foundRequestWrapperName = findArtifact(requestWrapperName);
if (foundRequestWrapperName == null) {
foundRequestWrapperName = findArtifact(requestWrapperName, ed.getAxisService().getClassLoader());
}
if (foundRequestWrapperName == null) {
foundRequestWrapperName = missingArtifact(requestWrapperName);
}
if (foundRequestWrapperName != null) {
requestWrapperMap.put(opDesc, foundRequestWrapperName);
}
// @ResponseWrapper className processing
String responseWrapperName = opDesc.getResponseWrapperClassName();
if (responseWrapperName == null) {
if (packageName.length() > 0) {
responseWrapperName = packageName + "." +
javaMethodToClassName(methodName) + "Response";
} else {
responseWrapperName = javaMethodToClassName(methodName) + "Response";
}
}
String foundResponseWrapperName = findArtifact(responseWrapperName);
if (foundResponseWrapperName == null) {
foundResponseWrapperName = findArtifact(responseWrapperName, ed.getAxisService().getClassLoader());
}
if (foundResponseWrapperName == null) {
foundResponseWrapperName = missingArtifact(responseWrapperName);
}
if (foundResponseWrapperName != null) {
responseWrapperMap.put(opDesc, foundResponseWrapperName);
}
for (FaultDescription faultDesc : opDesc.getFaultDescriptions()) {
FaultBeanDesc faultBeanDesc = create(ed, faultDesc, opDesc);
faultBeanDescMap.put(faultDesc, faultBeanDesc);
}
}
}
}
}
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 by looking for the
* a class named <exceptionName>Bean in the interface's package.
* 4) If not present, the class is found by looking for the
* a class named <exceptionName>Bean in the interface + jaxws package
*/
String faultBeanClassName = faultDesc.getFaultBean();
if (faultBeanClassName == null || faultBeanClassName.length() == 0) {
faultBeanClassName = faultDesc.getFaultInfo();
}
if (faultBeanClassName == null || faultBeanClassName.length() == 0) {
String declaringClassName = opDesc.getJavaDeclaringClassName();
String packageName = getPackageName(declaringClassName);
String simpleName = getSimpleClassName(faultDesc.getExceptionClassName());
if (packageName.length() > 0) {
faultBeanClassName = packageName + "." + simpleName + "Bean";
} else {
faultBeanClassName = simpleName + "Bean";
}
}
String foundClassName = findArtifact(faultBeanClassName);
if (foundClassName == null) {
faultBeanClassName = findArtifact(faultBeanClassName, ed.getAxisService().getClassLoader());
}
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;
}
/**
* Determine the actual packager name for the generated artifacts by trying to load the class
* from one of two packages. This is necessary because the RI implementations of WSGen and
* WSImport generate the artifacts in different packages: - WSImport generates the artifacts in
* the same package as the SEI - WSGen generates the artifacts in a "jaxws" sub package under
* the SEI package. 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. ^^^^^^^^^^^^^^^^
*
* @param artifactClassName
* @return
*/
static final String JAXWS_SUBPACKAGE = "jaxws";
private static String findArtifact(String artifactClassName) {
return findArtifact(artifactClassName, getContextClassLoader());
}
private static String findArtifact(String artifactClassName, ClassLoader classLoader) {
String returnArtifactClassName = null;
if (artifactClassName == null) {
return returnArtifactClassName;
}
// Try to load the class that was passed in
try {
forName(artifactClassName, true, classLoader);
returnArtifactClassName = artifactClassName;
}
catch (ClassNotFoundException e) {
// Couldn't load the class; we'll try another one below.
}
// If the original class couldn't be loaded, try adding ".jaxws." to the package
if (returnArtifactClassName == null) {
String originalPackage = getPackageName(artifactClassName);
if (originalPackage.length() > 0) {
String alternatePackage = originalPackage + "." + JAXWS_SUBPACKAGE;
String className = getSimpleClassName(artifactClassName);
String alternateWrapperClass = alternatePackage + "." + className;
try {
loadClass(alternateWrapperClass, getContextClassLoader());
returnArtifactClassName = alternateWrapperClass;
}
catch (ClassNotFoundException e) {
// Couldn't load the class
}
}
}
return returnArtifactClassName;
}
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 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;
}
}