001: /**
002: * Licensed to the Apache Software Foundation (ASF) under one
003: * or more contributor license agreements. See the NOTICE file
004: * distributed with this work for additional information
005: * regarding copyright ownership. The ASF licenses this file
006: * to you under the Apache License, Version 2.0 (the
007: * "License"); you may not use this file except in compliance
008: * with the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing,
013: * software distributed under the License is distributed on an
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015: * KIND, either express or implied. See the License for the
016: * specific language governing permissions and limitations
017: * under the License.
018: */package org.apache.cxf.jaxws.interceptors;
019:
020: import java.lang.reflect.Field;
021: import java.lang.reflect.Method;
022: import java.util.Collection;
023: import java.util.HashMap;
024: import java.util.Map;
025:
026: import javax.xml.bind.JAXBElement;
027:
028: import org.objectweb.asm.ClassWriter;
029: import org.objectweb.asm.FieldVisitor;
030: import org.objectweb.asm.Label;
031: import org.objectweb.asm.MethodVisitor;
032: import org.objectweb.asm.Opcodes;
033:
034: final class WrapperHelperCompiler {
035: private static final Map<Class<?>, String> PRIMITIVE_MAP = new HashMap<Class<?>, String>();
036: private static final Map<Class<?>, String> NONPRIMITIVE_MAP = new HashMap<Class<?>, String>();
037: static {
038: PRIMITIVE_MAP.put(Byte.TYPE, "B");
039: PRIMITIVE_MAP.put(Boolean.TYPE, "Z");
040: PRIMITIVE_MAP.put(Long.TYPE, "J");
041: PRIMITIVE_MAP.put(Integer.TYPE, "I");
042: PRIMITIVE_MAP.put(Short.TYPE, "S");
043: PRIMITIVE_MAP.put(Character.TYPE, "C");
044: PRIMITIVE_MAP.put(Float.TYPE, "F");
045: PRIMITIVE_MAP.put(Double.TYPE, "D");
046:
047: NONPRIMITIVE_MAP.put(Byte.TYPE, Byte.class.getName()
048: .replaceAll("\\.", "/"));
049: NONPRIMITIVE_MAP.put(Boolean.TYPE, Boolean.class.getName()
050: .replaceAll("\\.", "/"));
051: NONPRIMITIVE_MAP.put(Long.TYPE, Long.class.getName()
052: .replaceAll("\\.", "/"));
053: NONPRIMITIVE_MAP.put(Integer.TYPE, Integer.class.getName()
054: .replaceAll("\\.", "/"));
055: NONPRIMITIVE_MAP.put(Short.TYPE, Short.class.getName()
056: .replaceAll("\\.", "/"));
057: NONPRIMITIVE_MAP.put(Character.TYPE, Character.class.getName()
058: .replaceAll("\\.", "/"));
059: NONPRIMITIVE_MAP.put(Float.TYPE, Float.class.getName()
060: .replaceAll("\\.", "/"));
061: NONPRIMITIVE_MAP.put(Double.TYPE, Double.class.getName()
062: .replaceAll("\\.", "/"));
063: }
064:
065: final Class<?> wrapperType;
066: final Method setMethods[];
067: final Method getMethods[];
068: final Method jaxbMethods[];
069: final Field fields[];
070: final Object objectFactory;
071: final ClassWriter cw;
072:
073: private WrapperHelperCompiler(Class<?> wrapperType,
074: Method setMethods[], Method getMethods[],
075: Method jaxbMethods[], Field fields[], Object objectFactory) {
076: this .wrapperType = wrapperType;
077: this .setMethods = setMethods;
078: this .getMethods = getMethods;
079: this .jaxbMethods = jaxbMethods;
080: this .fields = fields;
081: this .objectFactory = objectFactory;
082:
083: cw = new ClassWriter(ClassWriter.COMPUTE_MAXS
084: | ClassWriter.COMPUTE_FRAMES);
085: }
086:
087: static WrapperHelper compileWrapperHelper(Class<?> wrapperType,
088: Method setMethods[], Method getMethods[],
089: Method jaxbMethods[], Field fields[], Object objectFactory) {
090: return new WrapperHelperCompiler(wrapperType, setMethods,
091: getMethods, jaxbMethods, fields, objectFactory)
092: .compile();
093: }
094:
095: public WrapperHelper compile() {
096: String newClassName = wrapperType.getName()
097: + "_WrapperTypeHelper";
098: newClassName = newClassName.replaceAll("\\$", ".");
099: newClassName = periodToSlashes(newClassName);
100: cw.visit(Opcodes.V1_5, Opcodes.ACC_PUBLIC | Opcodes.ACC_SUPER,
101: newClassName, null, periodToSlashes(WrapperHelper.class
102: .getName()), null);
103:
104: addConstructor(newClassName, cw, objectFactory == null ? null
105: : objectFactory.getClass());
106: boolean b = addCreateWrapperObject(newClassName,
107: objectFactory == null ? null : objectFactory.getClass());
108: if (b) {
109: b = addGetWrapperParts(newClassName, wrapperType,
110: getMethods, fields, cw);
111: }
112:
113: try {
114: if (b) {
115: cw.visitEnd();
116: byte bt[] = cw.toByteArray();
117: Class<?> cl = new TypeHelperClassLoader(wrapperType
118: .getClassLoader()).defineClass(newClassName
119: .replaceAll("/", "."), bt);
120:
121: Object o = cl.newInstance();
122: return WrapperHelper.class.cast(o);
123: }
124: } catch (Exception e) {
125: //ignore, we'll just fall down to reflection based
126: }
127: return null;
128: }
129:
130: private static class TypeHelperClassLoader extends ClassLoader {
131: TypeHelperClassLoader(ClassLoader parent) {
132: super (parent);
133: }
134:
135: public Class<?> defineClass(String name, byte bytes[]) {
136: return super .defineClass(name, bytes, 0, bytes.length);
137: }
138: }
139:
140: private static void addConstructor(String newClassName,
141: ClassWriter cw, Class<?> objectFactory) {
142:
143: if (objectFactory != null) {
144: String ofName = "L"
145: + periodToSlashes(objectFactory.getName()) + ";";
146: FieldVisitor fv = cw.visitField(0, "factory", ofName, null,
147: null);
148: fv.visitEnd();
149: }
150:
151: MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>",
152: "()V", null, null);
153: mv.visitCode();
154: Label l0 = new Label();
155: mv.visitLabel(l0);
156:
157: mv.visitVarInsn(Opcodes.ALOAD, 0);
158: mv.visitMethodInsn(Opcodes.INVOKESPECIAL,
159: periodToSlashes(WrapperHelper.class.getName()),
160: "<init>", "()V");
161: if (objectFactory != null) {
162: mv.visitVarInsn(Opcodes.ALOAD, 0);
163: mv.visitTypeInsn(Opcodes.NEW, periodToSlashes(objectFactory
164: .getName()));
165: mv.visitInsn(Opcodes.DUP);
166: mv.visitMethodInsn(Opcodes.INVOKESPECIAL,
167: periodToSlashes(objectFactory.getName()), "<init>",
168: "()V");
169: mv.visitFieldInsn(Opcodes.PUTFIELD,
170: periodToSlashes(newClassName), "factory", "L"
171: + periodToSlashes(objectFactory.getName())
172: + ";");
173: }
174:
175: mv.visitInsn(Opcodes.RETURN);
176:
177: Label l1 = new Label();
178: mv.visitLabel(l1);
179: mv.visitLocalVariable("this", "L" + newClassName + ";", null,
180: l0, l1, 0);
181: mv.visitMaxs(0, 0);
182: mv.visitEnd();
183: }
184:
185: private boolean addCreateWrapperObject(String newClassName,
186: Class<?> objectFactoryClass) {
187:
188: MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC,
189: "createWrapperObject",
190: "(Ljava/util/List;)Ljava/lang/Object;",
191: "(Ljava/util/List<*>;)Ljava/lang/Object;",
192: new String[] { "org/apache/cxf/interceptor/Fault" });
193: mv.visitCode();
194: Label lBegin = new Label();
195: mv.visitLabel(lBegin);
196:
197: mv.visitTypeInsn(Opcodes.NEW, periodToSlashes(wrapperType
198: .getName()));
199: mv.visitInsn(Opcodes.DUP);
200: mv
201: .visitMethodInsn(Opcodes.INVOKESPECIAL,
202: periodToSlashes(wrapperType.getName()),
203: "<init>", "()V");
204: mv.visitVarInsn(Opcodes.ASTORE, 2);
205:
206: for (int x = 0; x < setMethods.length; x++) {
207: if (getMethods[x] == null) {
208: if (setMethods[x] == null && fields[x] == null) {
209: //null placeholder, just skip it
210: continue;
211: } else {
212: return false;
213: }
214: }
215: Class<?> tp = getMethods[x].getReturnType();
216: mv.visitVarInsn(Opcodes.ALOAD, 2);
217:
218: if (Collection.class.isAssignableFrom(tp)) {
219: doCollection(mv, x);
220: } else {
221: if (JAXBElement.class.isAssignableFrom(tp)) {
222: mv.visitVarInsn(Opcodes.ALOAD, 0);
223: mv
224: .visitFieldInsn(
225: Opcodes.GETFIELD,
226: periodToSlashes(newClassName),
227: "factory",
228: "L"
229: + periodToSlashes(objectFactoryClass
230: .getName()) + ";");
231: }
232: mv.visitVarInsn(Opcodes.ALOAD, 1);
233: mv.visitIntInsn(Opcodes.BIPUSH, x);
234: mv.visitMethodInsn(Opcodes.INVOKEINTERFACE,
235: "java/util/List", "get",
236: "(I)Ljava/lang/Object;");
237:
238: if (tp.isPrimitive()) {
239: mv.visitTypeInsn(Opcodes.CHECKCAST,
240: NONPRIMITIVE_MAP.get(tp));
241: mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
242: NONPRIMITIVE_MAP.get(tp), tp.getName()
243: + "Value", "()"
244: + PRIMITIVE_MAP.get(tp));
245: } else if (JAXBElement.class.isAssignableFrom(tp)) {
246: mv.visitTypeInsn(Opcodes.CHECKCAST,
247: periodToSlashes(jaxbMethods[x]
248: .getParameterTypes()[0].getName()));
249: mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
250: periodToSlashes(objectFactoryClass
251: .getName()), jaxbMethods[x]
252: .getName(),
253: getMethodSignature(jaxbMethods[x]));
254: } else if (tp.isArray()) {
255: mv.visitTypeInsn(Opcodes.CHECKCAST,
256: getClassCode(tp));
257: } else {
258: mv.visitTypeInsn(Opcodes.CHECKCAST,
259: periodToSlashes(tp.getName()));
260: }
261: mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
262: periodToSlashes(wrapperType.getName()),
263: setMethods[x].getName(), "(" + getClassCode(tp)
264: + ")V");
265: }
266: }
267:
268: mv.visitVarInsn(Opcodes.ALOAD, 2);
269: mv.visitInsn(Opcodes.ARETURN);
270:
271: Label lEnd = new Label();
272: mv.visitLabel(lEnd);
273: mv.visitLocalVariable("this", "L" + newClassName + ";", null,
274: lBegin, lEnd, 0);
275: mv.visitLocalVariable("lst", "Ljava/util/List;",
276: "Ljava/util/List<*>;", lBegin, lEnd, 1);
277: mv.visitLocalVariable("ok", "L"
278: + periodToSlashes(wrapperType.getName()) + ";", null,
279: lBegin, lEnd, 2);
280: mv.visitMaxs(0, 0);
281: mv.visitEnd();
282: return true;
283: }
284:
285: private void doCollection(MethodVisitor mv, int x) {
286: //List aVal = obj.getA();
287: //List newA = (List)lst.get(99);
288: //if (aVal == null) {
289: // obj.setA(newA);
290: //} else {
291: // aVal.addAll(newA);
292: //}
293:
294: mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
295: periodToSlashes(wrapperType.getName()), getMethods[x]
296: .getName(), getMethodSignature(getMethods[x]));
297: mv.visitVarInsn(Opcodes.ASTORE, 3);
298: mv.visitVarInsn(Opcodes.ALOAD, 1);
299: mv.visitIntInsn(Opcodes.BIPUSH, x);
300: mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, "java/util/List",
301: "get", "(I)Ljava/lang/Object;");
302: mv.visitTypeInsn(Opcodes.CHECKCAST, "java/util/List");
303: mv.visitTypeInsn(Opcodes.CHECKCAST, "java/util/List");
304: mv.visitVarInsn(Opcodes.ASTORE, 4);
305: mv.visitVarInsn(Opcodes.ALOAD, 3);
306: Label nonNullLabel = new Label();
307: mv.visitJumpInsn(Opcodes.IFNONNULL, nonNullLabel);
308:
309: if (setMethods[x] == null) {
310: mv.visitTypeInsn(Opcodes.NEW, "java/lang/RuntimeException");
311: mv.visitInsn(Opcodes.DUP);
312: mv.visitLdcInsn(getMethods[x].getName()
313: + " returned null and there isn't a set method.");
314: mv.visitMethodInsn(Opcodes.INVOKESPECIAL,
315: "java/lang/RuntimeException", "<init>",
316: "(Ljava/lang/String;)V");
317: mv.visitInsn(Opcodes.ATHROW);
318: } else {
319: mv.visitVarInsn(Opcodes.ALOAD, 2);
320: mv.visitVarInsn(Opcodes.ALOAD, 4);
321: mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
322: periodToSlashes(wrapperType.getName()),
323: setMethods[x].getName(),
324: getMethodSignature(setMethods[x]));
325: }
326: Label jumpOverLabel = new Label();
327: mv.visitJumpInsn(Opcodes.GOTO, jumpOverLabel);
328: mv.visitLabel(nonNullLabel);
329: mv.visitVarInsn(Opcodes.ALOAD, 3);
330: mv.visitVarInsn(Opcodes.ALOAD, 4);
331: mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, "java/util/List",
332: "addAll", "(Ljava/util/Collection;)Z");
333: mv.visitInsn(Opcodes.POP);
334: mv.visitLabel(jumpOverLabel);
335:
336: }
337:
338: private static boolean addGetWrapperParts(String newClassName,
339: Class<?> wrapperClass, Method getMethods[], Field fields[],
340: ClassWriter cw) {
341: MethodVisitor mv = cw
342: .visitMethod(
343: Opcodes.ACC_PUBLIC,
344: "getWrapperParts",
345: "(Ljava/lang/Object;)Ljava/util/List;",
346: "(Ljava/lang/Object;)Ljava/util/List<Ljava/lang/Object;>;",
347: new String[] { "org/apache/cxf/interceptor/Fault" });
348: mv.visitCode();
349: Label lBegin = new Label();
350: mv.visitLabel(lBegin);
351:
352: //the ret List
353: mv.visitTypeInsn(Opcodes.NEW, "java/util/ArrayList");
354: mv.visitInsn(Opcodes.DUP);
355: mv.visitMethodInsn(Opcodes.INVOKESPECIAL,
356: "java/util/ArrayList", "<init>", "()V");
357: mv.visitVarInsn(Opcodes.ASTORE, 2);
358:
359: // cast the Object to the wrapperType type
360: mv.visitVarInsn(Opcodes.ALOAD, 1);
361: mv.visitTypeInsn(Opcodes.CHECKCAST,
362: periodToSlashes(wrapperClass.getName()));
363: mv.visitVarInsn(Opcodes.ASTORE, 3);
364:
365: for (int x = 0; x < getMethods.length; x++) {
366: Method method = getMethods[x];
367: if (method == null && fields[x] != null) {
368: //fallback to reflection mode
369: return false;
370: }
371:
372: if (method == null) {
373: mv.visitVarInsn(Opcodes.ALOAD, 2);
374: mv.visitInsn(Opcodes.ACONST_NULL);
375: mv.visitMethodInsn(Opcodes.INVOKEINTERFACE,
376: "java/util/List", "add",
377: "(Ljava/lang/Object;)Z");
378: mv.visitInsn(Opcodes.POP);
379: } else {
380: mv.visitVarInsn(Opcodes.ALOAD, 2);
381: mv.visitVarInsn(Opcodes.ALOAD, 3);
382: mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
383: periodToSlashes(wrapperClass.getName()), method
384: .getName(), getMethodSignature(method));
385: if (method.getReturnType().isPrimitive()) {
386: //wrap into Object type
387: createObjectWrapper(mv, method.getReturnType());
388: }
389: if (JAXBElement.class.isAssignableFrom(method
390: .getReturnType())) {
391: mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
392: "javax/xml/bind/JAXBElement", "getValue",
393: "()Ljava/lang/Object;");
394: }
395:
396: mv.visitMethodInsn(Opcodes.INVOKEINTERFACE,
397: "java/util/List", "add",
398: "(Ljava/lang/Object;)Z");
399: mv.visitInsn(Opcodes.POP);
400: }
401: }
402:
403: //return the list
404: Label l2 = new Label();
405: mv.visitLabel(l2);
406: mv.visitVarInsn(Opcodes.ALOAD, 2);
407: mv.visitInsn(Opcodes.ARETURN);
408:
409: Label lEnd = new Label();
410: mv.visitLabel(lEnd);
411: mv.visitLocalVariable("this", "L" + newClassName + ";", null,
412: lBegin, lEnd, 0);
413: mv.visitLocalVariable("o", "Ljava/lang/Object;", null, lBegin,
414: lEnd, 1);
415: mv
416: .visitLocalVariable("ret", "Ljava/util/List;",
417: "Ljava/util/List<Ljava/lang/Object;>;", lBegin,
418: lEnd, 2);
419: mv.visitLocalVariable("ok", "L"
420: + periodToSlashes(wrapperClass.getName()) + ";", null,
421: lBegin, lEnd, 3);
422: mv.visitMaxs(0, 0);
423: mv.visitEnd();
424: return true;
425: }
426:
427: private static String getMethodSignature(Method m) {
428: StringBuffer buf = new StringBuffer("(");
429: for (Class<?> cl : m.getParameterTypes()) {
430: buf.append(getClassCode(cl));
431: }
432: buf.append(")");
433: buf.append(getClassCode(m.getReturnType()));
434:
435: return buf.toString();
436: }
437:
438: private static String getClassCode(Class<?> cl) {
439: if (cl == Void.TYPE) {
440: return "V";
441: }
442: if (cl.isPrimitive()) {
443: return PRIMITIVE_MAP.get(cl);
444: }
445: if (cl.isArray()) {
446: return "[" + getClassCode(cl.getComponentType());
447: }
448: return "L" + periodToSlashes(cl.getName()) + ";";
449: }
450:
451: private static void createObjectWrapper(MethodVisitor mv,
452: Class<?> cl) {
453: mv.visitMethodInsn(Opcodes.INVOKESTATIC, NONPRIMITIVE_MAP
454: .get(cl), "valueOf", "(" + PRIMITIVE_MAP.get(cl) + ")L"
455: + NONPRIMITIVE_MAP.get(cl) + ";");
456: }
457:
458: private static String periodToSlashes(String s) {
459: char ch[] = s.toCharArray();
460: for (int x = 0; x < ch.length; x++) {
461: if (ch[x] == '.') {
462: ch[x] = '/';
463: }
464: }
465: return new String(ch);
466: }
467:
468: }
|