blob: 28fd2afe735ed425e137893351171e6691e54512 [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.tuscany.sca.interfacedef.java.jaxws;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.ref.WeakReference;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import javax.xml.namespace.QName;
import javax.xml.ws.WebFault;
import org.apache.tuscany.sca.interfacedef.Operation;
import org.apache.tuscany.sca.interfacedef.java.JavaInterface;
import org.apache.tuscany.sca.interfacedef.java.impl.JavaInterfaceUtil;
import org.objectweb.asm.ClassWriter;
public class FaultBeanGenerator extends BaseBeanGenerator {
public FaultBeanGenerator() {
super();
}
protected BeanProperty[] getProperties(Class<? extends Throwable> exceptionClass) {
BeanInfo beanInfo;
try {
beanInfo = Introspector.getBeanInfo(exceptionClass);
} catch (IntrospectionException e) {
throw new IllegalArgumentException(e);
}
List<BeanProperty> props = new ArrayList<BeanProperty>();
for (PropertyDescriptor pd : beanInfo.getPropertyDescriptors()) {
if (pd.getReadMethod() != null) {
String name = pd.getReadMethod().getName();
if ("getClass".equals(name) || "getStackTrace".equals(name) || "getSuppressed".equals(name)
|| "getCause".equals(name)
|| "getLocalizedMessage".equals(name)) {
continue;
}
// Add the field
String field = pd.getName();
Method getter = pd.getReadMethod();
props.add(new BeanProperty("", field, getter.getReturnType(), getter.getGenericReturnType(), false));
}
}
Collections.sort(props, new Comparator<BeanProperty>() {
public int compare(BeanProperty o1, BeanProperty o2) {
return o1.getName().compareTo(o2.getName());
}
});
return props.toArray(new BeanProperty[0]);
}
public byte[] generate(Class<? extends Throwable> exceptionClass, Operation operation) {
// The reflection code here allows for toleration of older versions of ASM.
ClassWriter cw;
try {
Constructor<ClassWriter> c = ClassWriter.class.getConstructor(new Class[] {int.class});
Field f = ClassWriter.class.getField("COMPUTE_MAXS");
cw = c.newInstance(f.get(null));
} catch ( Exception ex ) {
try {
Constructor<ClassWriter> c = ClassWriter.class.getConstructor(new Class[] {boolean.class});
cw = c.newInstance(true);
} catch ( Exception ex2 ) {
throw new IllegalArgumentException(ex2);
}
}
// TUSCANY-3283 - all generated classes (including exception) should go in the namespace
// of the interface not the namespace of the originating exception.
// consequently we need to create a matching package name for the schema
QName element = getElementName(exceptionClass, operation);
String name = element.getLocalPart();
String namespace = element.getNamespaceURI();
String className = getFaultBeanName(exceptionClass, operation);
String classDescriptor = className.replace('.', '/');
String classSignature = "L" + classDescriptor + ";";
return defineClass(cw, classDescriptor, classSignature, namespace, name, getProperties(exceptionClass));
}
public Class<?> generate(Class<? extends Throwable> exceptionClass, GeneratedClassLoader cl, Operation operation) {
synchronized (exceptionClass) {
QName element = getElementName(exceptionClass, operation);
WeakReference<Class<?>> wr = generatedClasses.get(element);
Class<?> faultBeanClass = null;
if (wr != null){
faultBeanClass = wr.get();
}
if (faultBeanClass == null) {
// TUSCANY-3283 - all generated classes (including exception) should go in the namespace
// of the interface not the namespace of the originating exception.
// consequently we need to create a matching package name for the schema
String name = element.getLocalPart();
String namespace = element.getNamespaceURI();
String className = getFaultBeanName(exceptionClass, operation);
String classDescriptor = className.replace('.', '/');
String classSignature = "L" + classDescriptor + ";";
faultBeanClass = generate(classDescriptor, classSignature, namespace, name, getProperties(exceptionClass), cl);
generatedClasses.put(element, new WeakReference<Class<?>>(faultBeanClass));
}
return faultBeanClass;
}
}
private static String getFaultBeanName(Class<?> exceptionClass, Operation operation) {
// TUSCANY-3283 - all generated classes (including exception) should go in the namespace
// of the interface not the namespace of the originating exception.
// consequently we need to create a matching package name for the schema
String interfacePkg = null;
if (operation != null && operation.getInterface() instanceof JavaInterface){
interfacePkg = ((JavaInterface)operation.getInterface()).getJavaClass().getPackage().getName();
}
String faultBeanName = null;
WebFault webFault = exceptionClass.getAnnotation(WebFault.class);
if (webFault != null) {
faultBeanName = webFault.faultBean();
if (!"".equals(faultBeanName)) {
return faultBeanName;
}
}
String name = exceptionClass.getName();
int index = name.lastIndexOf('.');
String pkg = null;
if (interfacePkg != null){
pkg = interfacePkg;
} else {
pkg = name.substring(0, index);
}
String clsName = name.substring(index + 1);
// FIXME: [rfeng] This is a workaround to avoid "Prohibited package name: java.lang.jaxws"
if (pkg.startsWith("java.") || pkg.startsWith("javax.")) {
pkg = "tuscany";
}
faultBeanName = (pkg + ".jaxws." + clsName + "Bean");
return faultBeanName;
}
public static QName getElementName(Class<? extends Throwable> exceptionClass, Operation operation) {
WebFault webFault = exceptionClass.getAnnotation(WebFault.class);
// TUSCANY-3283 - all generated classes (including exception) should go in the namespace
// of the interface not the namespace of the originating exception.
// consequently we need to create a matching package name for the schema
String namespace = null;
if (operation != null && operation.getInterface() instanceof JavaInterface){
namespace = ((JavaInterface)operation.getInterface()).getQName().getNamespaceURI();
}
String name = null;
if (webFault != null) {
namespace = webFault.targetNamespace();
name = webFault.name();
}
if (namespace == null) {
namespace = JavaInterfaceUtil.getNamespace(exceptionClass);
}
if (name == null) {
name = exceptionClass.getSimpleName();
}
return new QName(namespace, name);
}
public static Class<?> generateFaultBeanClass(Class<? extends Throwable> exceptionClass) {
FaultBeanGenerator generator = new FaultBeanGenerator();
GeneratedClassLoader cl = new GeneratedClassLoader(exceptionClass.getClassLoader());
return generator.generate(exceptionClass, cl, null);
}
}