View Javadoc

1   /**
2    * Copyright (C) 2009 Erik Putrycz <erik.putrycz@gmail.com>
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * 
8    *         http://www.apache.org/licenses/LICENSE-2.0
9    * 
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package net.ep.db4o.javassist;
18  
19  import java.lang.reflect.Constructor;
20  import java.lang.reflect.Field;
21  import java.lang.reflect.Method;
22  import java.lang.reflect.Modifier;
23  import java.util.ArrayList;
24  import java.util.List;
25  
26  import javassist.CannotCompileException;
27  import javassist.ClassPool;
28  import javassist.CtClass;
29  import javassist.CtConstructor;
30  import javassist.CtField;
31  import javassist.CtMethod;
32  import javassist.CtNewMethod;
33  import net.ep.db4o.activator.TransparentActivation;
34  import net.ep.db4o.annotations.NoEnhancement;
35  
36  import com.db4o.activation.ActivationPurpose;
37  import com.db4o.activation.Activator;
38  import com.db4o.internal.Platform4;
39  import com.db4o.reflect.ReflectClass;
40  import com.db4o.reflect.ReflectField;
41  import com.db4o.reflect.ReflectMethod;
42  import com.db4o.reflect.Reflector;
43  import com.db4o.reflect.core.ConstructorSupport;
44  import com.db4o.reflect.core.ReflectConstructor;
45  import com.db4o.reflect.core.ReflectConstructorSpec;
46  import com.db4o.reflect.jdk.JavaReflectClass;
47  import com.db4o.reflect.jdk.JdkConstructor;
48  import com.db4o.reflect.jdk.JdkMethod;
49  import com.db4o.reflect.jdk.JdkReflector;
50  
51  @SuppressWarnings("unchecked")
52  public class JVSTClass implements JavaReflectClass {
53  
54  	public static final String ACTIVATOR_FIELD = "_activator";
55  
56  	private ReflectConstructorSpec _constructorSpec;
57  
58  	// private Object[] _constructorParams;
59  
60  	private final Class _enhancedClass;
61  
62  	private final Class _realClazz;
63  
64  	private final JVSTReflector _jVSTreflector;
65  
66  	private final Reflector _reflector;
67  
68  	private boolean _threadLocal;
69  
70  	private PersistOnMethodPolicy _persistPolicy;
71  
72  	private ActivateOnMethodPolicy _activatePolicy;
73  
74  	private boolean _ignoreNullActivator;
75  
76  	private static final boolean debug = false;
77  
78  	public JVSTClass(Reflector reflector, JVSTReflector jreflector,
79  			ClassPool pool, Class clazz, boolean threadLocal, boolean ignoreNullActivator,
80  			PersistOnMethodPolicy persistPolicy, ActivateOnMethodPolicy activatePolicy) {
81  		if (reflector == null) {
82  			throw new NullPointerException();
83  		}
84  		if (jreflector == null) {
85  			throw new NullPointerException();
86  		}
87  		_jVSTreflector = jreflector;
88  		_reflector = reflector;
89  		_realClazz = clazz;
90  		_threadLocal = threadLocal;
91  		_ignoreNullActivator = ignoreNullActivator;
92  		_persistPolicy = persistPolicy;
93  		_activatePolicy = activatePolicy;
94  		_constructorSpec = ReflectConstructorSpec.UNSPECIFIED_CONSTRUCTOR;
95  		try {
96  			CtClass cClass = pool.get(_realClazz.getName());
97  			String ext = "TA$" + Long.toHexString(System.currentTimeMillis());
98  			if (_threadLocal)
99  				ext = "L" + ext;
100 			if (_ignoreNullActivator)
101 				ext = "I" + ext;
102 			CtClass _extClass = pool.makeClass(_realClazz.getName() + "_" + ext, cClass);
103 			// ACTIVATOR_FIELD
104 			CtField activatorField = null;
105 			if (_threadLocal) {
106 				activatorField = new CtField(pool.get("java.lang.ThreadLocal"),
107 						ACTIVATOR_FIELD, _extClass);
108 				_extClass.addField(activatorField);
109 				// _extClass.addField(activatorField,"new ThreadLocal()");
110 			} else {
111 				activatorField = new CtField(pool.get(Activator.class.getName()),
112 						ACTIVATOR_FIELD, _extClass);
113 				_extClass.addField(activatorField);
114 
115 			}
116 
117 			_extClass.setSuperclass(cClass);
118 			_extClass.addInterface(pool.get(TransparentActivation.class.getName()));
119 			CtMethod bindMethod = null;
120 			if (_threadLocal) {
121 				bindMethod = CtNewMethod.make(
122 						" public void bind(com.db4o.activation.Activator activator) { if ("
123 								+ ACTIVATOR_FIELD + " == null) " + ACTIVATOR_FIELD
124 								+ " = new ThreadLocal();" + ACTIVATOR_FIELD
125 								+ ".set(activator);}", _extClass);
126 
127 			} else {
128 				bindMethod = CtNewMethod.make(
129 						" public void bind(com.db4o.activation.Activator activator) { "
130 								+ ACTIVATOR_FIELD + " = activator;}", _extClass);
131 
132 			}
133 			_extClass.addMethod(bindMethod);
134 
135 			List<String> extendedMethods = new ArrayList<String>();
136 			enhanceMethods(cClass, _extClass, extendedMethods);
137 			// enhance superclasses except java.lang.object
138 			while (!cClass.getSuperclass().getName().startsWith("java")) {
139 				cClass = cClass.getSuperclass();
140 				enhanceMethods(cClass, _extClass, extendedMethods);
141 			}
142 			boolean hasPublic = false;
143 			List<CtConstructor> consToDeclare = new ArrayList<CtConstructor>();
144 			CtConstructor defaultCons = null;
145 			for (CtConstructor cons : _extClass.getSuperclass()
146 					.getDeclaredConstructors()) {
147 				hasPublic = hasPublic
148 						|| javassist.Modifier.isPublic(cons.getModifiers());
149 				if (javassist.Modifier.isProtected(cons.getModifiers())
150 						|| javassist.Modifier.isPublic(cons.getModifiers()))
151 					consToDeclare.add(cons);
152 				if (javassist.Modifier.isPublic(cons.getModifiers())
153 						&& cons.getParameterTypes() == null)
154 					defaultCons = cons;
155 			}
156 			for (CtConstructor cons : consToDeclare) {
157 				CtConstructor newCons = new CtConstructor(cons.getParameterTypes(),
158 						_extClass);
159 				newCons.setBody("super($$);");
160 				newCons.setModifiers(javassist.Modifier.setPublic(cons.getModifiers()));
161 				_extClass.addConstructor(newCons);
162 			}
163 			_extClass.makeClassInitializer();
164 			_enhancedClass = _extClass.toClass();
165 			// _enhancedClass = _realClazz;
166 			_extClass.freeze();
167 		} catch (Exception ex) {
168 			ex.printStackTrace();
169 			throw new RuntimeException("Error while enhancing "
170 					+ _realClazz.getName(), ex);
171 		}
172 	}
173 
174 	private void enhanceForActivation(CtClass cClass, CtClass extClass,
175 			List<String> extendedMethods) throws CannotCompileException {
176 	}
177 
178 	private void enhanceMethods(CtClass cClass, CtClass extClass,
179 			List<String> extendedMethods) throws CannotCompileException {
180 		enhanceForActivation(cClass, extClass, extendedMethods);
181 		for (CtMethod method : cClass.getDeclaredMethods()) {
182 			String methodID = method.getName() + "." + method.getSignature();
183 			// raise a diagnostic if one method is final
184 			// if (!extendedMethods.contains(methodID)
185 			// && Modifier.isFinal(method.getModifiers()) && _diagnostics != null)
186 			// _diagnostics.onDiagnostic(new ClassWithFinalMethod(cClass, method
187 			// .getName()));
188 
189 			if (doEnhanceMethod(method) && !extendedMethods.contains(methodID)) {
190 				CtMethod extMethod = CtNewMethod.delegator(method, extClass);
191 				String activationPurpose = null;
192 				if (_activatePolicy != null && _activatePolicy.doActivate(method)) {
193 					activationPurpose = "READ";
194 				}
195 				if (_persistPolicy != null && _persistPolicy.doPersist(method)) {
196 					activationPurpose = "WRITE";
197 				}
198 				if (activationPurpose != null) {
199 					String debugCode = "System.out.println(\"activating "
200 							+ activationPurpose + " in " + method.getName()
201 							+ " activator: \" + " + ACTIVATOR_FIELD + " + \" \");";
202 					String activatorGetterCode = ACTIVATOR_FIELD;
203 					if (_threadLocal) {
204 						activatorGetterCode = "((com.db4o.activation.Activator)"
205 								+ ACTIVATOR_FIELD + ".get())";
206 					}
207 					String testCode = "if (" + activatorGetterCode + " == null) throw new NullPointerException(\"Activator field not binded\");";
208 					String activateCode = activatorGetterCode + ".activate("
209 							+ ActivationPurpose.class.getName() + "." + activationPurpose
210 							+ ");";
211 					if (_ignoreNullActivator) {
212 						testCode = "";						
213 						activateCode = "if (" + activatorGetterCode + " != null) {" +  activateCode +" }" ;
214 					}
215 					if (debug)
216 						extMethod.insertBefore("{ " + debugCode + testCode + activateCode + "}");
217 					else
218 						extMethod.insertBefore("{ " + testCode + activateCode + " }");
219 
220 					extClass.addMethod(extMethod);
221 					extendedMethods.add(methodID);
222 				}
223 			}
224 		}
225 	}
226 
227 	public static boolean doEnhanceMethod(CtMethod method) {
228 		int mods = method.getModifiers();
229 		return !Modifier.isStatic(mods) && !Modifier.isNative(mods)
230 				&& !Modifier.isFinal(mods) && !Modifier.isAbstract(mods)
231 				&& !Tools.hasAnnotation(method, NoEnhancement.class);
232 	}
233 
234 	public ReflectClass getComponentType() {
235 		return _reflector.forClass(_realClazz.getComponentType());
236 	}
237 
238 	private ReflectConstructor[] getDeclaredConstructors() {
239 		try {
240 			Constructor[] constructors = _enhancedClass.getDeclaredConstructors();
241 			ReflectConstructor[] reflectors = new ReflectConstructor[constructors.length];
242 			for (int i = 0; i < constructors.length; i++) {
243 				reflectors[i] = new JdkConstructor(_reflector, constructors[i]);
244 			}
245 			return reflectors;
246 		} catch (NoClassDefFoundError exc) {
247 			return new ReflectConstructor[0];
248 		}
249 	}
250 
251 	public ReflectField getDeclaredField(String name) {
252 
253 		try {
254 			return createField(_realClazz.getDeclaredField(name));
255 		} catch (Exception e) {
256 			return null;
257 		} catch (NoClassDefFoundError e) {
258 			return null;
259 		}
260 	}
261 
262 	private ReflectField createField(Field field) {
263 		return new JVSTField(_reflector, field);
264 	}
265 
266 	public ReflectField[] getDeclaredFields() {
267 		try {
268 			Field[] fields = _realClazz.getDeclaredFields();
269 			ReflectField[] reflectors = new ReflectField[fields.length];
270 			for (int i = 0; i < reflectors.length; i++) {
271 				reflectors[i] = createField(fields[i]);
272 			}
273 			return reflectors;
274 		} catch (NoClassDefFoundError exc) {
275 			return new ReflectField[0];
276 		}
277 
278 	}
279 
280 	public ReflectClass getDelegate() {
281 		return this;
282 	}
283 
284 	public Class getJavaClass() {
285 		return _realClazz;
286 	}
287 
288 	public ReflectMethod getMethod(String methodName, ReflectClass[] paramClasses) {
289 		try {
290 			Method method = _realClazz.getMethod(methodName, JVSTReflector
291 					.toNative(paramClasses));
292 			if (method == null) {
293 				return null;
294 			}
295 			return new JdkMethod(method, reflector());
296 		} catch (Exception e) {
297 			return null;
298 		}
299 	}
300 
301 	public String getName() {
302 		return _realClazz.getName();
303 	}
304 
305 	public ReflectClass getSuperclass() {
306 		return _jVSTreflector.forClass(_realClazz.getSuperclass());
307 	}
308 
309 	public boolean isAbstract() {
310 		return Modifier.isAbstract(_realClazz.getModifiers());
311 	}
312 
313 	public boolean isArray() {
314 		return _realClazz.isArray();
315 	}
316 
317 	public boolean isAssignableFrom(ReflectClass type) {
318 		// if (!(type instanceof JVSTClass))
319 		// return false;
320 		return _realClazz.isAssignableFrom(JdkReflector.toNative(type));
321 	}
322 
323 	public boolean isCollection() {
324 		return _reflector.isCollection(this);
325 	}
326 
327 	public boolean isInstance(Object obj) {
328 		return _realClazz.isInstance(obj);
329 	}
330 
331 	public boolean isInterface() {
332 		return _realClazz.isInterface();
333 	}
334 
335 	public boolean isPrimitive() {
336 		return _realClazz.isPrimitive();
337 	}
338 
339 	public Object newInstance() {
340 		createConstructor();
341 		return _constructorSpec.newInstance();
342 	}
343 
344 	public Reflector reflector() {
345 		return _jVSTreflector;
346 	}
347 
348 	public Object[] toArray(Object obj) {
349 		return null;
350 	}
351 
352 	@Override
353 	public String toString() {
354 		return "Proxy class for " + _realClazz + " (" + _enhancedClass.getName() + ")";
355 	}
356 
357 	public ReflectConstructor getSerializableConstructor() {
358 		Constructor serializableConstructor = Platform4.jdk()
359 				.serializableConstructor(_enhancedClass);
360 		if (serializableConstructor == null) {
361 			return null;
362 		}
363 		return new JdkConstructor(_reflector, serializableConstructor);
364 	}
365 
366 	private void createConstructor() {
367 		if (!_constructorSpec.canBeInstantiated().isUnspecified()) {
368 			return;
369 		}
370 		_constructorSpec = ConstructorSupport.createConstructor(this,
371 				_enhancedClass, _jVSTreflector.configuration(),
372 				getDeclaredConstructors());
373 	}
374 
375 	public boolean ensureCanBeInstantiated() {
376 		createConstructor();
377 		return !_constructorSpec.canBeInstantiated().definiteNo();
378 	}
379 
380 	public Object nullValue() {
381 		return _jVSTreflector.nullValue(this);
382 	}
383 
384 	public Class getEnhancedClass() {
385 		return _enhancedClass;
386 	}
387 
388 }