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.REFLECTION, FIELD_FACTORY);
  65 
  66     private static final class RuntimeWrapperBeanGenerator extends AbstractWrapperBeanGenerator<java.lang.reflect.Type, Class, java.lang.reflect.Method, Field> {
  67 
  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
  73         protected java.lang.reflect.Type getSafeType(java.lang.reflect.Type type) {
  74             return type;
  75         }
  76 
  77         @Override
  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
  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
  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
 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 
 258     private static String replaceDotWithSlash(String name) {
 259         return name.replace('.', '/');
 260     }
 261 
 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 
 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 
 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 
 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 
 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 
 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 
 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
 385         public int compareTo(Field o) {
 386             return fieldName.compareTo(o.fieldName);
 387         }
 388     }
 389 
 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 }