From 132aa8a77685ec92bc90c03f987650d275a7b639 Mon Sep 17 00:00:00 2001 From: lresende Date: Mon, 30 Sep 2013 06:59:11 +0000 Subject: 2.0.1 RC1 release tag git-svn-id: http://svn.us.apache.org/repos/asf/tuscany@1527464 13f79535-47bb-0310-9956-ffa450edef68 --- .../interfacedef/java/jaxws/BaseBeanGenerator.java | 569 +++++++++++++++++++++ .../java/jaxws/CodeGenerationHelper.java | 280 ++++++++++ .../java/jaxws/FaultBeanGenerator.java | 202 ++++++++ .../java/jaxws/GeneratedClassLoader.java | 70 +++ .../java/jaxws/GeneratedDataTypeImpl.java | 150 ++++++ .../java/jaxws/JAXWSAsyncInterfaceProcessor.java | 293 +++++++++++ .../java/jaxws/JAXWSFaultExceptionMapper.java | 404 +++++++++++++++ .../java/jaxws/JAXWSJavaInterfaceProcessor.java | 448 ++++++++++++++++ .../sca/interfacedef/java/jaxws/JAXWSUtils.java | 104 ++++ .../java/jaxws/WebServiceInterfaceProcessor.java | 49 ++ .../java/jaxws/WrapperBeanGenerator.java | 270 ++++++++++ 11 files changed, 2839 insertions(+) create mode 100644 sca-java-2.x/tags/2.0.1-RC1/modules/interface-java-jaxws/src/main/java/org/apache/tuscany/sca/interfacedef/java/jaxws/BaseBeanGenerator.java create mode 100644 sca-java-2.x/tags/2.0.1-RC1/modules/interface-java-jaxws/src/main/java/org/apache/tuscany/sca/interfacedef/java/jaxws/CodeGenerationHelper.java create mode 100644 sca-java-2.x/tags/2.0.1-RC1/modules/interface-java-jaxws/src/main/java/org/apache/tuscany/sca/interfacedef/java/jaxws/FaultBeanGenerator.java create mode 100644 sca-java-2.x/tags/2.0.1-RC1/modules/interface-java-jaxws/src/main/java/org/apache/tuscany/sca/interfacedef/java/jaxws/GeneratedClassLoader.java create mode 100644 sca-java-2.x/tags/2.0.1-RC1/modules/interface-java-jaxws/src/main/java/org/apache/tuscany/sca/interfacedef/java/jaxws/GeneratedDataTypeImpl.java create mode 100644 sca-java-2.x/tags/2.0.1-RC1/modules/interface-java-jaxws/src/main/java/org/apache/tuscany/sca/interfacedef/java/jaxws/JAXWSAsyncInterfaceProcessor.java create mode 100644 sca-java-2.x/tags/2.0.1-RC1/modules/interface-java-jaxws/src/main/java/org/apache/tuscany/sca/interfacedef/java/jaxws/JAXWSFaultExceptionMapper.java create mode 100644 sca-java-2.x/tags/2.0.1-RC1/modules/interface-java-jaxws/src/main/java/org/apache/tuscany/sca/interfacedef/java/jaxws/JAXWSJavaInterfaceProcessor.java create mode 100644 sca-java-2.x/tags/2.0.1-RC1/modules/interface-java-jaxws/src/main/java/org/apache/tuscany/sca/interfacedef/java/jaxws/JAXWSUtils.java create mode 100644 sca-java-2.x/tags/2.0.1-RC1/modules/interface-java-jaxws/src/main/java/org/apache/tuscany/sca/interfacedef/java/jaxws/WebServiceInterfaceProcessor.java create mode 100644 sca-java-2.x/tags/2.0.1-RC1/modules/interface-java-jaxws/src/main/java/org/apache/tuscany/sca/interfacedef/java/jaxws/WrapperBeanGenerator.java (limited to 'sca-java-2.x/tags/2.0.1-RC1/modules/interface-java-jaxws/src/main/java/org/apache/tuscany') diff --git a/sca-java-2.x/tags/2.0.1-RC1/modules/interface-java-jaxws/src/main/java/org/apache/tuscany/sca/interfacedef/java/jaxws/BaseBeanGenerator.java b/sca-java-2.x/tags/2.0.1-RC1/modules/interface-java-jaxws/src/main/java/org/apache/tuscany/sca/interfacedef/java/jaxws/BaseBeanGenerator.java new file mode 100644 index 0000000000..923d13cd5e --- /dev/null +++ b/sca-java-2.x/tags/2.0.1-RC1/modules/interface-java-jaxws/src/main/java/org/apache/tuscany/sca/interfacedef/java/jaxws/BaseBeanGenerator.java @@ -0,0 +1,569 @@ +/* + * 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.lang.annotation.Annotation; +import java.lang.ref.WeakReference; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.WeakHashMap; + +import javax.xml.bind.annotation.XmlAttachmentRef; +import javax.xml.bind.annotation.XmlList; +import javax.xml.bind.annotation.XmlMimeType; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; +import javax.xml.ws.Holder; + +import org.apache.tuscany.sca.databinding.jaxb.XMLAdapterExtensionPoint; +import org.objectweb.asm.AnnotationVisitor; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.FieldVisitor; +import org.objectweb.asm.Label; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; + +public abstract class BaseBeanGenerator implements Opcodes { + private static final Map COLLECTION_CLASSES = new HashMap(); + + static { + COLLECTION_CLASSES.put("Ljava/util/Collection;", "java/util/ArrayList"); + COLLECTION_CLASSES.put("Ljava/util/List;", "java/util/ArrayList"); + COLLECTION_CLASSES.put("Ljava/util/Set;", "java/util/HashSet"); + COLLECTION_CLASSES.put("Ljava/util/Queue;", "java/util/LinkedList"); + } + private final static Class[] KNOWN_JAXB_ANNOTATIONS = + {XmlAttachmentRef.class, + XmlMimeType.class, + XmlJavaTypeAdapter.class, + XmlList.class}; + + private static final Map JAVA_KEYWORDS = new HashMap(); + + static { + JAVA_KEYWORDS.put("abstract", "_abstract"); + JAVA_KEYWORDS.put("assert", "_assert"); + JAVA_KEYWORDS.put("boolean", "_boolean"); + JAVA_KEYWORDS.put("break", "_break"); + JAVA_KEYWORDS.put("byte", "_byte"); + JAVA_KEYWORDS.put("case", "_case"); + JAVA_KEYWORDS.put("catch", "_catch"); + JAVA_KEYWORDS.put("char", "_char"); + JAVA_KEYWORDS.put("class", "_class"); + JAVA_KEYWORDS.put("const", "_const"); + JAVA_KEYWORDS.put("continue", "_continue"); + JAVA_KEYWORDS.put("default", "_default"); + JAVA_KEYWORDS.put("do", "_do"); + JAVA_KEYWORDS.put("double", "_double"); + JAVA_KEYWORDS.put("else", "_else"); + JAVA_KEYWORDS.put("extends", "_extends"); + JAVA_KEYWORDS.put("false", "_false"); + JAVA_KEYWORDS.put("final", "_final"); + JAVA_KEYWORDS.put("finally", "_finally"); + JAVA_KEYWORDS.put("float", "_float"); + JAVA_KEYWORDS.put("for", "_for"); + JAVA_KEYWORDS.put("goto", "_goto"); + JAVA_KEYWORDS.put("if", "_if"); + JAVA_KEYWORDS.put("implements", "_implements"); + JAVA_KEYWORDS.put("import", "_import"); + JAVA_KEYWORDS.put("instanceof", "_instanceof"); + JAVA_KEYWORDS.put("int", "_int"); + JAVA_KEYWORDS.put("interface", "_interface"); + JAVA_KEYWORDS.put("long", "_long"); + JAVA_KEYWORDS.put("native", "_native"); + JAVA_KEYWORDS.put("new", "_new"); + JAVA_KEYWORDS.put("null", "_null"); + JAVA_KEYWORDS.put("package", "_package"); + JAVA_KEYWORDS.put("private", "_private"); + JAVA_KEYWORDS.put("protected", "_protected"); + JAVA_KEYWORDS.put("public", "_public"); + JAVA_KEYWORDS.put("return", "_return"); + JAVA_KEYWORDS.put("short", "_short"); + JAVA_KEYWORDS.put("static", "_static"); + JAVA_KEYWORDS.put("strictfp", "_strictfp"); + JAVA_KEYWORDS.put("super", "_super"); + JAVA_KEYWORDS.put("switch", "_switch"); + JAVA_KEYWORDS.put("synchronized", "_synchronized"); + JAVA_KEYWORDS.put("this", "_this"); + JAVA_KEYWORDS.put("throw", "_throw"); + JAVA_KEYWORDS.put("throws", "_throws"); + JAVA_KEYWORDS.put("transient", "_transient"); + JAVA_KEYWORDS.put("true", "_true"); + JAVA_KEYWORDS.put("try", "_try"); + JAVA_KEYWORDS.put("void", "_void"); + JAVA_KEYWORDS.put("volatile", "_volatile"); + JAVA_KEYWORDS.put("while", "_while"); + JAVA_KEYWORDS.put("enum", "_enum"); + } + + protected static final Map>> generatedClasses = + Collections.synchronizedMap(new WeakHashMap>>()); + + protected XMLAdapterExtensionPoint xmlAdapters; + + public byte[] defineClass(ClassWriter cw, + String classDescriptor, + String classSignature, + String namespace, + String name, + BeanProperty[] properties) { + // Declare the class + declareClass(cw, classDescriptor); + + // Compute the propOrder + String[] propOrder = null; + if (properties != null && properties.length > 0) { + int size = properties.length; + propOrder = new String[size]; + for (int i = 0; i < size; i++) { + propOrder[i] = getFieldName(properties[i].getName()); + } + } + // Annotate the class + annotateClass(cw, name, namespace, propOrder); + + // Declare the default constructor + declareConstructor(cw, classSignature); + if (properties != null) { + for (BeanProperty p : properties) { + boolean isElement = p.isElement() && (!Map.class.isAssignableFrom(p.getType())); + String xmlAdapterClassSignature = null; + if (xmlAdapters != null) { + Class adapterClass = xmlAdapters.getAdapter(p.getType()); + if (adapterClass != null) { + xmlAdapterClassSignature = CodeGenerationHelper.getSignature(adapterClass); + } + } + declareProperty(cw, classDescriptor, classSignature, p.getName(), p.getSignature(), p + .getGenericSignature(), isElement, p.isNillable(), xmlAdapterClassSignature, p.getJaxbAnnotaions()); + } + } + + // Close the generation + cw.visitEnd(); + return cw.toByteArray(); + } + + protected static boolean isHolder(java.lang.reflect.Type type) { + if (type instanceof ParameterizedType) { + Class cls = CodeGenerationHelper.getErasure(type); + return cls == Holder.class; + } + return false; + } + + protected static java.lang.reflect.Type getHolderValueType(java.lang.reflect.Type paramType) { + if (paramType instanceof ParameterizedType) { + ParameterizedType p = (ParameterizedType)paramType; + Class cls = CodeGenerationHelper.getErasure(p); + if (cls == Holder.class) { + return p.getActualTypeArguments()[0]; + } + } + return paramType; + } + + protected void declareProperty(ClassWriter cw, + String classDescriptor, + String classSignature, + String propName, + String propClassSignature, + String propTypeSignature, + boolean isElement, + boolean isNillable, + String xmlAdapterClassSignature, + List jaxbAnnotations) { + if (propClassSignature.equals(propTypeSignature)) { + propTypeSignature = null; + } + declareField(cw, + propName, + propClassSignature, + propTypeSignature, + isElement, + isNillable, + xmlAdapterClassSignature, + jaxbAnnotations); + decalreGetter(cw, classDescriptor, classSignature, propName, propClassSignature, propTypeSignature); + declareSetter(cw, classDescriptor, classSignature, propName, propClassSignature, propTypeSignature); + } + + protected String getFieldName(String propName) { + String name = JAVA_KEYWORDS.get(propName); + return name != null ? name : propName; + } + + protected void declareField(ClassWriter cw, + String propName, + String propClassSignature, + String propTypeSignature, + boolean isElement, + boolean isNillable, + String xmlAdapterClassSignature, + List jaxbAnnotations) { + FieldVisitor fv; + AnnotationVisitor av0; + fv = cw.visitField(ACC_PROTECTED, getFieldName(propName), propClassSignature, propTypeSignature, null); + + // For Map property, we cannot have the XmlElement annotation + if (isElement && xmlAdapterClassSignature == null) { + av0 = fv.visitAnnotation("Ljavax/xml/bind/annotation/XmlElement;", true); + av0.visit("name", propName); + av0.visit("namespace", ""); + // TUSCANY-3283 - force not nillable if it isn't + if (isNillable) { + av0.visit("nillable", Boolean.TRUE); + } else { + av0.visit("nillable", Boolean.FALSE); + } + // FIXME: + // av0.visit("required", Boolean.FALSE); + av0.visitEnd(); + } + + if (xmlAdapterClassSignature != null) { + av0 = fv.visitAnnotation("Ljavax/xml/bind/annotation/XmlAnyElement;", true); + av0.visit("lax", Boolean.TRUE); + av0.visitEnd(); + av0 = fv.visitAnnotation("Ljavax/xml/bind/annotation/adapters/XmlJavaTypeAdapter;", true); + av0.visit("value", org.objectweb.asm.Type.getType(xmlAdapterClassSignature)); + av0.visitEnd(); + } + + for (Annotation ann : jaxbAnnotations) { + if (ann instanceof XmlMimeType) { + AnnotationVisitor mime = fv.visitAnnotation("Ljavax/xml/bind/annotation/XmlMimeType;", true); + mime.visit("value", ((XmlMimeType)ann).value()); + mime.visitEnd(); + } else if (ann instanceof XmlJavaTypeAdapter) { + AnnotationVisitor ada = fv.visitAnnotation("Ljavax/xml/bind/annotation/adapters/XmlJavaTypeAdapter;", true); + ada.visit("value", org.objectweb.asm.Type.getType(((XmlJavaTypeAdapter)ann).value())); + ada.visit("type", org.objectweb.asm.Type.getType(((XmlJavaTypeAdapter)ann).type())); + ada.visitEnd(); + } else if (ann instanceof XmlAttachmentRef) { + AnnotationVisitor att = fv.visitAnnotation("Ljavax/xml/bind/annotation/XmlAttachmentRef;", true); + att.visitEnd(); + } else if (ann instanceof XmlList) { + AnnotationVisitor list = fv.visitAnnotation("Ljavax/xml/bind/annotation/XmlList;", true); + list.visitEnd(); + } + } + + fv.visitEnd(); + } + + protected void declareSetter(ClassWriter cw, + String classDescriptor, + String classSignature, + String propName, + String propClassSignature, + String propTypeSignature) { + if ("Ljava/util/List;".equals(propClassSignature)) { + return; + } + MethodVisitor mv = + cw.visitMethod(ACC_PUBLIC, + "set" + capitalize(propName), + "(" + propClassSignature + ")V", + propTypeSignature == null ? null : "(" + propTypeSignature + ")V", + null); + mv.visitCode(); + Label l0 = new Label(); + mv.visitLabel(l0); + // mv.visitLineNumber(57, l0); + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(CodeGenerationHelper.getLoadOPCode(propClassSignature), 1); + mv.visitFieldInsn(PUTFIELD, classDescriptor, getFieldName(propName), propClassSignature); + Label l1 = new Label(); + mv.visitLabel(l1); + // mv.visitLineNumber(58, l1); + mv.visitInsn(RETURN); + Label l2 = new Label(); + mv.visitLabel(l2); + mv.visitLocalVariable("this", classSignature, null, l0, l2, 0); + mv.visitLocalVariable(getFieldName(propName), propClassSignature, propTypeSignature, l0, l2, 1); + mv.visitMaxs(3, 3); + mv.visitEnd(); + + } + + protected void decalreGetter(ClassWriter cw, + String classDescriptor, + String classSignature, + String propName, + String propClassSignature, + String propTypeSignature) { + String collectionImplClass = COLLECTION_CLASSES.get(propClassSignature); + if (collectionImplClass != null) { + decalreCollectionGetter(cw, + classDescriptor, + classSignature, + propName, + propClassSignature, + propTypeSignature, + collectionImplClass); + return; + } + + String getterName = ("Z".equals(propClassSignature) ? "is" : "get") + capitalize(propName); + MethodVisitor mv = + cw.visitMethod(ACC_PUBLIC, getterName, "()" + propClassSignature, propTypeSignature == null ? null + : "()" + propTypeSignature, null); + mv.visitCode(); + Label l0 = new Label(); + mv.visitLabel(l0); + // mv.visitLineNumber(48, l0); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, classDescriptor, getFieldName(propName), propClassSignature); + mv.visitInsn(CodeGenerationHelper.getReturnOPCode(propClassSignature)); + Label l1 = new Label(); + mv.visitLabel(l1); + mv.visitLocalVariable("this", classSignature, null, l0, l1, 0); + mv.visitMaxs(2, 1); + mv.visitEnd(); + } + + protected void decalreCollectionGetter(ClassWriter cw, + String classDescriptor, + String classSignature, + String propName, + String propClassSignature, + String propTypeSignature, + String collectionImplClass) { + String getterName = "get" + capitalize(propName); + String fieldName = getFieldName(propName); + MethodVisitor mv = + cw.visitMethod(ACC_PUBLIC, getterName, "()" + propClassSignature, propTypeSignature == null ? null + : "()" + propTypeSignature, null); + mv.visitCode(); + Label l0 = new Label(); + mv.visitLabel(l0); + mv.visitLineNumber(63, l0); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, classDescriptor, fieldName, propClassSignature); + Label l1 = new Label(); + mv.visitJumpInsn(IFNONNULL, l1); + Label l2 = new Label(); + mv.visitLabel(l2); + mv.visitLineNumber(64, l2); + mv.visitVarInsn(ALOAD, 0); + mv.visitTypeInsn(NEW, collectionImplClass); + mv.visitInsn(DUP); + mv.visitMethodInsn(INVOKESPECIAL, collectionImplClass, "", "()V"); + mv.visitFieldInsn(PUTFIELD, classDescriptor, fieldName, propClassSignature); + mv.visitLabel(l1); + mv.visitLineNumber(66, l1); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, classDescriptor, fieldName, propClassSignature); + mv.visitInsn(ARETURN); + Label l3 = new Label(); + mv.visitLabel(l3); + mv.visitLocalVariable("this", classSignature, null, l0, l3, 0); + mv.visitMaxs(3, 1); + mv.visitEnd(); + } + + protected static String capitalize(String name) { + if (name == null || name.length() == 0) { + return name; + } else { + return Character.toUpperCase(name.charAt(0)) + name.substring(1); + } + } + + protected void declareConstructor(ClassWriter cw, String classSignature) { + MethodVisitor mv; + mv = cw.visitMethod(ACC_PUBLIC, "", "()V", null, null); + mv.visitCode(); + Label l0 = new Label(); + mv.visitLabel(l0); + // mv.visitLineNumber(37, l0); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V"); + mv.visitInsn(RETURN); + Label l1 = new Label(); + mv.visitLabel(l1); + mv.visitLocalVariable("this", classSignature, null, l0, l1, 0); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + + protected void declareClass(ClassWriter cw, String classDescriptor) { + cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER, classDescriptor, null, "java/lang/Object", null); + } + + protected void annotateClass(ClassWriter cw, String name, String namespace, String[] propOrder) { + AnnotationVisitor av0; + // @XmlRootElement + av0 = cw.visitAnnotation("Ljavax/xml/bind/annotation/XmlRootElement;", true); + av0.visit("name", name); + av0.visit("namespace", namespace); + av0.visitEnd(); + // @XmlAccessorType + av0 = cw.visitAnnotation("Ljavax/xml/bind/annotation/XmlAccessorType;", true); + av0.visitEnum("value", "Ljavax/xml/bind/annotation/XmlAccessType;", "FIELD"); + av0.visitEnd(); + // @XmlType + av0 = cw.visitAnnotation("Ljavax/xml/bind/annotation/XmlType;", true); + av0.visit("name", name); + av0.visit("namespace", namespace); + if (propOrder != null) { + AnnotationVisitor pv = av0.visitArray("propOrder"); + for (String p : propOrder) { + pv.visit(null, p); + } + pv.visitEnd(); + } + av0.visitEnd(); + } + + public Class generate(String classDescriptor, + String classSignature, + String namespace, + String name, + BeanProperty[] properties, + GeneratedClassLoader classLoader) { + + // The reflection code here allows for toleration of older versions of ASM. + ClassWriter cw; + try { + Constructor 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 c = ClassWriter.class.getConstructor(new Class[] {boolean.class}); + cw = c.newInstance(true); + } catch ( Exception ex2 ) { + throw new IllegalArgumentException(ex2); + } + + } + + byte[] byteCode = defineClass(cw, classDescriptor, classSignature, namespace, name, properties); + String className = classDescriptor.replace('/', '.'); + Class generated = classLoader.getGeneratedClass(className, byteCode); + return generated; + } + + public static class BeanProperty { + private Class type; + private String namespace; + private String name; + private String signature; + private String genericSignature; + private List jaxbAnnotaions = new ArrayList(); + private boolean element; + private boolean nillable; + + public BeanProperty(String namespace, String name, Class javaClass, Type type, boolean isElement) { + super(); + this.namespace = namespace; + this.name = name; + this.signature = CodeGenerationHelper.getJAXWSSignature(javaClass); + this.type = javaClass; + this.genericSignature = CodeGenerationHelper.getJAXWSSignature(type); + this.element = isElement; + // FIXME: How to test nillable? + // this.nillable = (type instanceof GenericArrayType) || Collection.class.isAssignableFrom(javaClass) || javaClass.isArray(); + // TUSCANY-2389: Set the nillable consistent with what wsgen produces + this.nillable = javaClass.isArray(); + } + + public String getName() { + return name; + } + + public String getSignature() { + return signature; + } + + public String getGenericSignature() { + return genericSignature; + } + + public Class getType() { + return type; + } + + public List getJaxbAnnotaions() { + return jaxbAnnotaions; + } + + public String getNamespace() { + return namespace; + } + + public boolean isElement() { + return element; + } + + public boolean isNillable() { + return nillable; + } + } + + public XMLAdapterExtensionPoint getXmlAdapters() { + return xmlAdapters; + } + + public void setXmlAdapters(XMLAdapterExtensionPoint xmlAdapters) { + this.xmlAdapters = xmlAdapters; + } + + protected static T findAnnotation(Annotation[] anns, Class annotationClass) { + for (Annotation a : anns) { + if (a.annotationType() == annotationClass) { + return annotationClass.cast(a); + } + } + return null; + } + + protected static List findJAXBAnnotations(Annotation[] anns) { + List jaxbAnnotation = new ArrayList(); + for (Class c : KNOWN_JAXB_ANNOTATIONS) { + Annotation a = findAnnotation(anns, c); + if (a != null) { + jaxbAnnotation.add(a); + } + } + return jaxbAnnotation; + } + + protected List findJAXBAnnotations(Method method) { + List anns = new ArrayList(); + for (Class c : KNOWN_JAXB_ANNOTATIONS) { + Annotation ann = method.getAnnotation(c); + if (ann != null) { + anns.add(ann); + } + } + return anns; + } + +} diff --git a/sca-java-2.x/tags/2.0.1-RC1/modules/interface-java-jaxws/src/main/java/org/apache/tuscany/sca/interfacedef/java/jaxws/CodeGenerationHelper.java b/sca-java-2.x/tags/2.0.1-RC1/modules/interface-java-jaxws/src/main/java/org/apache/tuscany/sca/interfacedef/java/jaxws/CodeGenerationHelper.java new file mode 100644 index 0000000000..b05715b54e --- /dev/null +++ b/sca-java-2.x/tags/2.0.1-RC1/modules/interface-java-jaxws/src/main/java/org/apache/tuscany/sca/interfacedef/java/jaxws/CodeGenerationHelper.java @@ -0,0 +1,280 @@ +/* + * 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.lang.reflect.Array; +import java.lang.reflect.GenericArrayType; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.lang.reflect.WildcardType; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.objectweb.asm.Opcodes; + +/** + * @version $Rev$ $Date$ + */ +public class CodeGenerationHelper { + /** + * @param type + * @return + */ + public static Class getErasure(Type type) { + if (type instanceof Class) { + return (Class)type; + } else if (type instanceof GenericArrayType) { + GenericArrayType arrayType = (GenericArrayType)type; + Class componentType = getErasure(arrayType.getGenericComponentType()); + return Array.newInstance(componentType, 0).getClass(); + } else if (type instanceof ParameterizedType) { + ParameterizedType pType = (ParameterizedType)type; + return getErasure(pType.getRawType()); + } else if (type instanceof WildcardType) { + WildcardType wType = (WildcardType)type; + Type[] types = wType.getUpperBounds(); + if (types.length == 0) { + return Object.class; + } + return getErasure(types[0]); + } else if (type instanceof TypeVariable) { + TypeVariable var = (TypeVariable)type; + Type[] types = var.getBounds(); + if (types.length == 0) { + return Object.class; + } + return getErasure(types[0]); + } + return null; + } + + /** + * @param type + * @return + */ + public static String getJAXWSSignature(Type type) { + Class cls = getErasure(type); + if (Collection.class.isAssignableFrom(cls) && (type instanceof ParameterizedType)) { + ParameterizedType pType = (ParameterizedType)type; + Type p = pType.getActualTypeArguments()[0]; + StringBuffer sb = new StringBuffer(); + sb.append(getSignature(cls)); + sb.deleteCharAt(sb.length() - 1); // Remove ; + sb.append('<').append(getSignature(getErasure(p))).append(">;"); + return sb.toString(); + } else if (Map.class.isAssignableFrom(cls) && (type instanceof ParameterizedType)) { + ParameterizedType pType = (ParameterizedType)type; + Type key = pType.getActualTypeArguments()[0]; + Type value = pType.getActualTypeArguments()[1]; + StringBuffer sb = new StringBuffer(); + sb.append(getSignature(cls)); + sb.deleteCharAt(sb.length() - 1); // Remove ; + sb.append('<').append(getSignature(getErasure(key))).append(getSignature(getErasure(value))).append(">;"); + return sb.toString(); + } else { + return getSignature(cls); + } + } + + /** + * @param type + * @return + */ + public static String getSignature(Type type) { + if (!(type instanceof Class)) { + if (type instanceof ParameterizedType) { + ParameterizedType pType = (ParameterizedType)type; + StringBuffer sb = new StringBuffer(); + String rawType = getSignature(pType.getRawType()); + sb.append(rawType.substring(0, rawType.length() - 1)); + sb.append('<'); + for (Type t : pType.getActualTypeArguments()) { + String argType = getSignature(t); + sb.append(argType); + } + sb.append('>'); + sb.append(rawType.substring(rawType.length() - 1)); + return sb.toString(); + } + if (type instanceof TypeVariable) { + return "T" + ((TypeVariable)type).getName() + ";"; + } + if (type instanceof GenericArrayType) { + GenericArrayType arrayType = (GenericArrayType)type; + return "[" + getSignature(arrayType.getGenericComponentType()); + } + if (type instanceof WildcardType) { + WildcardType wType = (WildcardType)type; + Type[] types = wType.getUpperBounds(); + StringBuffer sb = new StringBuffer(); + if (types.length == 0 || !(types.length == 1 && types[0] == Object.class)) { + sb.append('+'); + for (Type t : types) { + sb.append(getSignature(t)); + } + } + types = wType.getLowerBounds(); + if (types.length != 0) { + sb.append('-'); + for (Type t : wType.getLowerBounds()) { + sb.append(getSignature(t)); + } + } + if (sb.length() == 0) { + return "*"; + } + return sb.toString(); + } + } + Class cls = (Class)type; + return org.objectweb.asm.Type.getDescriptor(cls); + } + + /** + * Get the actual type arguments a child class has used to extend a generic base class. + * + * @param baseClass the base class + * @param childClass the child class + * @return a list of the raw classes for the actual type arguments. + */ + public static List> resovleTypeArguments(Class baseClass, Class childClass) { + Map resolvedTypes = new HashMap(); + Type type = childClass; + // start walking up the inheritance hierarchy until we hit baseClass + while (!getErasure(type).equals(baseClass)) { + if (type instanceof Class) { + // there is no useful information for us in raw types, so just keep going. + type = ((Class)type).getGenericSuperclass(); + } else { + ParameterizedType parameterizedType = (ParameterizedType)type; + Class rawType = getErasure(parameterizedType.getRawType()); + + Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); + TypeVariable[] typeParameters = rawType.getTypeParameters(); + for (int i = 0; i < actualTypeArguments.length; i++) { + resolvedTypes.put(typeParameters[i], actualTypeArguments[i]); + } + + if (!rawType.equals(baseClass)) { + type = rawType.getGenericSuperclass(); + } + } + } + + // finally, for each actual type argument provided to baseClass, determine (if possible) + // the raw class for that type argument. + Type[] actualTypeArguments; + if (type instanceof Class) { + actualTypeArguments = ((Class)type).getTypeParameters(); + } else { + actualTypeArguments = ((ParameterizedType)type).getActualTypeArguments(); + } + List> typeArgumentsAsClasses = new ArrayList>(); + // resolve types by chasing down type variables. + for (Type baseType : actualTypeArguments) { + while (resolvedTypes.containsKey(baseType)) { + baseType = resolvedTypes.get(baseType); + } + typeArgumentsAsClasses.add(getErasure(baseType)); + } + return typeArgumentsAsClasses; + } + + /* + signatures.put(boolean.class, "Z"); + signatures.put(byte.class, "B"); + signatures.put(char.class, "C"); + signatures.put(short.class, "S"); + signatures.put(int.class, "I"); + signatures.put(long.class, "J"); + signatures.put(float.class, "F"); + signatures.put(double.class, "D"); + */ + public static int getLoadOPCode(String signature) { + if ("Z".equals(signature) || "B".equals(signature) + || "C".equals(signature) + || "S".equals(signature) + || "I".equals(signature)) { + return Opcodes.ILOAD; + } + + if ("J".equals(signature)) { + return Opcodes.LLOAD; + } + + if ("F".equals(signature)) { + return Opcodes.FLOAD; + } + + if ("D".equals(signature)) { + return Opcodes.DLOAD; + } + + return Opcodes.ALOAD; + + } + + public static int getReturnOPCode(String signature) { + if ("Z".equals(signature) || "B".equals(signature) + || "C".equals(signature) + || "S".equals(signature) + || "I".equals(signature)) { + return Opcodes.IRETURN; + } + + if ("J".equals(signature)) { + return Opcodes.LRETURN; + } + + if ("F".equals(signature)) { + return Opcodes.FRETURN; + } + + if ("D".equals(signature)) { + return Opcodes.DRETURN; + } + if ("V".equals(signature)) { + return Opcodes.RETURN; + } + + return Opcodes.ARETURN; + + } + + /** + * Get the package prefix for generated JAXWS artifacts + * @param cls + * @return + */ + public static String getPackagePrefix(Class cls) { + String name = cls.getName(); + int index = name.lastIndexOf('.'); + if (index == -1) { + return "jaxws."; + } else { + return name.substring(0, index) + ".jaxws."; + } + } + +} diff --git a/sca-java-2.x/tags/2.0.1-RC1/modules/interface-java-jaxws/src/main/java/org/apache/tuscany/sca/interfacedef/java/jaxws/FaultBeanGenerator.java b/sca-java-2.x/tags/2.0.1-RC1/modules/interface-java-jaxws/src/main/java/org/apache/tuscany/sca/interfacedef/java/jaxws/FaultBeanGenerator.java new file mode 100644 index 0000000000..28fd2afe73 --- /dev/null +++ b/sca-java-2.x/tags/2.0.1-RC1/modules/interface-java-jaxws/src/main/java/org/apache/tuscany/sca/interfacedef/java/jaxws/FaultBeanGenerator.java @@ -0,0 +1,202 @@ +/* + * 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 exceptionClass) { + BeanInfo beanInfo; + try { + beanInfo = Introspector.getBeanInfo(exceptionClass); + } catch (IntrospectionException e) { + throw new IllegalArgumentException(e); + } + List props = new ArrayList(); + 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() { + public int compare(BeanProperty o1, BeanProperty o2) { + return o1.getName().compareTo(o2.getName()); + } + }); + return props.toArray(new BeanProperty[0]); + } + + public byte[] generate(Class exceptionClass, Operation operation) { + // The reflection code here allows for toleration of older versions of ASM. + ClassWriter cw; + try { + Constructor 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 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 exceptionClass, GeneratedClassLoader cl, Operation operation) { + synchronized (exceptionClass) { + QName element = getElementName(exceptionClass, operation); + WeakReference> 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>(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 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 exceptionClass) { + FaultBeanGenerator generator = new FaultBeanGenerator(); + GeneratedClassLoader cl = new GeneratedClassLoader(exceptionClass.getClassLoader()); + return generator.generate(exceptionClass, cl, null); + } +} diff --git a/sca-java-2.x/tags/2.0.1-RC1/modules/interface-java-jaxws/src/main/java/org/apache/tuscany/sca/interfacedef/java/jaxws/GeneratedClassLoader.java b/sca-java-2.x/tags/2.0.1-RC1/modules/interface-java-jaxws/src/main/java/org/apache/tuscany/sca/interfacedef/java/jaxws/GeneratedClassLoader.java new file mode 100644 index 0000000000..3f035ce585 --- /dev/null +++ b/sca-java-2.x/tags/2.0.1-RC1/modules/interface-java-jaxws/src/main/java/org/apache/tuscany/sca/interfacedef/java/jaxws/GeneratedClassLoader.java @@ -0,0 +1,70 @@ +/* + * 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.net.URL; +import java.net.URLClassLoader; +import java.util.HashMap; +import java.util.Map; + +public class GeneratedClassLoader extends URLClassLoader { + private class GeneratedClass { + private String className; + private byte[] byteCode; + private Class cls; + + public GeneratedClass(String className, byte[] byteCode) { + super(); + this.className = className; + this.byteCode = byteCode; + } + + public synchronized Class getGeneratedClass() { + if (cls == null) { + cls = defineClass(className, byteCode, 0, byteCode.length); + } + return cls; + } + } + + private Map generatedClasses = new HashMap(); + + public GeneratedClassLoader(ClassLoader parentLoader) { + super(new URL[0], parentLoader); + } + + @Override + protected Class findClass(String className) throws ClassNotFoundException { + GeneratedClass cls = generatedClasses.get(className); + if (cls != null) { + return cls.getGeneratedClass(); + } + return super.findClass(className); + } + + public synchronized Class getGeneratedClass(String className, byte[] byteCode) { + GeneratedClass cls = generatedClasses.get(className); + if (cls == null) { + cls = new GeneratedClass(className, byteCode); + generatedClasses.put(className, cls); + } + return cls.getGeneratedClass(); + } +} diff --git a/sca-java-2.x/tags/2.0.1-RC1/modules/interface-java-jaxws/src/main/java/org/apache/tuscany/sca/interfacedef/java/jaxws/GeneratedDataTypeImpl.java b/sca-java-2.x/tags/2.0.1-RC1/modules/interface-java-jaxws/src/main/java/org/apache/tuscany/sca/interfacedef/java/jaxws/GeneratedDataTypeImpl.java new file mode 100644 index 0000000000..2fbf374ac9 --- /dev/null +++ b/sca-java-2.x/tags/2.0.1-RC1/modules/interface-java-jaxws/src/main/java/org/apache/tuscany/sca/interfacedef/java/jaxws/GeneratedDataTypeImpl.java @@ -0,0 +1,150 @@ +/* + * 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.lang.reflect.Method; +import java.lang.reflect.Type; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import javax.xml.namespace.QName; + +import org.apache.tuscany.sca.databinding.jaxb.JAXBDataBinding; +import org.apache.tuscany.sca.databinding.jaxb.XMLAdapterExtensionPoint; +import org.apache.tuscany.sca.interfacedef.DataType; +import org.apache.tuscany.sca.interfacedef.Operation; +import org.apache.tuscany.sca.interfacedef.java.JavaInterface; +import org.apache.tuscany.sca.interfacedef.util.XMLType; + +/** + * A special data type that generate the class on demand + * @version $Rev$ $Date$ + */ +public class GeneratedDataTypeImpl implements DataType { + private XMLAdapterExtensionPoint xmlAdapters; + + private Class physical; + private XMLType logical; + + private Map, Object> metaDataMap; + private Method method; + private String wrapperClassName; + private String wrapperNamespace; + private String wrapperName; + private boolean request; + private GeneratedClassLoader classLoader; + private Operation operation; + + private Class exceptionClass; + + public GeneratedDataTypeImpl(XMLAdapterExtensionPoint xmlAdapters, + Class exceptionClass, + GeneratedClassLoader cl, + Operation operation) { + super(); + this.exceptionClass = exceptionClass; + this.classLoader = cl; + QName name = FaultBeanGenerator.getElementName(exceptionClass, operation); + this.logical = new XMLType(name, name); + this.xmlAdapters = xmlAdapters; + this.operation = operation; + } + + public GeneratedDataTypeImpl(XMLAdapterExtensionPoint xmlAdapters, + Method m, + String wrapperClassName, + String wrapperNamespace, + String wrapperName, + boolean request, + GeneratedClassLoader cl) { + super(); + this.method = m; + this.wrapperClassName = wrapperClassName; + this.wrapperNamespace = wrapperNamespace; + this.wrapperName = wrapperName; + this.classLoader = cl; + this.request = request; + QName name = new QName(wrapperNamespace, wrapperName); + this.logical = new XMLType(name, name); + this.xmlAdapters = xmlAdapters; + } + + public String getDataBinding() { + return JAXBDataBinding.NAME; + } + + public Type getGenericType() { + return getPhysical(); + } + + public XMLType getLogical() { + return logical; + } + + public synchronized Class getPhysical() { + if (physical == null) { + if (method != null) { + WrapperBeanGenerator generator = new WrapperBeanGenerator(); + generator.setXmlAdapters(xmlAdapters); + physical = + request ? generator.generateRequestWrapper(method, wrapperClassName, wrapperNamespace, wrapperName, classLoader) + : generator.generateResponseWrapper(method, wrapperClassName, wrapperNamespace, wrapperName, classLoader); + ; + } else if (exceptionClass != null) { + FaultBeanGenerator faultBeanGenerator = new FaultBeanGenerator(); + faultBeanGenerator.setXmlAdapters(xmlAdapters); + physical = faultBeanGenerator.generate(exceptionClass, classLoader, operation); + } + } + return physical; + } + + public void setDataBinding(String dataBinding) { + // NOP + } + + public void setGenericType(Type genericType) { + // NOP + } + + public void setLogical(XMLType logical) { + this.logical = logical; + } + + public void setPhysical(Class cls) { + // NOP + } + + @Override + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } + + public T getMetaData(Class type) { + return metaDataMap == null ? null : type.cast(metaDataMap.get(type)); + } + + public void setMetaData(Class type, T metaData) { + if (metaDataMap == null) { + metaDataMap = new ConcurrentHashMap, Object>(); + } + metaDataMap.put(type, metaData); + } +} diff --git a/sca-java-2.x/tags/2.0.1-RC1/modules/interface-java-jaxws/src/main/java/org/apache/tuscany/sca/interfacedef/java/jaxws/JAXWSAsyncInterfaceProcessor.java b/sca-java-2.x/tags/2.0.1-RC1/modules/interface-java-jaxws/src/main/java/org/apache/tuscany/sca/interfacedef/java/jaxws/JAXWSAsyncInterfaceProcessor.java new file mode 100644 index 0000000000..672c01a0b9 --- /dev/null +++ b/sca-java-2.x/tags/2.0.1-RC1/modules/interface-java-jaxws/src/main/java/org/apache/tuscany/sca/interfacedef/java/jaxws/JAXWSAsyncInterfaceProcessor.java @@ -0,0 +1,293 @@ +/* + * 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.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Future; + +import javax.xml.ws.AsyncHandler; +import javax.xml.ws.Response; + +import org.apache.tuscany.sca.core.ExtensionPointRegistry; +import org.apache.tuscany.sca.interfacedef.DataType; +import org.apache.tuscany.sca.interfacedef.InvalidInterfaceException; +import org.apache.tuscany.sca.interfacedef.Operation; +import org.apache.tuscany.sca.interfacedef.java.JavaInterface; +import org.apache.tuscany.sca.interfacedef.java.JavaOperation; +import org.apache.tuscany.sca.interfacedef.java.introspect.JavaInterfaceVisitor; + +public class JAXWSAsyncInterfaceProcessor implements JavaInterfaceVisitor { + private static String ASYNC = "Async"; + + public JAXWSAsyncInterfaceProcessor(ExtensionPointRegistry registry) { + } + + public void visitInterface(JavaInterface javaInterface) throws InvalidInterfaceException { + List validOperations = new ArrayList(); + List asyncOperations = new ArrayList(); + + validOperations.addAll(javaInterface.getOperations()); + for (Operation o : javaInterface.getOperations()) { + if (!o.getName().endsWith(ASYNC)) { + JavaOperation op = (JavaOperation)o; + if (op.getJavaMethod().getName().endsWith(ASYNC)) { + continue; + } + for (Operation asyncOp : getAsyncOperations(javaInterface.getOperations(), op)) { + if (isJAXWSAsyncPoolingOperation(op, asyncOp) || isJAXWSAsyncCallbackOperation(op, asyncOp)) { + validOperations.remove(asyncOp); + asyncOperations.add(asyncOp); + } + } + } + } + + javaInterface.getOperations().clear(); + javaInterface.getOperations().addAll(validOperations); + + javaInterface.getAttributes().put("JAXWS-ASYNC-OPERATIONS", asyncOperations); + } + + /** + * The additional client-side asynchronous polling and callback methods defined by JAX-WS are recognized in a Java interface as follows: + * For each method M in the interface, if another method P in the interface has + * + * a) a method name that is M's method name with the characters "Async" appended, and + * b) the same parameter signature as M, and + * c)a return type of Response where R is the return type of M + * + * @param operation + * @param asyncOperation + * @return + */ + private static boolean isJAXWSAsyncPoolingOperation(Operation operation, Operation asyncOperation) { + + if (asyncOperation.getOutputType().getLogical().size() == 0 || Response.class != asyncOperation.getOutputType().getLogical().get(0).getPhysical()) { + // The return type is not Response + return false; + } + + //the same parameter signature as M + List operationInputType = operation.getInputType().getLogical(); + List asyncOperationInputType = asyncOperation.getInputType().getLogical(); + int size = operationInputType.size(); + if (asyncOperationInputType.size() != size) { + return false; + } + for (int i = 0; i < size; i++) { + if (!isCompatible(operationInputType.get(i), asyncOperationInputType.get(i))) { + return false; + } + } + + //a return type of Response where R is the return type of M + DataType operationOutputType = null; + if (operation.getOutputType()!= null && operation.getOutputType().getLogical() != null && operation.getOutputType().getLogical().size() > 0) { + operationOutputType = operation.getOutputType().getLogical().get(0); + } + DataType asyncOperationOutputType = asyncOperation.getOutputType().getLogical().get(0); + + if (operationOutputType != null && asyncOperationOutputType != null) { + Class asyncReturnTypeClass = (Class)asyncOperationOutputType.getPhysical(); + if (asyncReturnTypeClass == Response.class) { + //now check the actual type of the Response with R + Class returnType = operationOutputType.getPhysical(); + Class asyncActualReturnTypeClass = Object.class; + if (asyncOperationOutputType.getGenericType() instanceof ParameterizedType) { + ParameterizedType asyncReturnType = (ParameterizedType)asyncOperationOutputType.getGenericType(); + asyncActualReturnTypeClass = (Class)asyncReturnType.getActualTypeArguments()[0]; + } + + if (operation.getOutputWrapper() != null) { + // The return type could be the wrapper type per JAX-WS spec + Class wrapperClass = operation.getOutputWrapper().getWrapperClass(); + if (wrapperClass == asyncActualReturnTypeClass) { + return true; + } + } + if (returnType == asyncActualReturnTypeClass || returnType.isPrimitive() + && primitiveAssignable(returnType, asyncActualReturnTypeClass)) { + return true; + } else { + return false; + } + } + } + + return true; + } + + /** + * For each method M in the interface, if another method C in the interface has + * a) a method name that is M's method name with the characters "Async" appended, and + * b) a parameter signature that is M's parameter signature with an additional + * final parameter of type AsyncHandler where R is the return type of M, and + * c) a return type of Future + * + * then C is a JAX-WS callback method that isn't part of the SCA interface contract. + * + * @param operation + * @param asyncOperation + * @return + */ + private static boolean isJAXWSAsyncCallbackOperation(Operation operation, Operation asyncOperation) { + + if (asyncOperation.getOutputType().getLogical().size() == 0 || Future.class != asyncOperation.getOutputType().getLogical().get(0).getPhysical()) { + // The return type is not Future + return false; + } + + //a parameter signature that is M's parameter signature + //with an additional final parameter of type AsyncHandler where R is the return type of M, and + List operationInputType = operation.getInputType().getLogical(); + List asyncOperationInputType = asyncOperation.getInputType().getLogical(); + int size = operationInputType.size(); + if (asyncOperationInputType.size() != size + 1) { + return false; + } + for (int i = 0; i < size; i++) { + if (!isCompatible(operationInputType.get(i), asyncOperationInputType.get(i))) { + return false; + } + } + + Type genericParamType = asyncOperationInputType.get(size).getGenericType(); + + Class asyncLastParameterTypeClass = asyncOperationInputType.get(size).getPhysical(); + if (asyncLastParameterTypeClass == AsyncHandler.class) { + //now check the actual type of the AsyncHandler with R + Class asyncActualLastParameterTypeClass = Object.class; + if (genericParamType instanceof ParameterizedType) { + ParameterizedType asyncLastParameterType = (ParameterizedType)genericParamType; + asyncActualLastParameterTypeClass = (Class)asyncLastParameterType.getActualTypeArguments()[0]; + } + + if (operation.getOutputWrapper() != null) { + // The return type could be the wrapper type per JAX-WS spec + Class wrapperClass = operation.getOutputWrapper().getWrapperClass(); + if (wrapperClass == asyncActualLastParameterTypeClass) { + return true; + } + } + + Class returnType = null; + if (operation.getOutputType() != null && operation.getOutputType().getLogical() != null && operation.getOutputType().getLogical().size() > 0) { + returnType = operation.getOutputType().getLogical().get(0).getPhysical(); + } + if (returnType != null) { + if (returnType == asyncActualLastParameterTypeClass || returnType.isPrimitive() + && primitiveAssignable(returnType, asyncActualLastParameterTypeClass)) { + return true; + } + } + return false; + } + + return true; + } + + /** + * Get operation by name + * + * @param operations + * @param operationName + * @return + */ + private static List getAsyncOperations(List operations, JavaOperation op) { + List returnOperations = new ArrayList(); + + for (Operation o : operations) { + if (o == op) { + continue; + } + String operationName = op.getName(); + if (o.getName().equals(operationName)) { + // Async operations have the same name when @WebMethod is present + /* + JavaOperation jop = (JavaOperation)o; + if (op.getJavaMethod().getName().equals(jop.getJavaMethod().getName() + ASYNC)) { + returnOperations.add(o); + } + */ + returnOperations.add(o); + } else if (o.getName().equals(operationName + ASYNC)) { + returnOperations.add(o); + } + } + + return returnOperations; + } + + /** + * Check if two operation parameters are compatible + * + * @param source + * @param target + * @return + */ + private static boolean isCompatible(DataType source, DataType target) { + if (source == target) { + return true; + } + + return target.getPhysical().isAssignableFrom(source.getPhysical()); + } + + /** + * Compares a two types, assuming one is a primitive, to determine if the + * other is its object counterpart + */ + private static boolean primitiveAssignable(Class memberType, Class param) { + if (memberType == Integer.class) { + return param == Integer.TYPE; + } else if (memberType == Double.class) { + return param == Double.TYPE; + } else if (memberType == Float.class) { + return param == Float.TYPE; + } else if (memberType == Short.class) { + return param == Short.TYPE; + } else if (memberType == Character.class) { + return param == Character.TYPE; + } else if (memberType == Boolean.class) { + return param == Boolean.TYPE; + } else if (memberType == Byte.class) { + return param == Byte.TYPE; + } else if (param == Integer.class) { + return memberType == Integer.TYPE; + } else if (param == Double.class) { + return memberType == Double.TYPE; + } else if (param == Float.class) { + return memberType == Float.TYPE; + } else if (param == Short.class) { + return memberType == Short.TYPE; + } else if (param == Character.class) { + return memberType == Character.TYPE; + } else if (param == Boolean.class) { + return memberType == Boolean.TYPE; + } else if (param == Byte.class) { + return memberType == Byte.TYPE; + } else { + return false; + } + } +} diff --git a/sca-java-2.x/tags/2.0.1-RC1/modules/interface-java-jaxws/src/main/java/org/apache/tuscany/sca/interfacedef/java/jaxws/JAXWSFaultExceptionMapper.java b/sca-java-2.x/tags/2.0.1-RC1/modules/interface-java-jaxws/src/main/java/org/apache/tuscany/sca/interfacedef/java/jaxws/JAXWSFaultExceptionMapper.java new file mode 100644 index 0000000000..166f1d6b6d --- /dev/null +++ b/sca-java-2.x/tags/2.0.1-RC1/modules/interface-java-jaxws/src/main/java/org/apache/tuscany/sca/interfacedef/java/jaxws/JAXWSFaultExceptionMapper.java @@ -0,0 +1,404 @@ +/* + * 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.Introspector; +import java.beans.PropertyDescriptor; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.security.AccessController; +import java.security.PrivilegedAction; + +import javax.xml.namespace.QName; +import javax.xml.ws.WebFault; + +import org.apache.tuscany.sca.core.ExtensionPointRegistry; +import org.apache.tuscany.sca.databinding.DataBindingExtensionPoint; +import org.apache.tuscany.sca.databinding.jaxb.XMLAdapterExtensionPoint; +import org.apache.tuscany.sca.interfacedef.DataType; +import org.apache.tuscany.sca.interfacedef.FaultExceptionMapper; +import org.apache.tuscany.sca.interfacedef.Operation; +import org.apache.tuscany.sca.interfacedef.impl.DataTypeImpl; +import org.apache.tuscany.sca.interfacedef.java.JavaInterface; +import org.apache.tuscany.sca.interfacedef.util.FaultException; +import org.apache.tuscany.sca.interfacedef.util.XMLType; +import org.oasisopen.sca.ServiceRuntimeException; + +/** + * JAX-WS ExceptionHandler + * + * @version $Rev$ $Date$ + */ +public class JAXWSFaultExceptionMapper implements FaultExceptionMapper { + public static final String GETCAUSE = "getCause"; + public static final String GETLOCALIZEDMESSAGE = "getLocalizedMessage"; + public static final String GETSTACKTRACE = "getStackTrace"; + public static final String GETCLASS = "getClass"; + + private static final Class[] EMPTY_CLASS_ARRAY = new Class[0]; + private DataBindingExtensionPoint dataBindingExtensionPoint; + private XMLAdapterExtensionPoint xmlAdapterExtensionPoint; + + + public JAXWSFaultExceptionMapper(DataBindingExtensionPoint dataBindingExtensionPoint, XMLAdapterExtensionPoint xmlAdapters) { + super(); + this.dataBindingExtensionPoint = dataBindingExtensionPoint; + this.xmlAdapterExtensionPoint = xmlAdapters; + } + + public JAXWSFaultExceptionMapper(ExtensionPointRegistry registry) { + this.dataBindingExtensionPoint = registry.getExtensionPoint(DataBindingExtensionPoint.class); + this.xmlAdapterExtensionPoint = registry.getExtensionPoint(XMLAdapterExtensionPoint.class); + } + + /** + * The following is quoted from the JAX-WS Specification v2.1 + *
    + *
  • WrapperException(String message, FaultBean faultInfo)
    + * A constructor where WrapperException is replaced with the name of the + * generated wrapper exception and FaultBean is replaced by the name of the + * generated fault bean. + *
  • WrapperException(String message, FaultBean faultInfo, Throwable + * cause)
    + * A constructor where WrapperException is replaced with the name of the + * generated wrapper exception and FaultBean is replaced by the name of the + * generated fault bean. The last argument, cause, may be used to convey + * protocol specific fault information + *
+ */ + @SuppressWarnings("unchecked") + public Throwable wrapFaultInfo(DataType exceptionType, String message, Object faultInfo, Throwable cause, Operation operation) { + Class exceptionClass = exceptionType.getPhysical(); + if (exceptionClass.isInstance(faultInfo)) { + return (Throwable)faultInfo; + } + DataType faultBeanType = exceptionType.getLogical(); + Class faultBeanClass = faultBeanType.getPhysical(); + try { + Throwable exc = + newInstance((Class)exceptionClass, message, faultBeanClass, faultInfo, cause); + // Include the elem name into the FaultException we build so it can be used for matching in the DataTransformationInterceptor + // + // Note this may happen even if we find a constructor above, that is the type of the non-generic fault exc may be an instance + // of FaultException + // + if ((exc instanceof FaultException) && (faultBeanType.getLogical() instanceof XMLType)) { + FaultException faultExc = (FaultException)exc; + DataType faultBeanXMLType = (DataType)faultBeanType; + XMLType faultLogical = faultBeanXMLType.getLogical(); + faultExc.setFaultName(faultLogical.getElementName()); + } + return exc; + } catch (Throwable e) { + throw new IllegalArgumentException(e); + } + } + + private Throwable newInstance(Class exceptionClass, + String message, + Class faultBeanClass, + Object faultInfo, + Throwable cause) throws Exception { + Throwable ex = null; + Constructor ctor = null; + try { + // Get the message property + Method getMessage = faultBeanClass.getMethod("getMessage"); + message = (String)getMessage.invoke(faultInfo); + } catch (Throwable e) { + // Ignore + } + if (faultInfo == null) { + try { + ctor = exceptionClass.getConstructor(String.class, Throwable.class); + ex = ctor.newInstance(message, cause); + } catch (NoSuchMethodException e1) { + try { + ctor = exceptionClass.getConstructor(String.class); + ex = ctor.newInstance(message); + } catch (NoSuchMethodException e2) { + try { + ctor = exceptionClass.getConstructor(Throwable.class); + ex = ctor.newInstance(cause); + } catch (NoSuchMethodException e3) { + ctor = exceptionClass.getConstructor(); + ex = ctor.newInstance(); + } + } + } + } else { + try { + // FIXME: What about if the faultBeanClass is a subclass of the argument type? + ctor = exceptionClass.getConstructor(String.class, faultBeanClass, Throwable.class); + ex = ctor.newInstance(message, faultInfo, cause); + } catch (NoSuchMethodException e1) { + try { + ctor = exceptionClass.getConstructor(String.class, faultInfo.getClass()); + ex = ctor.newInstance(message, faultInfo); + } catch (NoSuchMethodException e2) { + try { + ctor = exceptionClass.getConstructor(String.class, Throwable.class); + ex = ctor.newInstance(message, cause); + populateException(ex, faultInfo); + } catch (NoSuchMethodException e3) { + try { + ctor = exceptionClass.getConstructor(String.class); + ex = ctor.newInstance(message); + populateException(ex, faultInfo); + } catch (NoSuchMethodException e4) { + try { + ctor = exceptionClass.getConstructor(); + if (ctor != null) { + ex = ctor.newInstance(); + populateException(ex, faultInfo); + } else { + ex = new FaultException(message, faultInfo, cause); + } + } catch (NoSuchMethodException e5) { + try { + ctor = exceptionClass.getConstructor(Throwable.class); + ex = ctor.newInstance(cause); + populateException(ex, faultInfo); + } catch (NoSuchMethodException e6) { + ctor = exceptionClass.getConstructor(); + ex = ctor.newInstance(); + } + } + } + } + } + } + } + + return ex; + } + + /** + * Populate the java exception from the fault bean + * @param ex + * @param faultBean + * @throws Exception + */ + private void populateException(Throwable ex, Object faultBean) throws Exception { + PropertyDescriptor props[] = Introspector.getBeanInfo(faultBean.getClass()).getPropertyDescriptors(); + for (PropertyDescriptor p : props) { + Method getter = p.getReadMethod(); + Method setter = p.getWriteMethod(); + if (getter == null || setter == null) { + continue; + } + try { + Method m = ex.getClass().getMethod(setter.getName(), setter.getParameterTypes()); + Object pv = getter.invoke(faultBean); + m.invoke(ex, pv); + } catch (Exception e) { + // Ignore; + } + } + } + + public Object getFaultInfo(Throwable exception, Class faultBeanClass, Operation operation) { + if (exception == null) { + return null; + } + + // Check if it's the generic FaultException + if (exception instanceof FaultException) { + return ((FaultException)exception).getFaultInfo(); + } + + try { + Method method = exception.getClass().getMethod("getFaultInfo", EMPTY_CLASS_ARRAY); + return method.invoke(exception, (Object[])null); + } catch (NoSuchMethodException e) { + // Follow the JAX-WS v2.1 Specification section 3.7 + return createFaultBean(exception, faultBeanClass); + } catch (Throwable e) { + throw new IllegalArgumentException(e); + } + } + + private Object createFaultBean(Throwable exception, Class faultBeanClass) { + /** + * For each getter in the exception and its superclasses, a property of the same + * type and name is added to the bean. The getCause, getLocalizedMessage and + * getStackTrace getters from java.lang.Throwable and the getClass getter from + * java.lang.Object are excluded from the list of getters to be mapped. + */ + // Return the exception as-is if it's already the fault bean + if (faultBeanClass.isInstance(exception)) { + return exception; + } + try { + Object faultBean = null; + for (Constructor ctor : faultBeanClass.getConstructors()) { + Class[] params = ctor.getParameterTypes(); + if (params.length == 1 && String.class == params[0]) { + faultBean = ctor.newInstance(exception.getMessage()); + } else if (params.length == 2 && String.class == params[0] + && Throwable.class.isAssignableFrom(params[1])) { + faultBean = ctor.newInstance(exception.getMessage(), exception); + } else if (params.length == 0) { + faultBean = ctor.newInstance(); + } + if (faultBean != null) { + break; + } + } + if (faultBean == null) { + return exception; + } + BeanInfo beanInfo = Introspector.getBeanInfo(exception.getClass()); + for (PropertyDescriptor pd : beanInfo.getPropertyDescriptors()) { + Method getter = pd.getReadMethod(); + String name = getter.getName(); + if (!isMappedGetter(name)) { + continue; + } + Method setter = null; + try { + setter = faultBeanClass.getMethod("set" + capitalize(pd.getName()), getter.getReturnType()); + } catch (NoSuchMethodException e) { + continue; + } + Object prop = getter.invoke(exception); + setter.invoke(faultBean, prop); + } + return faultBean; + } catch (Throwable ex) { + throw new IllegalArgumentException(ex); + } + } + + @SuppressWarnings("unchecked") + public boolean introspectFaultDataType(DataType exceptionType, final Operation operation, final boolean generatingFaultBean) { + + boolean result = false; + + final Class cls = exceptionType.getPhysical(); + if (cls == FaultException.class) { + return true; + } + DataType faultType = (DataType)exceptionType.getLogical(); + QName faultName = ((XMLType)faultType.getLogical()).getElementName(); + Class faultBean = null; + final WebFault fault = cls.getAnnotation(WebFault.class); + if (fault != null) { + if (!"".equals(fault.name()) || !"".equals(fault.targetNamespace())) { + QName faultQName = ((XMLType)faultType.getLogical()).getElementName(); + String faultNS = + "".equals(fault.targetNamespace()) ? faultQName.getNamespaceURI() : fault.targetNamespace(); + String faultLocal = "".equals(fault.name()) ? faultQName.getLocalPart() : fault.name(); + faultName = new QName(faultNS, faultLocal); + XMLType xmlType = new XMLType(faultName, null); + faultType.setLogical(xmlType); + } + if (!"".equals(fault.faultBean())) { + faultBean = AccessController.doPrivileged(new PrivilegedAction>() { + public Class run() { + try { + return Class.forName(fault.faultBean(), false, cls.getClassLoader()); + } catch (ClassNotFoundException e) { + throw new ServiceRuntimeException(e); + } + } + }); + } else { + Method m; + try { + m = cls.getMethod("getFaultInfo", (Class[])null); + faultBean = m.getReturnType(); + } catch (NoSuchMethodException e) { + // Ignore + } + } + } + + if (faultBean == null) { + final String faultBeanClassName = CodeGenerationHelper.getPackagePrefix(cls) + cls.getSimpleName() + "Bean"; + final QName qname = faultName; + faultType = AccessController.doPrivileged(new PrivilegedAction>() { + public DataType run() { + try { + Class faultBean = Class.forName(faultBeanClassName, false, cls.getClassLoader()); + return new DataTypeImpl(faultBean, new XMLType(qname, qname)); + } catch (ClassNotFoundException e) { + if (generatingFaultBean) { + Class t = (Class)cls; + ClassLoader parent = + operation == null ? t.getClassLoader() : ((JavaInterface)operation.getInterface()) + .getJavaClass().getClassLoader(); + GeneratedClassLoader cl = new GeneratedClassLoader(parent); + GeneratedDataTypeImpl dt = new GeneratedDataTypeImpl(xmlAdapterExtensionPoint, t, cl, operation); + return dt; + } else { + return new DataTypeImpl(cls, new XMLType(qname, qname)); + } + } + } + }); + } else { + faultType.setDataBinding(null); + faultType.setGenericType(faultBean); + faultType.setPhysical(faultBean); + } + + // TODO: Use the databinding framework to introspect the fault bean class + if (faultType.getDataBinding() == null && dataBindingExtensionPoint != null) { + faultBean = faultType.getPhysical(); + result = + dataBindingExtensionPoint.introspectType(faultType, operation); + } + ((DataType) exceptionType).setLogical(faultType); + + /* + The introspection of the fault DT may not have calculated the correct element name, + though we may have already done this in this method. Let's look at the DataType now + that introspection is done, and, if it has an XMLType, let's set the element to the + 'faultName' if we calculated one. + */ + if ((faultName != null) && (faultType.getLogical() instanceof XMLType)) { + XMLType faultTypeXML = (XMLType)faultType.getLogical(); + // The element name (if set) should match the fault name + faultTypeXML.setElementName(faultName); + } + + return result; + } + + public static boolean isMappedGetter(String methodName) { + if (GETCAUSE.equals(methodName) || GETLOCALIZEDMESSAGE.equals(methodName) + || GETSTACKTRACE.equals(methodName) + || GETCLASS.equals(methodName)) { + return false; + } else { + return true; + } + } + + private static String capitalize(String name) { + if (name == null || name.length() == 0) { + return name; + } else { + return Character.toUpperCase(name.charAt(0)) + name.substring(1); + } + } +} diff --git a/sca-java-2.x/tags/2.0.1-RC1/modules/interface-java-jaxws/src/main/java/org/apache/tuscany/sca/interfacedef/java/jaxws/JAXWSJavaInterfaceProcessor.java b/sca-java-2.x/tags/2.0.1-RC1/modules/interface-java-jaxws/src/main/java/org/apache/tuscany/sca/interfacedef/java/jaxws/JAXWSJavaInterfaceProcessor.java new file mode 100644 index 0000000000..2e6b19b451 --- /dev/null +++ b/sca-java-2.x/tags/2.0.1-RC1/modules/interface-java-jaxws/src/main/java/org/apache/tuscany/sca/interfacedef/java/jaxws/JAXWSJavaInterfaceProcessor.java @@ -0,0 +1,448 @@ +/* + * 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.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import javax.jws.Oneway; +import javax.jws.WebMethod; +import javax.jws.WebParam; +import javax.jws.WebParam.Mode; +import javax.jws.WebResult; +import javax.jws.soap.SOAPBinding; +import javax.jws.soap.SOAPBinding.Style; +import javax.xml.namespace.QName; +import javax.xml.ws.RequestWrapper; +import javax.xml.ws.ResponseWrapper; + +import org.apache.tuscany.sca.core.ExtensionPointRegistry; +import org.apache.tuscany.sca.core.FactoryExtensionPoint; +import org.apache.tuscany.sca.core.UtilityExtensionPoint; +import org.apache.tuscany.sca.databinding.DataBindingExtensionPoint; +import org.apache.tuscany.sca.databinding.javabeans.JavaExceptionDataBinding; +import org.apache.tuscany.sca.databinding.jaxb.JAXBDataBinding; +import org.apache.tuscany.sca.databinding.jaxb.XMLAdapterExtensionPoint; +import org.apache.tuscany.sca.interfacedef.DataType; +import org.apache.tuscany.sca.interfacedef.FaultExceptionMapper; +import org.apache.tuscany.sca.interfacedef.InvalidInterfaceException; +import org.apache.tuscany.sca.interfacedef.Operation; +import org.apache.tuscany.sca.interfacedef.ParameterMode; +import org.apache.tuscany.sca.interfacedef.impl.DataTypeImpl; +import org.apache.tuscany.sca.interfacedef.java.JavaInterface; +import org.apache.tuscany.sca.interfacedef.java.JavaInterfaceFactory; +import org.apache.tuscany.sca.interfacedef.java.JavaOperation; +import org.apache.tuscany.sca.interfacedef.java.introspect.JavaInterfaceVisitor; +import org.apache.tuscany.sca.interfacedef.util.ElementInfo; +import org.apache.tuscany.sca.interfacedef.util.TypeInfo; +import org.apache.tuscany.sca.interfacedef.util.WrapperInfo; +import org.apache.tuscany.sca.interfacedef.util.XMLType; +import org.apache.tuscany.sca.interfacedef.wsdl.WSDLFactory; + +/** + * Introspect the java class/interface with JSR-181 and JAXWS annotations + * + * @version $Rev$ $Date$ + */ +public class JAXWSJavaInterfaceProcessor implements JavaInterfaceVisitor { + private static final String JAXB_DATABINDING = JAXBDataBinding.NAME; + private static final String GET = "get"; + private DataBindingExtensionPoint dataBindingExtensionPoint; + private FaultExceptionMapper faultExceptionMapper; + private XMLAdapterExtensionPoint xmlAdapterExtensionPoint; + protected JavaInterfaceFactory javaInterfaceFactory; + private WSDLFactory wsdlFactory; + + + public JAXWSJavaInterfaceProcessor(ExtensionPointRegistry registry) { + dataBindingExtensionPoint = registry.getExtensionPoint(DataBindingExtensionPoint.class); + faultExceptionMapper = registry.getExtensionPoint(UtilityExtensionPoint.class).getUtility(FaultExceptionMapper.class); + xmlAdapterExtensionPoint = registry.getExtensionPoint(XMLAdapterExtensionPoint.class); + + FactoryExtensionPoint factories = registry.getExtensionPoint(FactoryExtensionPoint.class); + this.javaInterfaceFactory = factories.getFactory(JavaInterfaceFactory.class); + this.wsdlFactory = factories.getFactory(WSDLFactory.class); + } + + + public JAXWSJavaInterfaceProcessor() { + super(); + } + + private ParameterMode getParameterMode(WebParam.Mode mode) { + if (mode == Mode.INOUT) { + return ParameterMode.INOUT; + } else if (mode == Mode.OUT) { + return ParameterMode.OUT; + } else { + return ParameterMode.IN; + } + } + + private static String capitalize(String name) { + if (name == null || name.length() == 0) { + return name; + } else { + return Character.toUpperCase(name.charAt(0)) + name.substring(1); + } + } + + public void visitInterface(JavaInterface contract) throws InvalidInterfaceException { + + final Class clazz = contract.getJavaClass(); + + contract = JAXWSUtils.configureJavaInterface(contract, clazz); + String tns = contract.getQName().getNamespaceURI(); + + // run this regardless in case the user overrides + // the remotable flag in the SCDL + //if (!contract.isRemotable()) { + // return; + //} + + // SOAP binding (doc/lit/wrapped|bare or rpc/lit) + SOAPBinding soapBinding = clazz.getAnnotation(SOAPBinding.class); + + for (Iterator it = contract.getOperations().iterator(); it.hasNext();) { + final JavaOperation operation = (JavaOperation)it.next(); + final Method method = operation.getJavaMethod(); + introspectFaultTypes(operation); + + // SOAP binding (doc/lit/wrapped|bare or rpc/lit) + SOAPBinding methodSOAPBinding = method.getAnnotation(SOAPBinding.class); + if (methodSOAPBinding == null) { + methodSOAPBinding = soapBinding; + } + + boolean documentStyle = true; + boolean bare = false; + if (methodSOAPBinding != null) { + bare = methodSOAPBinding.parameterStyle() == SOAPBinding.ParameterStyle.BARE; + if(bare) { + // For BARE parameter style, the data won't be unwrapped + // The wrapper should be null + operation.setInputWrapperStyle(false); + operation.setOutputWrapperStyle(false); + } + documentStyle = methodSOAPBinding.style() == Style.DOCUMENT; + } + + String operationName = operation.getName(); + // WebMethod + WebMethod webMethod = method.getAnnotation(WebMethod.class); + if (webMethod != null) { + if (webMethod.exclude()) { + // Exclude the method + it.remove(); + continue; + } + operationName = getValue(webMethod.operationName(), operationName); + operation.setName(operationName); + operation.setAction(webMethod.action()); + } + + // Is one way? + Oneway oneway = method.getAnnotation(Oneway.class); + if (oneway != null) { + // JSR 181 + assert method.getReturnType() == void.class; + operation.setNonBlocking(true); + } + + // Handle BARE mapping + if (bare) { + for (int i = 0; i < method.getParameterTypes().length; i++) { + String ns = tns; + // Default to for doc-bare + String name = (documentStyle ? operationName : "arg" + i); + WebParam param = getAnnotation(method, i, WebParam.class); + if (param != null) { + if (!"".equals(param.targetNamespace())) + ns = param.targetNamespace(); + if (!"".equals(param.name())) + name = param.name(); + operation.getParameterModes().set(i, getParameterMode(param.mode())); + } + QName element = new QName(ns, name); + Object logical = operation.getInputType().getLogical().get(i).getLogical(); + if (logical instanceof XMLType) { + ((XMLType)logical).setElementName(element); + } + } + + if (!operation.hasReturnTypeVoid()) { + String ns = tns; + // Default to Response for doc-bare + String name = (documentStyle ? operationName + "Response" : "return"); + WebResult result = method.getAnnotation(WebResult.class); + if (result != null) { + if (!"".equals(result.targetNamespace())) + ns = result.targetNamespace(); + if (!"".equals(result.name())) + name = result.name(); + } + QName element = new QName(ns, name); + List outputDataTypes = operation.getOutputType().getLogical(); + DataType returnDataType = outputDataTypes.get(0); + if (returnDataType instanceof XMLType) { + ((XMLType)returnDataType).setElementName(element); + } + } + // Rather than relying on null wrapper, we use a flag with a clearer meaning. + operation.setNotSubjectToWrapping(true); + } else { + + RequestWrapper requestWrapper = method.getAnnotation(RequestWrapper.class); + String ns = requestWrapper == null ? tns : getValue(requestWrapper.targetNamespace(), tns); + String name = + requestWrapper == null ? operationName : getValue(requestWrapper.localName(), operationName); + String wrapperBeanName = requestWrapper == null ? "" : requestWrapper.className(); + if ("".equals(wrapperBeanName)) { + wrapperBeanName = CodeGenerationHelper.getPackagePrefix(clazz) + capitalize(method.getName()); + } + + DataType inputWrapperDT = null; + + final String inputWrapperClassName = wrapperBeanName; + final String inputNS = ns; + final String inputName = name; + inputWrapperDT = AccessController.doPrivileged(new PrivilegedAction>() { + public DataType run() { + try { + Class wrapperClass = Class.forName(inputWrapperClassName, false, clazz.getClassLoader()); + QName qname = new QName(inputNS, inputName); + DataType dt = new DataTypeImpl(wrapperClass, new XMLType(qname, qname)); + dataBindingExtensionPoint.introspectType(dt, operation); + // TUSCANY-2505 + if (dt.getLogical() instanceof XMLType) { + XMLType xmlType = (XMLType)dt.getLogical(); + xmlType.setElementName(qname); + } + return dt; + } catch (ClassNotFoundException e) { + GeneratedClassLoader cl = new GeneratedClassLoader(clazz.getClassLoader()); + return new GeneratedDataTypeImpl(xmlAdapterExtensionPoint, method, inputWrapperClassName, inputNS, inputName, true, + cl); + } + } + }); + + QName inputWrapper = inputWrapperDT.getLogical().getElementName(); + + ResponseWrapper responseWrapper = method.getAnnotation(ResponseWrapper.class); + ns = responseWrapper == null ? tns : getValue(responseWrapper.targetNamespace(), tns); + name = + responseWrapper == null ? operationName + "Response" : getValue(responseWrapper.localName(), + operationName + "Response"); + wrapperBeanName = responseWrapper == null ? "" : responseWrapper.className(); + if ("".equals(wrapperBeanName)) { + wrapperBeanName = + CodeGenerationHelper.getPackagePrefix(clazz) + capitalize(method.getName()) + "Response"; + } + + DataType outputWrapperDT = null; + final String outputWrapperClassName = wrapperBeanName; + final String outputNS = ns; + final String outputName = name; + + outputWrapperDT = AccessController.doPrivileged(new PrivilegedAction>() { + public DataType run() { + try { + Class wrapperClass = + Class.forName(outputWrapperClassName, false, clazz.getClassLoader()); + QName qname = new QName(outputNS, outputName); + DataType dt = new DataTypeImpl(wrapperClass, new XMLType(qname, qname)); + dataBindingExtensionPoint.introspectType(dt, operation); + // TUSCANY-2505 + if (dt.getLogical() instanceof XMLType) { + XMLType xmlType = (XMLType)dt.getLogical(); + xmlType.setElementName(qname); + } + return dt; + } catch (ClassNotFoundException e) { + GeneratedClassLoader cl = new GeneratedClassLoader(clazz.getClassLoader()); + return new GeneratedDataTypeImpl(xmlAdapterExtensionPoint, method, outputWrapperClassName, outputNS, outputName, + false, cl); + } + } + }); + QName outputWrapper = outputWrapperDT.getLogical().getElementName(); + + + // + // Since JAX-WS specifies that the output wrapper bean consists of the return type output first followed + // by any other outputs carried in Holder(s), let's look at the output first. + // + List outputElements = new ArrayList(); + WebResult result = method.getAnnotation(WebResult.class); + // Default to "" for doc-lit-wrapped && non-header + ns = result != null ? result.targetNamespace() : ""; + ns = getValue(ns, documentStyle && (result == null || !result.header()) ? "" : tns); + name = result != null ? result.name() : ""; + name = getValue(name, "return"); + QName element = new QName(ns, name); + + if (!operation.hasReturnTypeVoid()) { + Object logical = operation.getOutputType().getLogical().get(0).getLogical(); + QName type = null; + if (logical instanceof XMLType) { + ((XMLType)logical).setElementName(element); + type = ((XMLType)logical).getTypeName(); + } + outputElements.add(new ElementInfo(element, new TypeInfo(type, false, null))); + } + + List inputElements = new ArrayList(); + for (int i = 0; i < operation.getInputType().getLogical().size(); i++) { + WebParam param = getAnnotation(method, i, WebParam.class); + ns = param != null ? param.targetNamespace() : ""; + // Default to "" for doc-lit-wrapped && non-header + ns = getValue(ns, documentStyle && (param == null || !param.header()) ? "" : tns); + name = param != null ? param.name() : ""; + name = getValue(name, "arg" + i); + element = new QName(ns, name); + Object logical = operation.getInputType().getLogical().get(i).getLogical(); + QName type = null; + if (logical instanceof XMLType) { + ((XMLType)logical).setElementName(element); + type = ((XMLType)logical).getTypeName(); + } + + if (param != null) { + ParameterMode mode = getParameterMode(param.mode()); + operation.getParameterModes().set(i, mode); + } + ParameterMode mode = operation.getParameterModes().get(i); + + if (mode.equals(ParameterMode.INOUT)) { + inputElements.add(new ElementInfo(element, new TypeInfo(type, false, null))); + outputElements.add(new ElementInfo(element, new TypeInfo(type, false, null))); + } else if (mode.equals(ParameterMode.OUT)) { + outputElements.add(new ElementInfo(element, new TypeInfo(type, false, null))); + } else { + inputElements.add(new ElementInfo(element, new TypeInfo(type, false, null))); + } + } + + // TUSCANY-3804 - handle output wrapper separately + String dbIn = inputWrapperDT != null ? inputWrapperDT.getDataBinding() : JAXB_DATABINDING; + String dbOut = outputWrapperDT != null ? outputWrapperDT.getDataBinding() : JAXB_DATABINDING; + + WrapperInfo inputWrapperInfo = new WrapperInfo(dbIn, new ElementInfo(inputWrapper, null), inputElements); + WrapperInfo outputWrapperInfo = new WrapperInfo(dbOut, new ElementInfo(outputWrapper, null), outputElements); + + inputWrapperInfo.setWrapperType(inputWrapperDT); + outputWrapperInfo.setWrapperType(outputWrapperDT); + + operation.setInputWrapper(inputWrapperInfo); + operation.setOutputWrapper(outputWrapperInfo); + } + + // In both bare and wrapped cases, remove OUT-only parameters from input DataType. + // This is a key point then because it's the last time in which the number of parameters in + // Java matches the number of logical inputs. After this, things will be out of synch, for + // example the number of parameter modes won't match the number of inputs. + List parmModes = operation.getParameterModes(); + List inputDTs = operation.getInputType().getLogical(); + for (int i = parmModes.size() - 1; i>=0; i--) { + if (parmModes.get(i).equals(ParameterMode.OUT)) { + inputDTs.remove(i); + } + } + + } + } + + @SuppressWarnings("unchecked") + private void introspectFaultTypes(Operation operation) { + if (operation != null && operation.getFaultTypes() != null) { + for (DataType exceptionType : operation.getFaultTypes()) { + faultExceptionMapper.introspectFaultDataType(exceptionType, operation, true); + DataType faultType = (DataType)exceptionType.getLogical(); + if (JavaExceptionDataBinding.NAME.equals(faultType.getDataBinding())) { + // The exception class doesn't have an associated bean class, so + // synthesize a virtual bean by introspecting the exception class. + createSyntheticBean(operation, exceptionType); + } + } + } + } + + private void createSyntheticBean(Operation operation, DataType exceptionType) { + DataType faultType = (DataType)exceptionType.getLogical(); + QName faultBeanName = ((XMLType)faultType.getLogical()).getElementName(); + List> beanDataTypes = new ArrayList>(); + for (Method aMethod : exceptionType.getPhysical().getMethods()) { + if (Modifier.isPublic(aMethod.getModifiers()) && aMethod.getName().startsWith(GET) + && aMethod.getParameterTypes().length == 0 + && JAXWSFaultExceptionMapper.isMappedGetter(aMethod.getName())) { + String propName = resolvePropertyFromMethod(aMethod.getName()); + QName propQName = new QName(faultBeanName.getNamespaceURI(), propName); + Class propType = aMethod.getReturnType(); + XMLType xmlPropType = new XMLType(propQName, null); + DataType propDT = new DataTypeImpl(propType, xmlPropType); + org.apache.tuscany.sca.databinding.annotation.DataType dt = + aMethod.getAnnotation(org.apache.tuscany.sca.databinding.annotation.DataType.class); + if (dt != null) { + propDT.setDataBinding(dt.value()); + } + dataBindingExtensionPoint.introspectType(propDT, operation); + + // sort the list lexicographically as specified in JAX-WS spec section 3.7 + int i = 0; + for (; i < beanDataTypes.size(); i++) { + if (beanDataTypes.get(i).getLogical().getElementName().getLocalPart().compareTo(propName) > 0) { + break; + } + } + beanDataTypes.add(i, propDT); + } + } + operation.getFaultBeans().put(faultBeanName, beanDataTypes); + } + + private String resolvePropertyFromMethod(String methodName) { + StringBuffer propName = new StringBuffer(); + propName.append(Character.toLowerCase(methodName.charAt(GET.length()))); + propName.append(methodName.substring(GET.length() + 1)); + return propName.toString(); + } + + private T getAnnotation(Method method, int index, Class annotationType) { + Annotation[] annotations = method.getParameterAnnotations()[index]; + for (Annotation annotation : annotations) { + if (annotation.annotationType() == annotationType) { + return annotationType.cast(annotation); + } + } + return null; + } + + private static String getValue(String value, String defaultValue) { + return "".equals(value) ? defaultValue : value; + } + +} diff --git a/sca-java-2.x/tags/2.0.1-RC1/modules/interface-java-jaxws/src/main/java/org/apache/tuscany/sca/interfacedef/java/jaxws/JAXWSUtils.java b/sca-java-2.x/tags/2.0.1-RC1/modules/interface-java-jaxws/src/main/java/org/apache/tuscany/sca/interfacedef/java/jaxws/JAXWSUtils.java new file mode 100644 index 0000000000..011d74625d --- /dev/null +++ b/sca-java-2.x/tags/2.0.1-RC1/modules/interface-java-jaxws/src/main/java/org/apache/tuscany/sca/interfacedef/java/jaxws/JAXWSUtils.java @@ -0,0 +1,104 @@ +/* + * 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 javax.jws.WebService; +import javax.xml.namespace.QName; +import javax.xml.ws.WebServiceProvider; + +import org.apache.tuscany.sca.interfacedef.InvalidInterfaceException; +import org.apache.tuscany.sca.interfacedef.java.JavaInterface; +import org.apache.tuscany.sca.interfacedef.util.JavaXMLMapper; + + +/** + * A set of utility methods for processing JAXWS annotations that are + * shared between Java inteface and implementation processing + */ +public class JAXWSUtils { + + /** + * JAXWS annotations may identify a service interface via either + * - an interface class name, e.g. @WebService(endpointInterface="my.service.ServiceImpl") + * - a wsdl file name, e.g. @WebService(wsdlLocation="some.wsdl") + * - a Java class/interface, e.g. @WebService + * This operation configures the Java interface based on these separate pieces + * of information. The resulting interface contract must be subsequently resolved in order that + * the named endpoint interface class or wsdl file is found + * + * @param javaInterface the Tuscany representation of the Java interface + * @param clazz the Java class that the interface refers to (may have JAXWS annotations) + * @return + */ + public static JavaInterface configureJavaInterface(JavaInterface javaInterface, + Class clazz){ + + String servineNamespace = JavaXMLMapper.getNamespace(clazz); + String serviceName = clazz.getSimpleName(); + QName serviceQName = null; + String serviceInterfaceClassName = null; + String wsdlFileName = null; + + WebService webServiceAnnotation = clazz.getAnnotation(WebService.class); + if (webServiceAnnotation != null) { + servineNamespace = getValue(webServiceAnnotation.targetNamespace(), servineNamespace); + serviceName = getValue(webServiceAnnotation.name(), serviceName); + serviceInterfaceClassName = webServiceAnnotation.endpointInterface(); + wsdlFileName = webServiceAnnotation.wsdlLocation(); + javaInterface.setRemotable(true); + } + + WebServiceProvider webServiceProviderAnnotation = clazz.getAnnotation(WebServiceProvider.class); + if (webServiceProviderAnnotation != null) { + servineNamespace = getValue(webServiceProviderAnnotation.targetNamespace(), servineNamespace); + serviceName = getValue(webServiceProviderAnnotation.serviceName(), serviceName); + wsdlFileName = webServiceProviderAnnotation.wsdlLocation(); + javaInterface.setRemotable(true); + } + + serviceQName = new QName(servineNamespace, serviceName); + javaInterface.setQName(serviceQName); + + // use the provided Java interface name to overwrite + // any Java interface created from an implemented interfaces + if (serviceInterfaceClassName != null && + serviceInterfaceClassName.length() > 0){ + javaInterface.setName(serviceInterfaceClassName); + javaInterface.setJAXWSJavaInterfaceName(serviceInterfaceClassName); + javaInterface.setUnresolved(true); + } + + // Store the WSDL location if it's specified in + // the @WebService annotation. Later this is resolved and is attached + // to the Java interface contract in the normalized space so that effectively the contract + // has both Java and WSDL interfaces. This allows databinding to + // operate correctly as it still expects a Java interface for a Java implementation + if (wsdlFileName != null && + wsdlFileName.length() > 0){ + javaInterface.setJAXWSWSDLLocation(wsdlFileName); + } + + return javaInterface; + } + + private static String getValue(String value, String defaultValue) { + return "".equals(value) ? defaultValue : value; + } +} diff --git a/sca-java-2.x/tags/2.0.1-RC1/modules/interface-java-jaxws/src/main/java/org/apache/tuscany/sca/interfacedef/java/jaxws/WebServiceInterfaceProcessor.java b/sca-java-2.x/tags/2.0.1-RC1/modules/interface-java-jaxws/src/main/java/org/apache/tuscany/sca/interfacedef/java/jaxws/WebServiceInterfaceProcessor.java new file mode 100644 index 0000000000..18eda13efb --- /dev/null +++ b/sca-java-2.x/tags/2.0.1-RC1/modules/interface-java-jaxws/src/main/java/org/apache/tuscany/sca/interfacedef/java/jaxws/WebServiceInterfaceProcessor.java @@ -0,0 +1,49 @@ +/* + * 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 javax.jws.WebService; + +import org.apache.tuscany.sca.interfacedef.InvalidInterfaceException; +import org.apache.tuscany.sca.interfacedef.java.JavaInterface; +import org.apache.tuscany.sca.interfacedef.java.introspect.JavaInterfaceVisitor; + +/** + * Introspect the java class/interface to see if it has @WebService annotation + * + * @version $Rev$ $Date$ + */ +public class WebServiceInterfaceProcessor implements JavaInterfaceVisitor { + + public WebServiceInterfaceProcessor() { + super(); + } + + public void visitInterface(JavaInterface contract) throws InvalidInterfaceException { + + final Class clazz = contract.getJavaClass(); + WebService webService = clazz.getAnnotation(WebService.class); + if (webService != null) { + // Mark SEI as Remotable + contract.setRemotable(true); + } + } + +} diff --git a/sca-java-2.x/tags/2.0.1-RC1/modules/interface-java-jaxws/src/main/java/org/apache/tuscany/sca/interfacedef/java/jaxws/WrapperBeanGenerator.java b/sca-java-2.x/tags/2.0.1-RC1/modules/interface-java-jaxws/src/main/java/org/apache/tuscany/sca/interfacedef/java/jaxws/WrapperBeanGenerator.java new file mode 100644 index 0000000000..ab7036c0ce --- /dev/null +++ b/sca-java-2.x/tags/2.0.1-RC1/modules/interface-java-jaxws/src/main/java/org/apache/tuscany/sca/interfacedef/java/jaxws/WrapperBeanGenerator.java @@ -0,0 +1,270 @@ +/* + * 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.lang.annotation.Annotation; +import java.lang.ref.WeakReference; +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.List; + +import javax.jws.WebParam; +import javax.jws.WebResult; + +import org.apache.tuscany.sca.interfacedef.java.impl.JavaInterfaceUtil; +import org.oasisopen.sca.ResponseDispatch; + +public class WrapperBeanGenerator extends BaseBeanGenerator { + + public List> generateWrapperBeans(Class sei) { + GeneratedClassLoader cl = new GeneratedClassLoader(sei.getClassLoader()); + List> classes = new ArrayList>(); + for (Method m : sei.getMethods()) { + if (m.getDeclaringClass() == Object.class) { + continue; + } + classes.add(generateRequestWrapper(sei, m, cl)); + classes.add(generateResponseWrapper(sei, m, cl)); + } + return classes; + + } + + public Class generateRequestWrapper(Class sei, Method m, GeneratedClassLoader cl) { + String wrapperNamespace = JavaInterfaceUtil.getNamespace(sei); + String wrapperName = m.getName(); + String wrapperBeanName = capitalize(wrapperName); + String wrapperClassName = CodeGenerationHelper.getPackagePrefix(sei) + wrapperBeanName; + + return generateRequestWrapper(m, wrapperClassName, wrapperNamespace, wrapperName, cl); + } + + public Class generateRequestWrapper(Method m, + String wrapperClassName, + String wrapperNamespace, + String wrapperName, + GeneratedClassLoader cl) { + synchronized (m.getDeclaringClass()) { + MethodKey key = new MethodKey(m, true); + WeakReference> wr = generatedClasses.get(key); + Class wrapperClass = null; + if (wr != null){ + wrapperClass = wr.get(); + } + if (wrapperClass == null) { + String wrapperClassDescriptor = wrapperClassName.replace('.', '/'); + String wrapperClassSignature = "L" + wrapperClassDescriptor + ";"; + + Class[] paramTypes = m.getParameterTypes(); + Type[] genericParamTypes = m.getGenericParameterTypes(); + Annotation[][] paramAnnotations = m.getParameterAnnotations(); + List properties = new ArrayList(); + + boolean asyncMethod = m.getName().endsWith("Async") && paramTypes.length > 0 && ResponseDispatch.class.equals(paramTypes[paramTypes.length-1]); + int length = paramTypes.length; + if (asyncMethod) { + length -= 1; + } + + for (int i = 0; i < length; i++) { + + String propNS = ""; + String propName = "arg" + i; + + WebParam webParam = findAnnotation(paramAnnotations[i], WebParam.class); + if (webParam != null && webParam.header()) { + continue; + } + WebParam.Mode mode = WebParam.Mode.IN; + if (webParam != null) { + mode = webParam.mode(); + if (webParam.name().length() > 0) { + propName = webParam.name(); + } + propNS = webParam.targetNamespace(); + } + + if (mode.equals(WebParam.Mode.IN) || mode.equals(WebParam.Mode.INOUT)) { + java.lang.reflect.Type genericParamType = getHolderValueType(genericParamTypes[i]); + Class paramType = CodeGenerationHelper.getErasure(genericParamType); + BeanProperty prop = new BeanProperty(propNS, propName, paramType, genericParamType, true); + prop.getJaxbAnnotaions().addAll(findJAXBAnnotations(paramAnnotations[i])); + properties.add(prop); + } + } + + wrapperClass = + generate(wrapperClassDescriptor, wrapperClassSignature, wrapperNamespace, wrapperName, properties + .toArray(new BeanProperty[properties.size()]), cl); + generatedClasses.put(key, new WeakReference>(wrapperClass)); + } + return wrapperClass; + + } + } + + public Class generateResponseWrapper(Class sei, Method m, GeneratedClassLoader cl) { + String wrapperNamespace = JavaInterfaceUtil.getNamespace(sei); + + String wrapperName = m.getName() + "Response"; + String wrapperBeanName = capitalize(wrapperName); + String wrapperClassName = CodeGenerationHelper.getPackagePrefix(sei) + wrapperBeanName; + return generateResponseWrapper(m, wrapperClassName, wrapperNamespace, wrapperName, cl); + + } + + public Class generateResponseWrapper(Method m, + String wrapperClassName, + String wrapperNamespace, + String wrapperName, + GeneratedClassLoader cl) { + synchronized (m.getDeclaringClass()) { + MethodKey key = new MethodKey(m, false); + WeakReference> wr = generatedClasses.get(key); + Class wrapperClass = null; + if (wr != null){ + wrapperClass = wr.get(); + } + if (wrapperClass == null) { + String wrapperClassDescriptor = wrapperClassName.replace('.', '/'); + String wrapperClassSignature = "L" + wrapperClassDescriptor + ";"; + + List properties = new ArrayList(); + // Collect all OUT, INOUT parameters as fields + Annotation[][] paramAnns = m.getParameterAnnotations(); + Class[] paramTypes = m.getParameterTypes(); + java.lang.reflect.Type[] genericParamTypes = m.getGenericParameterTypes(); + + boolean asyncMethod = m.getName().endsWith("Async") && paramTypes.length > 0 && ResponseDispatch.class.equals(paramTypes[paramTypes.length-1]); + int length = paramTypes.length; + if (asyncMethod) { + length -= 1; + } + + for (int i = 0; i < length; i++) { + WebParam webParam = findAnnotation(paramAnns[i], WebParam.class); + if (webParam != null) { + if (webParam.header() || webParam.mode() == WebParam.Mode.IN) { + continue; + } + } + if (!isHolder(genericParamTypes[i])) { + continue; + } + + List jaxb = findJAXBAnnotations(paramAnns[i]); + + java.lang.reflect.Type genericParamType = getHolderValueType(genericParamTypes[i]); + Class paramType = CodeGenerationHelper.getErasure(genericParamType); + + String paramNamespace = ""; + String paramName = "arg" + i; + + if (webParam != null) { + if (webParam.name().length() > 0) + paramName = webParam.name(); + if (webParam.targetNamespace().length() > 0) + paramNamespace = webParam.targetNamespace(); + } + + BeanProperty prop = new BeanProperty(paramNamespace, paramName, paramType, genericParamType, true); + prop.getJaxbAnnotaions().addAll(jaxb); + properties.add(prop); + } + + WebResult webResult = m.getAnnotation(WebResult.class); + Class returnType = m.getReturnType(); + if (asyncMethod) { + returnType = (Class)((ParameterizedType)genericParamTypes[genericParamTypes.length-1]).getActualTypeArguments()[0]; + } + if (!((webResult != null && webResult.header()) || returnType == Void.TYPE)) { + String propName = "return"; + String propNS = ""; + + if (webResult != null) { + if (webResult.name().length() > 0) { + propName = webResult.name(); + } + if (webResult.targetNamespace().length() > 1) { + propNS = webResult.targetNamespace(); + } + } + + List jaxb = findJAXBAnnotations(m.getAnnotations()); + + Type genericReturnType = asyncMethod? returnType : m.getGenericReturnType(); + BeanProperty prop = new BeanProperty(propNS, propName, returnType, genericReturnType, true); + prop.getJaxbAnnotaions().addAll(jaxb); + // TUSCANY-3283 - As per JAXWS spec () the "return" value should come first in the + // list when there are holders. + properties.add(0, prop); + } + wrapperClass = + generate(wrapperClassDescriptor, wrapperClassSignature, wrapperNamespace, wrapperName, properties + .toArray(new BeanProperty[properties.size()]), cl); + generatedClasses.put(key, new WeakReference>(wrapperClass)); + } + return wrapperClass; + + } + } + + private static class MethodKey { + private Method m; + private boolean request; + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((m == null) ? 0 : m.hashCode()); + result = prime * result + (request ? 1231 : 1237); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + final MethodKey other = (MethodKey)obj; + if (m == null) { + if (other.m != null) + return false; + } else if (!m.equals(other.m)) + return false; + if (request != other.request) + return false; + return true; + } + + public MethodKey(Method m, boolean request) { + super(); + this.m = m; + this.request = request; + } + } + +} -- cgit v1.2.3