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.Field; 021 import java.lang.reflect.Method; 022 import java.lang.reflect.Modifier; 023 import java.util.ArrayList; 024 import java.util.List; 025 026 import javassist.CannotCompileException; 027 import javassist.ClassPool; 028 import javassist.CtClass; 029 import javassist.CtConstructor; 030 import javassist.CtField; 031 import javassist.CtMethod; 032 import javassist.CtNewMethod; 033 import net.ep.db4o.activator.TransparentActivation; 034 import net.ep.db4o.annotations.NoEnhancement; 035 036 import com.db4o.activation.ActivationPurpose; 037 import com.db4o.activation.Activator; 038 import com.db4o.internal.Platform4; 039 import com.db4o.reflect.ReflectClass; 040 import com.db4o.reflect.ReflectField; 041 import com.db4o.reflect.ReflectMethod; 042 import com.db4o.reflect.Reflector; 043 import com.db4o.reflect.core.ConstructorSupport; 044 import com.db4o.reflect.core.ReflectConstructor; 045 import com.db4o.reflect.core.ReflectConstructorSpec; 046 import com.db4o.reflect.jdk.JavaReflectClass; 047 import com.db4o.reflect.jdk.JdkConstructor; 048 import com.db4o.reflect.jdk.JdkMethod; 049 import com.db4o.reflect.jdk.JdkReflector; 050 051 @SuppressWarnings("unchecked") 052 public class JVSTClass implements JavaReflectClass { 053 054 public static final String ACTIVATOR_FIELD = "_activator"; 055 056 private ReflectConstructorSpec _constructorSpec; 057 058 // private Object[] _constructorParams; 059 060 private final Class _enhancedClass; 061 062 private final Class _realClazz; 063 064 private final JVSTReflector _jVSTreflector; 065 066 private final Reflector _reflector; 067 068 private boolean _threadLocal; 069 070 private PersistOnMethodPolicy _persistPolicy; 071 072 private ActivateOnMethodPolicy _activatePolicy; 073 074 private boolean _ignoreNullActivator; 075 076 private static final boolean debug = false; 077 078 public JVSTClass(Reflector reflector, JVSTReflector jreflector, 079 ClassPool pool, Class clazz, boolean threadLocal, boolean ignoreNullActivator, 080 PersistOnMethodPolicy persistPolicy, ActivateOnMethodPolicy activatePolicy) { 081 if (reflector == null) { 082 throw new NullPointerException(); 083 } 084 if (jreflector == null) { 085 throw new NullPointerException(); 086 } 087 _jVSTreflector = jreflector; 088 _reflector = reflector; 089 _realClazz = clazz; 090 _threadLocal = threadLocal; 091 _ignoreNullActivator = ignoreNullActivator; 092 _persistPolicy = persistPolicy; 093 _activatePolicy = activatePolicy; 094 _constructorSpec = ReflectConstructorSpec.UNSPECIFIED_CONSTRUCTOR; 095 try { 096 CtClass cClass = pool.get(_realClazz.getName()); 097 String ext = "TA$" + Long.toHexString(System.currentTimeMillis()); 098 if (_threadLocal) 099 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 }