1 | /* |
2 | * Copyright 2007 brunella ltd |
3 | * |
4 | * Licensed under the LGPL Version 3 (the "License"); |
5 | * you may not use this file except in compliance with the License. |
6 | * |
7 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
8 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
9 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
10 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
11 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
12 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
13 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
14 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
15 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
16 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
17 | * THE POSSIBILITY OF SUCH DAMAGE. |
18 | */ |
19 | package sf.qof.util; |
20 | |
21 | import static sf.qof.codegen.Constants.TYPE_boolean; |
22 | |
23 | import java.lang.reflect.Method; |
24 | import java.lang.reflect.Modifier; |
25 | |
26 | import net.sf.cglib.core.ClassEmitter; |
27 | import net.sf.cglib.core.CodeEmitter; |
28 | import net.sf.cglib.core.Constants; |
29 | import net.sf.cglib.core.DebuggingClassWriter; |
30 | import net.sf.cglib.core.Signature; |
31 | |
32 | import org.objectweb.asm.ClassWriter; |
33 | import org.objectweb.asm.Label; |
34 | import org.objectweb.asm.Type; |
35 | |
36 | /** |
37 | * Factory class to create a delegator object for a given delegatee class. |
38 | * |
39 | * <p> |
40 | * The factory creates wrappers for each public non-final method of the |
41 | * delegatee class. Once any of these methods is called the delegatee factory is |
42 | * used to initialize the delegatee object. The delegatee class must have a |
43 | * default constructor. |
44 | * </p> |
45 | * <p> |
46 | * The delegatee factory must implement a static <code>initialize</code> |
47 | * method with the first argument being the delegatee object and the other |
48 | * arguments matching the types of the <code>constructorParameters</code>: |
49 | * </p> |
50 | * |
51 | * <pre> |
52 | * public class PersonDelegateeFactory { |
53 | * public static void initialize(Person personDelegatee, Integer id) { |
54 | * String name = getNameForId(id); |
55 | * personDelegatee.setName(name); |
56 | * } |
57 | * } |
58 | * |
59 | * // create a delegator for person and pass it the id of 1 |
60 | * Person person = DelegatorFactory<Person>(Person.class, |
61 | * PersonDelegateeFactory.class, 1); |
62 | * // person is instanziated but not initialized |
63 | * // a call to getName() or any other public method of person for the first time |
64 | * // calls PersonDelegateeFactory.initialize(person, 1) |
65 | * String name = person.getName(); |
66 | * // person is initialized now |
67 | * </pre> |
68 | * |
69 | * <p> |
70 | * Initialization of delegator objects is thread-safe. |
71 | * </p> |
72 | * <p> |
73 | * <code>DelegatorFactory</code> can be used to implement lazy initialization |
74 | * of objects |
75 | * </p> |
76 | * <p> |
77 | * The generated delegatee objects super class is the delegatee class. |
78 | * </p> |
79 | */ |
80 | public class DelegatorFactory { |
81 | |
82 | /** |
83 | * Returns an instance of a delegator object for a delegatee class. |
84 | * |
85 | * @param <T> |
86 | * type of the delegatee object |
87 | * @param delegateeClass |
88 | * class of the delegatee object |
89 | * @param delegateeFactory |
90 | * factory to initialize the delegatee object |
91 | * @param constructorParameters |
92 | * parameters that are passed to the delegatee factory |
93 | * @return delegator object instance |
94 | */ |
95 | @SuppressWarnings("unchecked") |
96 | public static <T> T create(Class<T> delegateeClass, Class<?> delegateeFactory, Object... constructorParameters) { |
97 | Class<T> delegatorClass = (Class<T>) ClassGenerationCache.getCachedClass(delegateeClass, delegateeFactory); |
98 | if (delegatorClass == null) { |
99 | try { |
100 | // create new class |
101 | delegatorClass = createClass(delegateeClass, delegateeFactory, constructorParameters); |
102 | |
103 | // put the newly created class |
104 | ClassGenerationCache.putCachedClass(delegatorClass, delegateeClass, delegateeFactory); |
105 | } catch (Throwable e) { |
106 | ClassGenerationCache.putCachedClass(null, delegateeClass, delegateeFactory); |
107 | throw new RuntimeException(e); |
108 | } |
109 | } |
110 | |
111 | // create instance |
112 | Class[] constructorParameterTypes = new Class[constructorParameters.length]; |
113 | for (int i = 0; i < constructorParameters.length; i++) { |
114 | constructorParameterTypes[i] = constructorParameters[i].getClass(); |
115 | } |
116 | try { |
117 | return delegatorClass.getConstructor(constructorParameterTypes).newInstance(constructorParameters); |
118 | } catch (Throwable e) { |
119 | throw new RuntimeException(e); |
120 | } |
121 | } |
122 | |
123 | private static <T> Class<T> createClass(Class<T> delegateeClass, Class<?> delegateeFactory, |
124 | Object... constructorParameters) throws Exception { |
125 | |
126 | ClassWriter cw = new DebuggingClassWriter(true); |
127 | ClassEmitter ce = new ClassEmitter(cw); |
128 | |
129 | String className = delegateeClass.getName() + "$" + delegateeFactory.getName() + "$Delegator"; |
130 | if (className.startsWith("java")) { |
131 | className = "$" + className; |
132 | } |
133 | |
134 | Type[] delegateeInterfaces = getInterfaceTypes(delegateeClass); |
135 | |
136 | ce.begin_class(Constants.V1_2, Constants.ACC_PUBLIC, className, Type.getType(delegateeClass), delegateeInterfaces, |
137 | "<generated>"); |
138 | |
139 | createConstructorAndFields(ce, delegateeClass, constructorParameters); |
140 | |
141 | createInitializeMethod(ce, delegateeFactory, delegateeClass, constructorParameters); |
142 | createMethods(ce, delegateeClass); |
143 | |
144 | ce.end_class(); |
145 | |
146 | ClassLoader classLoader = delegateeClass.getClassLoader(); |
147 | if (classLoader == null) { |
148 | classLoader = DelegatorFactory.class.getClassLoader(); |
149 | } |
150 | |
151 | return DefineClassHelper.defineClass(className, cw.toByteArray(), classLoader); |
152 | } |
153 | |
154 | private static void createConstructorAndFields(ClassEmitter ce, Class<?> delegateeClass, |
155 | Object[] constructorParameters) { |
156 | ce.declare_field(Constants.ACC_PRIVATE, "$$initialized", TYPE_boolean, false, null); |
157 | for (int i = 0; i < constructorParameters.length; i++) { |
158 | ce.declare_field(Constants.ACC_PRIVATE, "$$" + i, Type.getType(constructorParameters[i].getClass()), null, null); |
159 | } |
160 | |
161 | Signature sigConstructor = new Signature("<init>", Type.VOID_TYPE, getTypes(constructorParameters)); |
162 | CodeEmitter co = ce.begin_method(Constants.ACC_PUBLIC, sigConstructor, null, null); |
163 | co.load_this(); |
164 | co.invoke_constructor(ce.getSuperType(), new Signature("<init>", "()V")); |
165 | // store constructor parameters |
166 | for (int i = 0; i < constructorParameters.length; i++) { |
167 | co.load_this(); |
168 | co.load_arg(i); |
169 | co.putfield("$$" + i); |
170 | } |
171 | co.return_value(); |
172 | co.end_method(); |
173 | } |
174 | |
175 | private static void createMethods(ClassEmitter ce, Class<?> delegateeClass) { |
176 | for (Method method : delegateeClass.getMethods()) { |
177 | createMethod(ce, delegateeClass, method); |
178 | } |
179 | } |
180 | |
181 | private static void createInitializeMethod(ClassEmitter ce, Class<?> delegateeFactory, Class<?> delegateeClass, |
182 | Object[] constructorParameters) { |
183 | CodeEmitter co = ce.begin_method(Constants.ACC_PRIVATE + Constants.ACC_SYNCHRONIZED, new Signature("$initialize", |
184 | "()V"), null, null); |
185 | co.load_this(); |
186 | co.getfield("$$initialized"); |
187 | Label labelInitialized = co.make_label(); |
188 | co.if_jump(CodeEmitter.NE, labelInitialized); |
189 | |
190 | co.load_this(); |
191 | co.push(true); |
192 | co.putfield("$$initialized"); |
193 | co.load_this(); |
194 | String desc = "(L" + Type.getType(delegateeClass).getInternalName() + ";"; |
195 | for (int i = 0; i < constructorParameters.length; i++) { |
196 | desc += "L" + Type.getType(constructorParameters[i].getClass()).getInternalName() + ";"; |
197 | } |
198 | desc += ")V"; |
199 | for (int i = 0; i < constructorParameters.length; i++) { |
200 | co.load_this(); |
201 | co.getfield("$$" + i); |
202 | } |
203 | co.invoke_static(Type.getType(delegateeFactory), new Signature("initialize", desc)); |
204 | |
205 | co.mark(labelInitialized); |
206 | |
207 | co.return_value(); |
208 | co.end_method(); |
209 | } |
210 | |
211 | private static void createMethod(ClassEmitter ce, Class<?> delegateeClass, Method method) { |
212 | if (Modifier.isFinal(method.getModifiers())) { |
213 | return; |
214 | } |
215 | Signature methodSignature = ReflectionUtils.getMethodSignature(method); |
216 | CodeEmitter co = ce.begin_method(Constants.ACC_PUBLIC, methodSignature, getTypes(method.getExceptionTypes()), null); |
217 | // call delegatee |
218 | co.load_this(); |
219 | co.getfield("$$initialized"); |
220 | Label labelInitialized = co.make_label(); |
221 | co.if_jump(CodeEmitter.NE, labelInitialized); |
222 | co.load_this(); |
223 | co.invoke_virtual_this(new Signature("$initialize", "()V")); |
224 | co.mark(labelInitialized); |
225 | co.load_this(); |
226 | int num = method.getParameterTypes().length; |
227 | for (int i = 0; i < num; i++) { |
228 | co.load_arg(i); |
229 | } |
230 | co.super_invoke(); |
231 | co.return_value(); |
232 | co.end_method(); |
233 | } |
234 | |
235 | private static Type[] getInterfaceTypes(Class<?> clazz) { |
236 | Class<?>[] interfaces = clazz.getInterfaces(); |
237 | return getTypes(interfaces); |
238 | } |
239 | |
240 | private static Type[] getTypes(Class<?>[] classes) { |
241 | Type[] types = new Type[classes.length]; |
242 | for (int i = 0; i < classes.length; i++) { |
243 | types[i] = Type.getType(classes[i]); |
244 | } |
245 | return types; |
246 | } |
247 | |
248 | private static Type[] getTypes(Object[] objs) { |
249 | Type[] types = new Type[objs.length]; |
250 | for (int i = 0; i < objs.length; i++) { |
251 | types[i] = Type.getType(objs[i].getClass()); |
252 | } |
253 | return types; |
254 | } |
255 | } |