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 }