1 /*
2  * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package com.sun.xml.internal.ws.model;
27 
28 import com.sun.xml.internal.ws.model.AbstractWrapperBeanGenerator.BeanMemberFactory;
29 import com.sun.xml.internal.bind.v2.model.annotation.AnnotationReader;
30 import com.sun.xml.internal.bind.v2.model.annotation.RuntimeInlineAnnotationReader;
31 import com.sun.xml.internal.bind.v2.model.nav.Navigator;
32 import com.sun.xml.internal.ws.org.objectweb.asm.*;
33 import static com.sun.xml.internal.ws.org.objectweb.asm.Opcodes.*;
34 import com.sun.xml.internal.ws.org.objectweb.asm.Type;
35 
36 import javax.xml.bind.annotation.XmlAttachmentRef;
37 import javax.xml.bind.annotation.XmlElement;
38 import javax.xml.bind.annotation.XmlList;
39 import javax.xml.bind.annotation.XmlMimeType;
40 import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
41 import javax.xml.namespace.QName;
42 import javax.xml.ws.Holder;
43 import javax.xml.ws.WebServiceException;
44 import java.lang.annotation.Annotation;
45 import java.lang.reflect.*;
46 import java.util.*;
47 import java.util.logging.Level;
48 import java.util.logging.Logger;
49 
50 /**
51  * Runtime Wrapper and exception bean generator implementation.
52  * It uses ASM to generate request, response and exception beans.
53  *
54  * @author Jitendra Kotamraju
55  */
56 public class WrapperBeanGenerator {
57 
58     private static final Logger LOGGER = Logger.getLogger(WrapperBeanGenerator.class.getName());
59 
60     private static final FieldFactory FIELD_FACTORY = new FieldFactory();
61 
62     private static final AbstractWrapperBeanGenerator RUNTIME_GENERATOR =
63             new RuntimeWrapperBeanGenerator(new RuntimeInlineAnnotationReader(),
64                     (Navigator<java.lang.reflect.Type, Class, ?, Method>) Utils.REFLECTION_NAVIGATOR, FIELD_FACTORY);
65 
66     private static final class RuntimeWrapperBeanGenerator extends AbstractWrapperBeanGenerator<java.lang.reflect.Type, Class, java.lang.reflect.Method, Field> {
67 
RuntimeWrapperBeanGenerator(AnnotationReader<java.lang.reflect.Type, Class, ?, Method> annReader, Navigator<java.lang.reflect.Type, Class, ?, Method> nav, BeanMemberFactory<java.lang.reflect.Type, Field> beanMemberFactory)68         protected RuntimeWrapperBeanGenerator(AnnotationReader<java.lang.reflect.Type, Class, ?, Method> annReader, Navigator<java.lang.reflect.Type, Class, ?, Method> nav, BeanMemberFactory<java.lang.reflect.Type, Field> beanMemberFactory) {
69             super(annReader, nav, beanMemberFactory);
70         }
71 
72         @Override
getSafeType(java.lang.reflect.Type type)73         protected java.lang.reflect.Type getSafeType(java.lang.reflect.Type type) {
74             return type;
75         }
76 
77         @Override
getHolderValueType(java.lang.reflect.Type paramType)78         protected java.lang.reflect.Type getHolderValueType(java.lang.reflect.Type paramType) {
79             if (paramType instanceof ParameterizedType) {
80                 ParameterizedType p = (ParameterizedType)paramType;
81                 if (p.getRawType().equals(Holder.class)) {
82                     return p.getActualTypeArguments()[0];
83                 }
84             }
85             return null;
86         }
87 
88         @Override
isVoidType(java.lang.reflect.Type type)89         protected boolean isVoidType(java.lang.reflect.Type type) {
90             return type == Void.TYPE;
91         }
92 
93     }
94 
95     private static final class FieldFactory implements BeanMemberFactory<java.lang.reflect.Type, Field> {
96         @Override
createWrapperBeanMember(java.lang.reflect.Type paramType, String paramName, List<Annotation> jaxb)97         public Field createWrapperBeanMember(java.lang.reflect.Type paramType,
98                 String paramName, List<Annotation> jaxb) {
99             return new Field(paramName, paramType, getASMType(paramType), jaxb);
100         }
101     }
102 
103     // Creates class's bytes
createBeanImage(String className, String rootName, String rootNS, String typeName, String typeNS, Collection<Field> fields)104     private static byte[] createBeanImage(String className,
105                                String rootName, String rootNS,
106                                String typeName, String typeNS,
107                                Collection<Field> fields) throws Exception {
108 
109         ClassWriter cw = new ClassWriter(0);
110         //org.objectweb.asm.util.TraceClassVisitor cw = new org.objectweb.asm.util.TraceClassVisitor(actual, new java.io.PrintWriter(System.out));
111 
112         cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER, replaceDotWithSlash(className), null, "java/lang/Object", null);
113 
114         AnnotationVisitor root = cw.visitAnnotation("Ljavax/xml/bind/annotation/XmlRootElement;", true);
115         root.visit("name", rootName);
116         root.visit("namespace", rootNS);
117         root.visitEnd();
118 
119         AnnotationVisitor type = cw.visitAnnotation("Ljavax/xml/bind/annotation/XmlType;", true);
120         type.visit("name", typeName);
121         type.visit("namespace", typeNS);
122         if (fields.size() > 1) {
123             AnnotationVisitor propVisitor = type.visitArray("propOrder");
124             for(Field field : fields) {
125                 propVisitor.visit("propOrder", field.fieldName);
126             }
127             propVisitor.visitEnd();
128         }
129         type.visitEnd();
130 
131         for(Field field : fields) {
132             FieldVisitor fv = cw.visitField(ACC_PUBLIC, field.fieldName, field.asmType.getDescriptor(), field.getSignature(), null);
133 
134             for(Annotation ann : field.jaxbAnnotations) {
135                 if (ann instanceof XmlMimeType) {
136                     AnnotationVisitor mime = fv.visitAnnotation("Ljavax/xml/bind/annotation/XmlMimeType;", true);
137                     mime.visit("value", ((XmlMimeType)ann).value());
138                     mime.visitEnd();
139                 } else if (ann instanceof XmlJavaTypeAdapter) {
140                     AnnotationVisitor ada = fv.visitAnnotation("Ljavax/xml/bind/annotation/adapters/XmlJavaTypeAdapter;", true);
141                     ada.visit("value", getASMType(((XmlJavaTypeAdapter)ann).value()));
142                     // XmlJavaTypeAdapter.type() is for package only. No need to copy.
143                     // ada.visit("type", ((XmlJavaTypeAdapter)ann).type());
144                     ada.visitEnd();
145                 } else if (ann instanceof XmlAttachmentRef) {
146                     AnnotationVisitor att = fv.visitAnnotation("Ljavax/xml/bind/annotation/XmlAttachmentRef;", true);
147                     att.visitEnd();
148                 } else if (ann instanceof XmlList) {
149                     AnnotationVisitor list = fv.visitAnnotation("Ljavax/xml/bind/annotation/XmlList;", true);
150                     list.visitEnd();
151                 } else if (ann instanceof XmlElement) {
152                     AnnotationVisitor elem = fv.visitAnnotation("Ljavax/xml/bind/annotation/XmlElement;", true);
153                     XmlElement xmlElem = (XmlElement)ann;
154                     elem.visit("name", xmlElem.name());
155                     elem.visit("namespace", xmlElem.namespace());
156                     if (xmlElem.nillable()) {
157                         elem.visit("nillable", true);
158                     }
159                     if (xmlElem.required()) {
160                         elem.visit("required", true);
161                     }
162                     elem.visitEnd();
163                 } else {
164                     throw new WebServiceException("Unknown JAXB annotation " + ann);
165                 }
166             }
167 
168             fv.visitEnd();
169         }
170 
171         MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
172         mv.visitCode();
173         mv.visitVarInsn(ALOAD, 0);
174         mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
175         mv.visitInsn(RETURN);
176         mv.visitMaxs(1, 1);
177         mv.visitEnd();
178 
179         cw.visitEnd();
180 
181         if (LOGGER.isLoggable(Level.FINE)) {
182             // Class's @XmlRootElement
183             StringBuilder sb = new StringBuilder();
184             sb.append("\n");
185             sb.append("@XmlRootElement(name=").append(rootName)
186                     .append(", namespace=").append(rootNS).append(")");
187 
188             // Class's @XmlType
189             sb.append("\n");
190             sb.append("@XmlType(name=").append(typeName)
191                     .append(", namespace=").append(typeNS);
192             if (fields.size() > 1) {
193                 sb.append(", propOrder={");
194                 for(Field field : fields) {
195                     sb.append(" ");
196                     sb.append(field.fieldName);
197                 }
198                 sb.append(" }");
199             }
200             sb.append(")");
201 
202             // class declaration
203             sb.append("\n");
204             sb.append("public class ").append(className).append(" {");
205 
206             // fields declaration
207             for(Field field : fields) {
208                 sb.append("\n");
209 
210                 // Field's other JAXB annotations
211                 for(Annotation ann : field.jaxbAnnotations) {
212                     sb.append("\n    ");
213 
214                     if (ann instanceof XmlMimeType) {
215                         sb.append("@XmlMimeType(value=").append(((XmlMimeType)ann).value()).append(")");
216                     } else if (ann instanceof XmlJavaTypeAdapter) {
217                         sb.append("@XmlJavaTypeAdapter(value=").append(getASMType(((XmlJavaTypeAdapter)ann).value())).append(")");
218                     } else if (ann instanceof XmlAttachmentRef) {
219                         sb.append("@XmlAttachmentRef");
220                     } else if (ann instanceof XmlList) {
221                         sb.append("@XmlList");
222                     } else if (ann instanceof XmlElement) {
223                         XmlElement xmlElem = (XmlElement)ann;
224                         sb.append("\n    ");
225                         sb.append("@XmlElement(name=").append(xmlElem.name())
226                                 .append(", namespace=").append(xmlElem.namespace());
227                         if (xmlElem.nillable()) {
228                             sb.append(", nillable=true");
229                         }
230                         if (xmlElem.required()) {
231                             sb.append(", required=true");
232                         }
233                         sb.append(")");
234                     } else {
235                         throw new WebServiceException("Unknown JAXB annotation " + ann);
236                     }
237                 }
238 
239                 // Field declaration
240                 sb.append("\n    ");
241                 sb.append("public ");
242                 if (field.getSignature() == null) {
243                     sb.append(field.asmType.getDescriptor());
244                 } else {
245                     sb.append(field.getSignature());
246                 }
247                 sb.append(" ");
248                 sb.append(field.fieldName);
249             }
250 
251             sb.append("\n\n}");
252             LOGGER.fine(sb.toString());
253         }
254 
255         return cw.toByteArray();
256     }
257 
replaceDotWithSlash(String name)258     private static String replaceDotWithSlash(String name) {
259         return name.replace('.', '/');
260     }
261 
createRequestWrapperBean(String className, Method method, QName reqElemName, ClassLoader cl)262     static Class createRequestWrapperBean(String className, Method method, QName reqElemName, ClassLoader cl) {
263 
264         if (LOGGER.isLoggable(Level.FINE)) {
265             LOGGER.log(Level.FINE, "Request Wrapper Class : {0}", className);
266         }
267 
268         List<Field> requestMembers = RUNTIME_GENERATOR.collectRequestBeanMembers(
269                 method);
270 
271         byte[] image;
272         try {
273             image = createBeanImage(className, reqElemName.getLocalPart(), reqElemName.getNamespaceURI(),
274                 reqElemName.getLocalPart(), reqElemName.getNamespaceURI(),
275                 requestMembers);
276         } catch(Exception e) {
277             throw new WebServiceException(e);
278         }
279 //        write(image, className);
280         return Injector.inject(cl, className, image);
281     }
282 
createResponseWrapperBean(String className, Method method, QName resElemName, ClassLoader cl)283     static Class createResponseWrapperBean(String className, Method method, QName resElemName, ClassLoader cl) {
284 
285         if (LOGGER.isLoggable(Level.FINE)) {
286             LOGGER.log(Level.FINE, "Response Wrapper Class : {0}", className);
287         }
288 
289         List<Field> responseMembers = RUNTIME_GENERATOR.collectResponseBeanMembers(method);
290 
291         byte[] image;
292         try {
293             image = createBeanImage(className, resElemName.getLocalPart(), resElemName.getNamespaceURI(),
294                 resElemName.getLocalPart(), resElemName.getNamespaceURI(),
295                 responseMembers);
296         } catch(Exception e) {
297             throw new WebServiceException(e);
298         }
299 //      write(image, className);
300 
301         return Injector.inject(cl, className, image);
302     }
303 
304 
getASMType(java.lang.reflect.Type t)305     private static Type getASMType(java.lang.reflect.Type t) {
306         assert t!=null;
307 
308         if (t instanceof Class) {
309             return Type.getType((Class)t);
310         }
311 
312         if (t instanceof ParameterizedType) {
313             ParameterizedType pt = (ParameterizedType)t;
314             if (pt.getRawType() instanceof Class) {
315                 return Type.getType((Class)pt.getRawType());
316             }
317         }
318         if (t instanceof GenericArrayType) {
319             return Type.getType(FieldSignature.vms(t));
320         }
321 
322         if (t instanceof WildcardType) {
323             return Type.getType(FieldSignature.vms(t));
324         }
325 
326         if (t instanceof TypeVariable) {
327             TypeVariable tv = (TypeVariable)t;
328             if (tv.getBounds()[0] instanceof Class) {
329                 return Type.getType((Class)tv.getBounds()[0]);
330             }
331         }
332 
333         throw new IllegalArgumentException("Not creating ASM Type for type = "+t);
334     }
335 
336 
createExceptionBean(String className, Class exception, String typeNS, String elemName, String elemNS, ClassLoader cl)337     static Class createExceptionBean(String className, Class exception, String typeNS, String elemName, String elemNS, ClassLoader cl) {
338         return createExceptionBean(className, exception, typeNS, elemName, elemNS, cl, true);
339     }
340 
createExceptionBean(String className, Class exception, String typeNS, String elemName, String elemNS, ClassLoader cl, boolean decapitalizeExceptionBeanProperties)341     static Class createExceptionBean(String className, Class exception, String typeNS, String elemName, String elemNS, ClassLoader cl, boolean decapitalizeExceptionBeanProperties) {
342 
343         Collection<Field> fields = RUNTIME_GENERATOR.collectExceptionBeanMembers(exception, decapitalizeExceptionBeanProperties);
344 
345         byte[] image;
346         try {
347             image = createBeanImage(className, elemName, elemNS,
348                 exception.getSimpleName(), typeNS,
349                 fields);
350         } catch(Exception e) {
351             throw new WebServiceException(e);
352         }
353 
354         return Injector.inject(cl, className, image);
355     }
356 
357     /**
358      * Note: this class has a natural ordering that is inconsistent with equals.
359      */
360     private static class Field implements Comparable<Field> {
361         private final java.lang.reflect.Type reflectType;
362         private final Type asmType;
363         private final String fieldName;
364         private final List<Annotation> jaxbAnnotations;
365 
Field(String paramName, java.lang.reflect.Type paramType, Type asmType, List<Annotation> jaxbAnnotations)366         Field(String paramName, java.lang.reflect.Type paramType, Type asmType,
367               List<Annotation> jaxbAnnotations) {
368             this.reflectType = paramType;
369             this.asmType = asmType;
370             this.fieldName = paramName;
371             this.jaxbAnnotations = jaxbAnnotations;
372         }
373 
getSignature()374         String getSignature() {
375             if (reflectType instanceof Class) {
376                 return null;
377             }
378             if (reflectType instanceof TypeVariable) {
379                 return null;
380             }
381             return FieldSignature.vms(reflectType);
382         }
383 
384         @Override
compareTo(Field o)385         public int compareTo(Field o) {
386             return fieldName.compareTo(o.fieldName);
387         }
388     }
389 
write(byte[] b, String className)390     static void write(byte[] b, String className) {
391         className = className.substring(className.lastIndexOf(".")+1);
392         try {
393             java.io.FileOutputStream fo = new java.io.FileOutputStream(className + ".class");
394             fo.write(b);
395             fo.flush();
396             fo.close();
397         } catch (java.io.IOException e) {
398             LOGGER.log(Level.INFO, "Error Writing class", e);
399         }
400     }
401 
402 }
403