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