001: /*****************************************************************************
002: * Copyright (C) PicoContainer Organization. All rights reserved. *
003: * ------------------------------------------------------------------------- *
004: * The software in this package is published under the terms of the BSD *
005: * style license a copy of which has been included with this distribution in *
006: * the LICENSE.txt file. *
007: * *
008: * Original code by *
009: *****************************************************************************/package org.picocontainer.gems.behaviors;
010:
011: import org.picocontainer.ComponentAdapter;
012: import org.picocontainer.PicoContainer;
013: import org.picocontainer.behaviors.AbstractBehavior;
014: import org.picocontainer.behaviors.Cached;
015:
016: import org.objectweb.asm.ClassWriter;
017: import org.objectweb.asm.FieldVisitor;
018: import org.objectweb.asm.MethodVisitor;
019:
020: import java.lang.reflect.Method;
021: import java.lang.reflect.Constructor;
022: import java.lang.reflect.InvocationTargetException;
023: import java.util.Set;
024: import java.util.HashSet;
025:
026: import org.objectweb.asm.*;
027:
028: /**
029: * This component adapter makes it possible to hide the implementation of a real subject (behind a proxy).
030: * The proxy will implement all the interfaces of the
031: * underlying subject. If you want caching,
032: * use a {@link Cached} around this one.
033: *
034: * @author Paul Hammant
035: */
036: public class HiddenImplementation extends AbstractBehavior implements
037: Opcodes {
038:
039: public HiddenImplementation(final ComponentAdapter delegate) {
040: super (delegate);
041: }
042:
043: public Object getComponentInstance(final PicoContainer container) {
044: Object o = getDelegate().getComponentInstance(container);
045: Class[] interfaces = o.getClass().getInterfaces();
046: if (interfaces.length != 0) {
047: byte[] bytes = makeProxy("XX", interfaces, true);
048: AsmClassLoader cl = new AsmClassLoader(
049: HotSwappable.Swappable.class.getClassLoader());
050: Class<?> pClazz = cl.defineClass("XX", bytes);
051: try {
052: Constructor<?> ctor = pClazz
053: .getConstructor(HotSwappable.Swappable.class);
054: final HotSwappable.Swappable swappable = getSwappable();
055: swappable.swap(o);
056: return ctor.newInstance(swappable);
057: } catch (NoSuchMethodException e) {
058: } catch (InstantiationException e) {
059: } catch (IllegalAccessException e) {
060: } catch (InvocationTargetException e) {
061: }
062: }
063: return o;
064: }
065:
066: public String getDescriptor() {
067: return "Hidden";
068: }
069:
070: protected HotSwappable.Swappable getSwappable() {
071: return new HotSwappable.Swappable();
072: }
073:
074: public byte[] makeProxy(String proxyName, Class[] interfaces,
075: boolean setter) {
076:
077: ClassWriter cw = new ClassWriter(0);
078: FieldVisitor fv;
079:
080: Class<Object> super class = Object.class;
081:
082: cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER, proxyName, null,
083: dotsToSlashes(super class), getNames(interfaces));
084:
085: {
086: fv = cw.visitField(ACC_PRIVATE + ACC_TRANSIENT,
087: "swappable",
088: encodedClassName(HotSwappable.Swappable.class),
089: null, null);
090: fv.visitEnd();
091: }
092: doConstructor(proxyName, cw);
093: Set<String> methodsDone = new HashSet<String>();
094: for (Class iface : interfaces) {
095: Method[] meths = iface.getMethods();
096: for (Method meth : meths) {
097: if (!methodsDone.contains(meth.toString())) {
098: doMethod(proxyName, cw, iface, meth);
099: methodsDone.add(meth.toString());
100: }
101: }
102: }
103:
104: cw.visitEnd();
105:
106: return cw.toByteArray();
107: }
108:
109: private String[] getNames(Class[] interfaces) {
110: String[] retVal = new String[interfaces.length];
111: for (int i = 0; i < interfaces.length; i++) {
112: retVal[i] = dotsToSlashes(interfaces[i]);
113: }
114: return retVal;
115: }
116:
117: private void doConstructor(String proxyName, ClassWriter cw) {
118: MethodVisitor mv;
119: mv = cw.visitMethod(ACC_PUBLIC, "<init>", "(L"
120: + dotsToSlashes(HotSwappable.Swappable.class) + ";)V",
121: null, null);
122: mv.visitCode();
123: mv.visitVarInsn(ALOAD, 0);
124: mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>",
125: "()V");
126: mv.visitVarInsn(ALOAD, 0);
127: mv.visitVarInsn(ALOAD, 1);
128: mv.visitFieldInsn(PUTFIELD, proxyName, "swappable",
129: encodedClassName(HotSwappable.Swappable.class));
130: mv.visitInsn(RETURN);
131: mv.visitMaxs(2, 2);
132: mv.visitEnd();
133: }
134:
135: private void doMethod(String proxyName, ClassWriter cw,
136: Class iface, Method meth) {
137: String signature = "(" + encodedParameterNames(meth) + ")"
138: + encodedClassName(meth.getReturnType());
139: String[] exceptions = encodedExceptionNames(meth
140: .getExceptionTypes());
141: MethodVisitor mv;
142: mv = cw.visitMethod(ACC_PUBLIC, meth.getName(), signature,
143: null, exceptions);
144: mv.visitCode();
145: mv.visitVarInsn(ALOAD, 0);
146: mv.visitFieldInsn(GETFIELD, proxyName, "swappable",
147: encodedClassName(HotSwappable.Swappable.class));
148: mv.visitMethodInsn(INVOKEVIRTUAL,
149: dotsToSlashes(HotSwappable.Swappable.class),
150: "getInstance", "()Ljava/lang/Object;");
151: mv.visitTypeInsn(CHECKCAST, dotsToSlashes(iface));
152: Class[] types = meth.getParameterTypes();
153: int ix = 1;
154: for (Class type : types) {
155: int load = whichLoad(type);
156: mv.visitVarInsn(load, ix);
157: ix = indexOf(ix, load);
158: }
159: mv.visitMethodInsn(INVOKEINTERFACE, dotsToSlashes(iface), meth
160: .getName(), signature);
161: mv.visitInsn(whichReturn(meth.getReturnType()));
162: mv.visitMaxs(ix, ix);
163: mv.visitEnd();
164: }
165:
166: private int indexOf(int ix, int loadType) {
167: if (loadType == LLOAD) {
168: return ix + 2;
169: } else if (loadType == DLOAD) {
170: return ix + 2;
171: } else if (loadType == ILOAD) {
172: return ix + 1;
173: } else if (loadType == ALOAD) {
174: return ix + 1;
175: } else if (loadType == FLOAD) {
176: return ix + 1;
177: }
178: return 0;
179: }
180:
181: private String[] encodedExceptionNames(Class[] exceptionTypes) {
182: if (exceptionTypes.length == 0) {
183: return null;
184: }
185: String[] retVal = new String[exceptionTypes.length];
186: for (int i = 0; i < exceptionTypes.length; i++) {
187: Class clazz = exceptionTypes[i];
188: retVal[i] = dotsToSlashes(clazz);
189: }
190: return retVal;
191: }
192:
193: private int whichReturn(Class clazz) {
194: if (!clazz.isPrimitive()) {
195: return ARETURN;
196: } else if (clazz.isArray()) {
197: return ARETURN;
198: } else if (clazz == int.class) {
199: return IRETURN;
200: } else if (clazz == long.class) {
201: return LRETURN;
202: } else if (clazz == byte.class) {
203: return IRETURN;
204: } else if (clazz == float.class) {
205: return FRETURN;
206: } else if (clazz == double.class) {
207: return DRETURN;
208: } else if (clazz == char.class) {
209: return IRETURN;
210: } else if (clazz == short.class) {
211: return IRETURN;
212: } else if (clazz == boolean.class) {
213: return IRETURN;
214: } else if (clazz == void.class) {
215: return RETURN;
216: } else {
217: return 0;
218: }
219: }
220:
221: private int whichLoad(Class clazz) {
222: if (!clazz.isPrimitive()) {
223: return ALOAD;
224: } else if (clazz.isArray()) {
225: return ALOAD;
226: } else if (clazz == int.class) {
227: return ILOAD;
228: } else if (clazz == long.class) {
229: return LLOAD;
230: } else if (clazz == byte.class) {
231: return ILOAD;
232: } else if (clazz == float.class) {
233: return FLOAD;
234: } else if (clazz == double.class) {
235: return DLOAD;
236: } else if (clazz == char.class) {
237: return ILOAD;
238: } else if (clazz == short.class) {
239: return ILOAD;
240: } else if (clazz == boolean.class) {
241: return ILOAD;
242: } else {
243: return 0;
244: }
245: }
246:
247: private String encodedClassName(Class clazz) {
248: if (clazz.getName().startsWith("[")) {
249: return dotsToSlashes(clazz);
250: } else if (!clazz.isPrimitive()) {
251: return "L" + dotsToSlashes(clazz) + ";";
252: } else if (clazz == int.class) {
253: return "I";
254: } else if (clazz == long.class) {
255: return "J";
256: } else if (clazz == byte.class) {
257: return "B";
258: } else if (clazz == float.class) {
259: return "F";
260: } else if (clazz == double.class) {
261: return "D";
262: } else if (clazz == char.class) {
263: return "C";
264: } else if (clazz == short.class) {
265: return "S";
266: } else if (clazz == boolean.class) {
267: return "Z";
268: } else if (clazz == void.class) {
269: return "V";
270: } else {
271: return null;
272: }
273: }
274:
275: private String encodedParameterNames(Method meth) {
276: String retVal = "";
277: for (Class type : meth.getParameterTypes()) {
278: retVal += encodedClassName(type);
279: }
280: return retVal;
281: }
282:
283: private String dotsToSlashes(Class type) {
284: return type.getName().replace('.', '/');
285: }
286:
287: private static class AsmClassLoader extends ClassLoader {
288:
289: public AsmClassLoader(ClassLoader parent) {
290: super (parent);
291: }
292:
293: public Class<?> defineClass(String name, byte[] b) {
294: return defineClass(name, b, 0, b.length);
295: }
296: }
297:
298: }
|