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 }