aoqi@0: /* aoqi@0: * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. aoqi@0: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. aoqi@0: * aoqi@0: * This code is free software; you can redistribute it and/or modify it aoqi@0: * under the terms of the GNU General Public License version 2 only, as aoqi@0: * published by the Free Software Foundation. Oracle designates this aoqi@0: * particular file as subject to the "Classpath" exception as provided aoqi@0: * by Oracle in the LICENSE file that accompanied this code. aoqi@0: * aoqi@0: * This code is distributed in the hope that it will be useful, but WITHOUT aoqi@0: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or aoqi@0: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License aoqi@0: * version 2 for more details (a copy is included in the LICENSE file that aoqi@0: * accompanied this code). aoqi@0: * aoqi@0: * You should have received a copy of the GNU General Public License version aoqi@0: * 2 along with this work; if not, write to the Free Software Foundation, aoqi@0: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. aoqi@0: * aoqi@0: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA aoqi@0: * or visit www.oracle.com if you need additional information or have any aoqi@0: * questions. aoqi@0: */ aoqi@0: aoqi@0: package com.sun.xml.internal.ws.model; aoqi@0: aoqi@0: import com.sun.xml.internal.ws.model.AbstractWrapperBeanGenerator.BeanMemberFactory; aoqi@0: import com.sun.xml.internal.bind.v2.model.annotation.AnnotationReader; aoqi@0: import com.sun.xml.internal.bind.v2.model.annotation.RuntimeInlineAnnotationReader; aoqi@0: import com.sun.xml.internal.bind.v2.model.nav.Navigator; aoqi@0: import com.sun.xml.internal.ws.org.objectweb.asm.*; aoqi@0: import static com.sun.xml.internal.ws.org.objectweb.asm.Opcodes.*; aoqi@0: import com.sun.xml.internal.ws.org.objectweb.asm.Type; aoqi@0: aoqi@0: import javax.xml.bind.annotation.XmlAttachmentRef; aoqi@0: import javax.xml.bind.annotation.XmlElement; aoqi@0: import javax.xml.bind.annotation.XmlList; aoqi@0: import javax.xml.bind.annotation.XmlMimeType; aoqi@0: import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; aoqi@0: import javax.xml.namespace.QName; aoqi@0: import javax.xml.ws.Holder; aoqi@0: import javax.xml.ws.WebServiceException; aoqi@0: import java.lang.annotation.Annotation; aoqi@0: import java.lang.reflect.*; aoqi@0: import java.util.*; aoqi@0: import java.util.logging.Level; aoqi@0: import java.util.logging.Logger; aoqi@0: aoqi@0: /** aoqi@0: * Runtime Wrapper and exception bean generator implementation. aoqi@0: * It uses ASM to generate request, response and exception beans. aoqi@0: * aoqi@0: * @author Jitendra Kotamraju aoqi@0: */ aoqi@0: public class WrapperBeanGenerator { aoqi@0: aoqi@0: private static final Logger LOGGER = Logger.getLogger(WrapperBeanGenerator.class.getName()); aoqi@0: aoqi@0: private static final FieldFactory FIELD_FACTORY = new FieldFactory(); aoqi@0: aoqi@0: private static final AbstractWrapperBeanGenerator RUNTIME_GENERATOR = aoqi@0: new RuntimeWrapperBeanGenerator(new RuntimeInlineAnnotationReader(), aoqi@0: (Navigator) Utils.REFLECTION_NAVIGATOR, FIELD_FACTORY); aoqi@0: aoqi@0: private static final class RuntimeWrapperBeanGenerator extends AbstractWrapperBeanGenerator { aoqi@0: aoqi@0: protected RuntimeWrapperBeanGenerator(AnnotationReader annReader, Navigator nav, BeanMemberFactory beanMemberFactory) { aoqi@0: super(annReader, nav, beanMemberFactory); aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: protected java.lang.reflect.Type getSafeType(java.lang.reflect.Type type) { aoqi@0: return type; aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: protected java.lang.reflect.Type getHolderValueType(java.lang.reflect.Type paramType) { aoqi@0: if (paramType instanceof ParameterizedType) { aoqi@0: ParameterizedType p = (ParameterizedType)paramType; aoqi@0: if (p.getRawType().equals(Holder.class)) { aoqi@0: return p.getActualTypeArguments()[0]; aoqi@0: } aoqi@0: } aoqi@0: return null; aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: protected boolean isVoidType(java.lang.reflect.Type type) { aoqi@0: return type == Void.TYPE; aoqi@0: } aoqi@0: aoqi@0: } aoqi@0: aoqi@0: private static final class FieldFactory implements BeanMemberFactory { aoqi@0: @Override aoqi@0: public Field createWrapperBeanMember(java.lang.reflect.Type paramType, aoqi@0: String paramName, List jaxb) { aoqi@0: return new Field(paramName, paramType, getASMType(paramType), jaxb); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: // Creates class's bytes aoqi@0: private static byte[] createBeanImage(String className, aoqi@0: String rootName, String rootNS, aoqi@0: String typeName, String typeNS, aoqi@0: Collection fields) throws Exception { aoqi@0: aoqi@0: ClassWriter cw = new ClassWriter(0); aoqi@0: //org.objectweb.asm.util.TraceClassVisitor cw = new org.objectweb.asm.util.TraceClassVisitor(actual, new java.io.PrintWriter(System.out)); aoqi@0: aoqi@0: cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER, replaceDotWithSlash(className), null, "java/lang/Object", null); aoqi@0: aoqi@0: AnnotationVisitor root = cw.visitAnnotation("Ljavax/xml/bind/annotation/XmlRootElement;", true); aoqi@0: root.visit("name", rootName); aoqi@0: root.visit("namespace", rootNS); aoqi@0: root.visitEnd(); aoqi@0: aoqi@0: AnnotationVisitor type = cw.visitAnnotation("Ljavax/xml/bind/annotation/XmlType;", true); aoqi@0: type.visit("name", typeName); aoqi@0: type.visit("namespace", typeNS); aoqi@0: if (fields.size() > 1) { aoqi@0: AnnotationVisitor propVisitor = type.visitArray("propOrder"); aoqi@0: for(Field field : fields) { aoqi@0: propVisitor.visit("propOrder", field.fieldName); aoqi@0: } aoqi@0: propVisitor.visitEnd(); aoqi@0: } aoqi@0: type.visitEnd(); aoqi@0: aoqi@0: for(Field field : fields) { aoqi@0: FieldVisitor fv = cw.visitField(ACC_PUBLIC, field.fieldName, field.asmType.getDescriptor(), field.getSignature(), null); aoqi@0: aoqi@0: for(Annotation ann : field.jaxbAnnotations) { aoqi@0: if (ann instanceof XmlMimeType) { aoqi@0: AnnotationVisitor mime = fv.visitAnnotation("Ljavax/xml/bind/annotation/XmlMimeType;", true); aoqi@0: mime.visit("value", ((XmlMimeType)ann).value()); aoqi@0: mime.visitEnd(); aoqi@0: } else if (ann instanceof XmlJavaTypeAdapter) { aoqi@0: AnnotationVisitor ada = fv.visitAnnotation("Ljavax/xml/bind/annotation/adapters/XmlJavaTypeAdapter;", true); aoqi@0: ada.visit("value", getASMType(((XmlJavaTypeAdapter)ann).value())); aoqi@0: // XmlJavaTypeAdapter.type() is for package only. No need to copy. aoqi@0: // ada.visit("type", ((XmlJavaTypeAdapter)ann).type()); aoqi@0: ada.visitEnd(); aoqi@0: } else if (ann instanceof XmlAttachmentRef) { aoqi@0: AnnotationVisitor att = fv.visitAnnotation("Ljavax/xml/bind/annotation/XmlAttachmentRef;", true); aoqi@0: att.visitEnd(); aoqi@0: } else if (ann instanceof XmlList) { aoqi@0: AnnotationVisitor list = fv.visitAnnotation("Ljavax/xml/bind/annotation/XmlList;", true); aoqi@0: list.visitEnd(); aoqi@0: } else if (ann instanceof XmlElement) { aoqi@0: AnnotationVisitor elem = fv.visitAnnotation("Ljavax/xml/bind/annotation/XmlElement;", true); aoqi@0: XmlElement xmlElem = (XmlElement)ann; aoqi@0: elem.visit("name", xmlElem.name()); aoqi@0: elem.visit("namespace", xmlElem.namespace()); aoqi@0: if (xmlElem.nillable()) { aoqi@0: elem.visit("nillable", true); aoqi@0: } aoqi@0: if (xmlElem.required()) { aoqi@0: elem.visit("required", true); aoqi@0: } aoqi@0: elem.visitEnd(); aoqi@0: } else { aoqi@0: throw new WebServiceException("Unknown JAXB annotation " + ann); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: fv.visitEnd(); aoqi@0: } aoqi@0: aoqi@0: MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "", "()V", null, null); aoqi@0: mv.visitCode(); aoqi@0: mv.visitVarInsn(ALOAD, 0); aoqi@0: mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V"); aoqi@0: mv.visitInsn(RETURN); aoqi@0: mv.visitMaxs(1, 1); aoqi@0: mv.visitEnd(); aoqi@0: aoqi@0: cw.visitEnd(); aoqi@0: aoqi@0: if (LOGGER.isLoggable(Level.FINE)) { aoqi@0: // Class's @XmlRootElement aoqi@0: StringBuilder sb = new StringBuilder(); aoqi@0: sb.append("\n"); aoqi@0: sb.append("@XmlRootElement(name=").append(rootName) aoqi@0: .append(", namespace=").append(rootNS).append(")"); aoqi@0: aoqi@0: // Class's @XmlType aoqi@0: sb.append("\n"); aoqi@0: sb.append("@XmlType(name=").append(typeName) aoqi@0: .append(", namespace=").append(typeNS); aoqi@0: if (fields.size() > 1) { aoqi@0: sb.append(", propOrder={"); aoqi@0: for(Field field : fields) { aoqi@0: sb.append(" "); aoqi@0: sb.append(field.fieldName); aoqi@0: } aoqi@0: sb.append(" }"); aoqi@0: } aoqi@0: sb.append(")"); aoqi@0: aoqi@0: // class declaration aoqi@0: sb.append("\n"); aoqi@0: sb.append("public class ").append(className).append(" {"); aoqi@0: aoqi@0: // fields declaration aoqi@0: for(Field field : fields) { aoqi@0: sb.append("\n"); aoqi@0: aoqi@0: // Field's other JAXB annotations aoqi@0: for(Annotation ann : field.jaxbAnnotations) { aoqi@0: sb.append("\n "); aoqi@0: aoqi@0: if (ann instanceof XmlMimeType) { aoqi@0: sb.append("@XmlMimeType(value=").append(((XmlMimeType)ann).value()).append(")"); aoqi@0: } else if (ann instanceof XmlJavaTypeAdapter) { aoqi@0: sb.append("@XmlJavaTypeAdapter(value=").append(getASMType(((XmlJavaTypeAdapter)ann).value())).append(")"); aoqi@0: } else if (ann instanceof XmlAttachmentRef) { aoqi@0: sb.append("@XmlAttachmentRef"); aoqi@0: } else if (ann instanceof XmlList) { aoqi@0: sb.append("@XmlList"); aoqi@0: } else if (ann instanceof XmlElement) { aoqi@0: XmlElement xmlElem = (XmlElement)ann; aoqi@0: sb.append("\n "); aoqi@0: sb.append("@XmlElement(name=").append(xmlElem.name()) aoqi@0: .append(", namespace=").append(xmlElem.namespace()); aoqi@0: if (xmlElem.nillable()) { aoqi@0: sb.append(", nillable=true"); aoqi@0: } aoqi@0: if (xmlElem.required()) { aoqi@0: sb.append(", required=true"); aoqi@0: } aoqi@0: sb.append(")"); aoqi@0: } else { aoqi@0: throw new WebServiceException("Unknown JAXB annotation " + ann); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: // Field declaration aoqi@0: sb.append("\n "); aoqi@0: sb.append("public "); aoqi@0: if (field.getSignature() == null) { aoqi@0: sb.append(field.asmType.getDescriptor()); aoqi@0: } else { aoqi@0: sb.append(field.getSignature()); aoqi@0: } aoqi@0: sb.append(" "); aoqi@0: sb.append(field.fieldName); aoqi@0: } aoqi@0: aoqi@0: sb.append("\n\n}"); aoqi@0: LOGGER.fine(sb.toString()); aoqi@0: } aoqi@0: aoqi@0: return cw.toByteArray(); aoqi@0: } aoqi@0: aoqi@0: private static String replaceDotWithSlash(String name) { aoqi@0: return name.replace('.', '/'); aoqi@0: } aoqi@0: aoqi@0: static Class createRequestWrapperBean(String className, Method method, QName reqElemName, ClassLoader cl) { aoqi@0: aoqi@0: if (LOGGER.isLoggable(Level.FINE)) { aoqi@0: LOGGER.log(Level.FINE, "Request Wrapper Class : {0}", className); aoqi@0: } aoqi@0: aoqi@0: List requestMembers = RUNTIME_GENERATOR.collectRequestBeanMembers( aoqi@0: method); aoqi@0: aoqi@0: byte[] image; aoqi@0: try { aoqi@0: image = createBeanImage(className, reqElemName.getLocalPart(), reqElemName.getNamespaceURI(), aoqi@0: reqElemName.getLocalPart(), reqElemName.getNamespaceURI(), aoqi@0: requestMembers); aoqi@0: } catch(Exception e) { aoqi@0: throw new WebServiceException(e); aoqi@0: } aoqi@0: // write(image, className); aoqi@0: return Injector.inject(cl, className, image); aoqi@0: } aoqi@0: aoqi@0: static Class createResponseWrapperBean(String className, Method method, QName resElemName, ClassLoader cl) { aoqi@0: aoqi@0: if (LOGGER.isLoggable(Level.FINE)) { aoqi@0: LOGGER.log(Level.FINE, "Response Wrapper Class : {0}", className); aoqi@0: } aoqi@0: aoqi@0: List responseMembers = RUNTIME_GENERATOR.collectResponseBeanMembers(method); aoqi@0: aoqi@0: byte[] image; aoqi@0: try { aoqi@0: image = createBeanImage(className, resElemName.getLocalPart(), resElemName.getNamespaceURI(), aoqi@0: resElemName.getLocalPart(), resElemName.getNamespaceURI(), aoqi@0: responseMembers); aoqi@0: } catch(Exception e) { aoqi@0: throw new WebServiceException(e); aoqi@0: } aoqi@0: // write(image, className); aoqi@0: aoqi@0: return Injector.inject(cl, className, image); aoqi@0: } aoqi@0: aoqi@0: aoqi@0: private static Type getASMType(java.lang.reflect.Type t) { aoqi@0: assert t!=null; aoqi@0: aoqi@0: if (t instanceof Class) { aoqi@0: return Type.getType((Class)t); aoqi@0: } aoqi@0: aoqi@0: if (t instanceof ParameterizedType) { aoqi@0: ParameterizedType pt = (ParameterizedType)t; aoqi@0: if (pt.getRawType() instanceof Class) { aoqi@0: return Type.getType((Class)pt.getRawType()); aoqi@0: } aoqi@0: } aoqi@0: if (t instanceof GenericArrayType) { aoqi@0: return Type.getType(FieldSignature.vms(t)); aoqi@0: } aoqi@0: aoqi@0: if (t instanceof WildcardType) { aoqi@0: return Type.getType(FieldSignature.vms(t)); aoqi@0: } aoqi@0: aoqi@0: if (t instanceof TypeVariable) { aoqi@0: TypeVariable tv = (TypeVariable)t; aoqi@0: if (tv.getBounds()[0] instanceof Class) { aoqi@0: return Type.getType((Class)tv.getBounds()[0]); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: throw new IllegalArgumentException("Not creating ASM Type for type = "+t); aoqi@0: } aoqi@0: aoqi@0: aoqi@0: static Class createExceptionBean(String className, Class exception, String typeNS, String elemName, String elemNS, ClassLoader cl) { aoqi@0: return createExceptionBean(className, exception, typeNS, elemName, elemNS, cl, true); aoqi@0: } aoqi@0: aoqi@0: static Class createExceptionBean(String className, Class exception, String typeNS, String elemName, String elemNS, ClassLoader cl, boolean decapitalizeExceptionBeanProperties) { aoqi@0: aoqi@0: Collection fields = RUNTIME_GENERATOR.collectExceptionBeanMembers(exception, decapitalizeExceptionBeanProperties); aoqi@0: aoqi@0: byte[] image; aoqi@0: try { aoqi@0: image = createBeanImage(className, elemName, elemNS, aoqi@0: exception.getSimpleName(), typeNS, aoqi@0: fields); aoqi@0: } catch(Exception e) { aoqi@0: throw new WebServiceException(e); aoqi@0: } aoqi@0: aoqi@0: return Injector.inject(cl, className, image); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Note: this class has a natural ordering that is inconsistent with equals. aoqi@0: */ aoqi@0: private static class Field implements Comparable { aoqi@0: private final java.lang.reflect.Type reflectType; aoqi@0: private final Type asmType; aoqi@0: private final String fieldName; aoqi@0: private final List jaxbAnnotations; aoqi@0: aoqi@0: Field(String paramName, java.lang.reflect.Type paramType, Type asmType, aoqi@0: List jaxbAnnotations) { aoqi@0: this.reflectType = paramType; aoqi@0: this.asmType = asmType; aoqi@0: this.fieldName = paramName; aoqi@0: this.jaxbAnnotations = jaxbAnnotations; aoqi@0: } aoqi@0: aoqi@0: String getSignature() { aoqi@0: if (reflectType instanceof Class) { aoqi@0: return null; aoqi@0: } aoqi@0: if (reflectType instanceof TypeVariable) { aoqi@0: return null; aoqi@0: } aoqi@0: return FieldSignature.vms(reflectType); aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: public int compareTo(Field o) { aoqi@0: return fieldName.compareTo(o.fieldName); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: static void write(byte[] b, String className) { aoqi@0: className = className.substring(className.lastIndexOf(".")+1); aoqi@0: try { aoqi@0: java.io.FileOutputStream fo = new java.io.FileOutputStream(className + ".class"); aoqi@0: fo.write(b); aoqi@0: fo.flush(); aoqi@0: fo.close(); aoqi@0: } catch (java.io.IOException e) { aoqi@0: LOGGER.log(Level.INFO, "Error Writing class", e); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: }