001 /** 002 * Copyright (C) 2009 Erik Putrycz <erik.putrycz@gmail.com> 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017 package net.ep.db4o.javassist; 018 019 import java.lang.reflect.Constructor; 020 import java.lang.reflect.Modifier; 021 import java.util.ArrayList; 022 import java.util.HashMap; 023 import java.util.List; 024 import java.util.Map; 025 026 import javassist.ClassPool; 027 import javassist.LoaderClassPath; 028 import net.ep.db4o.activator.TransparentActivation; 029 030 import com.db4o.internal.Platform4; 031 import com.db4o.reflect.ReflectArray; 032 import com.db4o.reflect.ReflectClass; 033 import com.db4o.reflect.Reflector; 034 import com.db4o.reflect.ReflectorConfiguration; 035 import com.db4o.reflect.jdk.ClassLoaderJdkLoader; 036 import com.db4o.reflect.jdk.JdkClass; 037 import com.db4o.reflect.jdk.JdkLoader; 038 import com.db4o.reflect.jdk.JdkReflector; 039 import com.db4o.ta.Activatable; 040 041 @SuppressWarnings("unchecked") 042 public class JVSTReflector implements Reflector { 043 044 private static final boolean debug = false; 045 046 047 private static final Map<Class, List<ClassData>> _classCache = new HashMap<Class, List<ClassData>>(); 048 049 public static class ClassData { 050 ReflectClass _reflectClass; 051 boolean ignoreNullActivator; 052 boolean threadLocal; 053 @Override 054 public String toString() { 055 return _reflectClass + " ignoreNullActivator:" + ignoreNullActivator + " threadLocal:" + threadLocal; 056 } 057 } 058 059 public static final <T> Class<? super T> getRealClass(Class<? super T> clazz) { 060 if (isEnhanced(clazz)) { 061 clazz = clazz.getSuperclass(); 062 } 063 return clazz; 064 } 065 066 public static final boolean hasPublicConstructor(Class clazz) { 067 boolean hasPublic = true; 068 for (Constructor constr : clazz.getDeclaredConstructors()) { 069 if (Modifier.isPublic(constr.getModifiers())) 070 return true; 071 hasPublic = false; 072 } 073 return hasPublic; 074 } 075 076 public static final boolean isEnhanced(Class clazz) { 077 if (clazz.equals(TransparentActivation.class)) 078 return false; 079 return TransparentActivation.class.isAssignableFrom(clazz); 080 } 081 082 public static final boolean shouldBeEnhanced(Class clazz) { 083 return canBeEnhanced(clazz) 084 && hasPublicConstructor(clazz) 085 && (clazz.getPackage().getName() == null || !clazz.getPackage() 086 .getName().startsWith("java") 087 && !clazz.getPackage().getName().startsWith("com.db4o")); 088 } 089 090 public static final boolean canBeEnhanced(Class clazz) { 091 return !Modifier.isFinal(clazz.getModifiers()) 092 && !clazz.isInterface() 093 && !clazz.isAnnotation() 094 && !clazz.isPrimitive() 095 && !clazz.isArray() 096 && !clazz.isAnonymousClass() 097 && !Activatable.class.isAssignableFrom(clazz); 098 } 099 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 }