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.Modifier;
21  import java.util.ArrayList;
22  import java.util.HashMap;
23  import java.util.List;
24  import java.util.Map;
25  
26  import javassist.ClassPool;
27  import javassist.LoaderClassPath;
28  import net.ep.db4o.activator.TransparentActivation;
29  
30  import com.db4o.internal.Platform4;
31  import com.db4o.reflect.ReflectArray;
32  import com.db4o.reflect.ReflectClass;
33  import com.db4o.reflect.Reflector;
34  import com.db4o.reflect.ReflectorConfiguration;
35  import com.db4o.reflect.jdk.ClassLoaderJdkLoader;
36  import com.db4o.reflect.jdk.JdkClass;
37  import com.db4o.reflect.jdk.JdkLoader;
38  import com.db4o.reflect.jdk.JdkReflector;
39  import com.db4o.ta.Activatable;
40  
41  @SuppressWarnings("unchecked")
42  public class JVSTReflector implements Reflector {
43  
44  	private static final boolean debug = false;
45  	
46  
47  	private static final Map<Class, List<ClassData>> _classCache = new HashMap<Class, List<ClassData>>();
48  	
49  	public static class ClassData {
50  		ReflectClass _reflectClass;
51  		boolean ignoreNullActivator;
52  		boolean threadLocal;
53  		@Override
54  		public String toString() {
55  			return _reflectClass + " ignoreNullActivator:" + ignoreNullActivator + " threadLocal:" + threadLocal;
56  		}
57  	}
58  	
59  	public static final <T> Class<? super T> getRealClass(Class<? super T> clazz) {
60  		if (isEnhanced(clazz)) {
61  			clazz = clazz.getSuperclass();
62  		}
63  		return clazz;
64  	}
65  
66  	public static final boolean hasPublicConstructor(Class clazz) {
67  		boolean hasPublic = true;
68  		for (Constructor constr : clazz.getDeclaredConstructors()) {
69  			if (Modifier.isPublic(constr.getModifiers()))
70  				return true;
71  			hasPublic = false;
72  		}
73  		return hasPublic;
74  	}
75  
76  	public static final boolean isEnhanced(Class clazz) {
77  		if (clazz.equals(TransparentActivation.class))
78  			return false;
79  		return TransparentActivation.class.isAssignableFrom(clazz);
80  	}
81  
82  	public static final boolean shouldBeEnhanced(Class clazz) {
83  		return canBeEnhanced(clazz)
84  				&& hasPublicConstructor(clazz)
85  				&& (clazz.getPackage().getName() == null || !clazz.getPackage()
86  						.getName().startsWith("java")
87  						&& !clazz.getPackage().getName().startsWith("com.db4o"));
88  	}
89  	
90  	public static final boolean canBeEnhanced(Class clazz) {
91  		return !Modifier.isFinal(clazz.getModifiers())
92  				&& !clazz.isInterface()
93  				&& !clazz.isAnnotation()
94  				&& !clazz.isPrimitive()
95  				&& !clazz.isArray()
96  				&& !clazz.isAnonymousClass()
97  				&& !Activatable.class.isAssignableFrom(clazz);
98  	}	
99  	
100 	public static String explainWhyNotEnhanced(Class clazz) {
101 		List<String> reasons = new ArrayList<String>();
102 		StringBuilder sb = new StringBuilder();
103 		if (Modifier.isFinal(clazz.getModifiers()))
104 			reasons.add("modifiers are final");
105 		if (clazz.isInterface())
106 			reasons.add("class is an interface");
107 		if (clazz.isAnnotation())
108 			reasons.add("class is an annotation");
109 		if (clazz.isPrimitive())
110 			reasons.add("the class represents a primitive type");
111 		if (clazz.isArray())
112 			reasons.add("the class represents an array");
113 		if (clazz.isAnonymousClass())
114 			reasons.add("the class represents an anonymous class");
115 		if (Activatable.class.isAssignableFrom(clazz))
116 			reasons.add("the class already implements " + Activatable.class.getName());
117 //		if (!hasPublicConstructor(clazz))
118 //			reasons.add("no public constructors have been found");
119 //		if (clazz.getPackage().getName() == null)
120 //			reasons.add("the package is empty");
121 //		if (clazz.getPackage()
122 //							.getName().startsWith("java"))
123 //			reasons.add("the package starts with 'java.xxxx'");
124 //		if (clazz.getPackage().getName().startsWith("com.db4o"))
125 //			reasons.add("the class seems to be an internal db4o class (package starts with 'com.db4o')");
126 		boolean first = true;
127 		for (String reason:reasons) {
128 			if (!first)
129 				sb.append("\n");
130 			sb.append(reason);
131 			first = false;
132 		}
133 		return sb.toString();
134 	}
135 
136 	/**
137 	 * Creates Class[] array from the ReflectClass[] array
138 	 * 
139 	 * @param claxx
140 	 *          ReflectClass array
141 	 * @return Class[] array
142 	 */
143 	static Class[] toNative(ReflectClass[] claxx) {
144 		Class[] clazz = null;
145 		if (claxx != null) {
146 			clazz = new Class[claxx.length];
147 			for (int i = 0; i < claxx.length; i++) {
148 				clazz[i] = JdkReflector.toNative(claxx[i]);
149 			}
150 		}
151 		return clazz;
152 	}
153 
154 	private ReflectArray _array;
155 
156 	private final JdkLoader _classLoader;
157 
158 	private ClassPool _classPool;
159 
160 	private ReflectorConfiguration _config;
161 
162 	private JdkReflector _jdkReflect;
163 
164 	private Reflector _parent;
165 
166 	private boolean _threadLocal;
167 
168 	private PersistOnMethodPolicy _persistPolicy;
169 
170 	private ActivateOnMethodPolicy _activationPolicy;
171 
172 	private boolean _ignoreNullActivator;
173 
174 	/**
175 	 * Constructor
176 	 * 
177 	 * @param classLoader
178 	 *          class loader
179 	 */
180 	public JVSTReflector(ClassLoader classLoader) {
181 		this(new ClassLoaderJdkLoader(classLoader));
182 	}
183 	
184 	/**
185 	 * Constructor
186 	 * 
187 	 * @param classLoader
188 	 *          class loader
189 	 */
190 	public JVSTReflector(JdkLoader classLoader) {
191 		_classLoader = classLoader;
192 		_classPool = new ClassPool();
193 		_classPool.appendClassPath(new LoaderClassPath(Thread.currentThread()
194 				.getContextClassLoader()));
195 		_jdkReflect = new JdkReflector(classLoader);
196 		// _classPool.appendSystemPath();
197 	}
198 
199 	private JVSTReflector(JdkLoader classLoader, ReflectorConfiguration config){
200 		_classLoader = classLoader;
201 		_config = config;
202 	}
203 
204 	/**
205 	 * ReflectArray factory
206 	 * 
207 	 * @return ReflectArray instance
208 	 */
209 	public ReflectArray array() {
210 		if (_array == null) {
211 			_array = new JVSTArray(parent());
212 		}
213 		return _array;
214 	}
215 	
216 	public ReflectorConfiguration configuration() {
217 		return _config;
218 	}	
219 
220 	public void configuration(ReflectorConfiguration config) {
221 		_config = config;
222 		_jdkReflect.configuration(config);
223 	}
224 
225 	private ReflectClass findInCache(Class clazz) {
226 		List<ClassData> clList = _classCache.get(clazz);
227 		if (clList == null)
228 			return null;
229 		if (debug)
230 			System.out.println(clazz.getName() + " - cache contains " + clList.size() + " items " + clList.toString());
231 		for (ClassData clData:clList) {
232 			if (clData.ignoreNullActivator == _ignoreNullActivator && clData.threadLocal == _threadLocal)
233 				return clData._reflectClass;
234 		}
235 		return null;
236 	}
237 	
238 	protected ReflectClass createClass(Class clazz) {				
239 		ReflectClass res = findInCache(clazz);
240 		if (res != null)
241 			return res;
242 		if (shouldBeEnhanced(clazz)) {
243 			if (debug)
244 				System.out.println("enhancing:" + clazz + " threadLocal:" + _threadLocal);
245 			res = enhanceClass(clazz);
246 		} else {
247 			res = new JdkClass(parent(), _jdkReflect, getRealClass(clazz));
248 			addToCache(clazz, res);
249 		}
250 		return res;
251 	}
252 
253 	private void addToCache(Class clazz, ReflectClass res) {
254 		ClassData clData = new ClassData();
255 		clData._reflectClass = res;
256 		clData.ignoreNullActivator = _ignoreNullActivator;
257 		clData.threadLocal = _threadLocal;
258 		List<ClassData> list = new ArrayList<ClassData>();
259 		list.add(clData);
260 		_classCache.put(clazz, list);
261 	} 
262 
263 	public JVSTClass enhanceClass(Class clazz) {
264 		ReflectClass res = findInCache(clazz);
265 		if (res != null && res instanceof JVSTClass)
266 			return (JVSTClass) res;
267 		JVSTClass jclazz = new JVSTClass(parent(), this, _classPool, clazz, _threadLocal, _ignoreNullActivator, _persistPolicy,_activationPolicy);
268 		addToCache(clazz, jclazz);
269 		return jclazz;
270 	}
271 	
272 	/**
273 	 * Creates a copy of the object
274 	 * 
275 	 * @param obj
276 	 *          object to copy
277 	 * @return object copy
278 	 */
279 	public Object deepClone(Object obj) {
280 		JVSTReflector clone = new JVSTReflector(_classLoader, _config);
281 		return clone;
282 	}
283 
284 	/**
285 	 * Returns ReflectClass for the specified class
286 	 * 
287 	 * @param clazz
288 	 *          class
289 	 * @return ReflectClass for the specified class
290 	 */
291 	public ReflectClass forClass(Class clazz) {
292 		return createClass(clazz);
293 	}
294 
295 	/**
296 	 * Returns ReflectClass for the specified class name
297 	 * 
298 	 * @param className
299 	 *          class name
300 	 * @return ReflectClass for the specified class name
301 	 */
302 	public ReflectClass forName(String className) {
303 		Class clazz = _classLoader.loadClass(className);
304 		if (clazz == null)
305 			return null;
306 		return createClass(clazz);
307 	}
308 
309 	/**
310 	 * Returns ReflectClass for the specified class object
311 	 * 
312 	 * @param a_object
313 	 *          class object
314 	 * @return ReflectClass for the specified class object
315 	 */
316 	public ReflectClass forObject(Object a_object) {
317 		if (a_object == null)
318 			return null;
319 		return parent().forClass(a_object.getClass());
320 	}
321 	
322 	public ActivateOnMethodPolicy getActivationPolicy() {
323 		return _activationPolicy;
324 	}	
325 	
326 	public PersistOnMethodPolicy getPersistPolicy() {
327 		return _persistPolicy;
328 	}
329 
330 	/**
331 	 * Method stub. Returns false.
332 	 */
333 	public boolean isCollection(ReflectClass candidate) {
334 		return false;
335 	}
336 
337 	/**
338 	 * Method stub. Returns false.
339 	 */
340 	public boolean methodCallsSupported() {
341 		return true;
342 	}
343 
344 	Object nullValue(ReflectClass clazz) {
345 		return Platform4.nullValue(JdkReflector.toNative(clazz));
346 	}
347 
348 	private Reflector parent() {
349 		if(_parent == null){
350 			return this;
351 		}
352 		return _parent; 
353 	}
354 
355 	public void setActivationPolicy(ActivateOnMethodPolicy activationPolicy) {
356 		_activationPolicy = activationPolicy;
357 	}
358 
359 	/**
360 	 * Sets parent reflector
361 	 * 
362 	 * @param reflector
363 	 *          parent reflector
364 	 */
365 	public void setParent(Reflector reflector) {
366 		_parent = reflector;
367 	}
368 
369 	public void setPersistPolicy(PersistOnMethodPolicy persistPolicy) {
370 		_persistPolicy = persistPolicy;
371 	}
372 
373 	public void setThreadLocal(boolean threadLocal) {
374 		_threadLocal = threadLocal;
375 	}
376 	
377 	public void setIgnoreNullActivator(boolean ignore) {
378 		_ignoreNullActivator = ignore;
379 	}
380 
381 }