KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > tc > object > TCClassImpl


1 /*
2  * All content copyright (c) 2003-2006 Terracotta, Inc., except as may otherwise be noted in a separate copyright notice. All rights reserved.
3  */

4 package com.tc.object;
5
6 import sun.misc.Unsafe;
7
8 import com.tc.exception.TCRuntimeException;
9 import com.tc.logging.TCLogger;
10 import com.tc.logging.TCLogging;
11 import com.tc.object.applicator.ChangeApplicator;
12 import com.tc.object.dna.api.DNA;
13 import com.tc.object.dna.api.DNAWriter;
14 import com.tc.object.dna.impl.ProxyInstance;
15 import com.tc.object.field.TCField;
16 import com.tc.object.field.TCFieldFactory;
17 import com.tc.object.loaders.Namespace;
18 import com.tc.object.tx.optimistic.OptimisticTransactionManager;
19 import com.tc.util.Assert;
20 import com.tc.util.ClassUtils;
21 import com.tc.util.ReflectionUtil;
22 import com.tc.util.UnsafeUtil;
23
24 import java.io.IOException JavaDoc;
25 import java.lang.reflect.Constructor JavaDoc;
26 import java.lang.reflect.Field JavaDoc;
27 import java.lang.reflect.Modifier JavaDoc;
28 import java.lang.reflect.Proxy JavaDoc;
29 import java.util.ConcurrentModificationException JavaDoc;
30 import java.util.HashMap JavaDoc;
31 import java.util.Iterator JavaDoc;
32 import java.util.LinkedList JavaDoc;
33 import java.util.Map JavaDoc;
34
35 /**
36  * Peer of a Class under management.
37  * <p>
38  * This is used to cache the fields of each class by type.
39  *
40  * @author orion
41  */

42 public class TCClassImpl implements TCClass {
43   private final static TCLogger logger = TCLogging.getLogger(TCClassImpl.class);
44   private final static Unsafe unsafe = UnsafeUtil.getUnsafe();
45
46   /**
47    * Peer java class that this TCClass represents.
48    */

49   private final Class JavaDoc peer;
50
51   private final TCClass superclazz;
52   private final TCClassFactory clazzFactory;
53   private final TCField[] portableFields;
54   private final boolean indexed;
55   private final boolean isNonStaticInner;
56   private final boolean isLogical;
57   private final boolean isCallConstructor;
58   private final String JavaDoc onLoadScript;
59   private final String JavaDoc onLoadMethod;
60   private final ChangeApplicator applicator;
61   private final String JavaDoc parentFieldName;
62   private final Map JavaDoc declaredTCFieldsByName = new HashMap JavaDoc();
63   private final Map JavaDoc tcFieldsByName = new HashMap JavaDoc();
64   private final String JavaDoc loaderDesc;
65   private Constructor JavaDoc constructor = null;
66   private final Field parentField;
67   private final static SerializationUtil SERIALIZATION_UTIL = new SerializationUtil();
68   private final boolean useNonDefaultConstructor;
69   private Map JavaDoc offsetToFields;
70   private final ClientObjectManager objectManager;
71   private final boolean isProxyClass;
72   private final boolean isEnum;
73
74   private final String JavaDoc logicalExtendingClassName;
75   private final Class JavaDoc logicalSuperClass;
76
77   TCClassImpl(TCFieldFactory factory, TCClassFactory clazzFactory, ClientObjectManager objectManager, Class JavaDoc peer,
78               Class JavaDoc logicalSuperClass, String JavaDoc loaderDesc, String JavaDoc logicalExtendingClassName, boolean isLogical,
79               boolean isCallConstructor, String JavaDoc onLoadScript, String JavaDoc onLoadMethod, boolean useNonDefaultConstructor) {
80     this.clazzFactory = clazzFactory;
81     this.objectManager = objectManager;
82     this.peer = peer;
83     this.loaderDesc = loaderDesc;
84     this.indexed = peer.isArray();
85
86     boolean isStatic = Modifier.isStatic(peer.getModifiers());
87     boolean mightBeInner = peer.getName().indexOf('$') != -1 && !isIndexed();
88     this.parentField = mightBeInner && !isStatic ? findParentField() : null;
89     this.isNonStaticInner = parentField != null;
90     this.parentFieldName = parentField == null ? null : getName() + '.' + parentField.getName();
91
92     this.isLogical = isLogical;
93     this.isProxyClass = Proxy.isProxyClass(peer) || ProxyInstance.class.getName().equals(peer.getName());
94     this.isCallConstructor = isCallConstructor;
95     this.onLoadScript = onLoadScript;
96     this.onLoadMethod = onLoadMethod;
97     this.superclazz = findSuperClass(peer);
98     this.isEnum = ClassUtils.isEnum(peer);
99     this.logicalExtendingClassName = logicalExtendingClassName;
100
101     this.applicator = createApplicator();
102
103     introspectFields(peer, factory);
104     this.portableFields = createPortableFields();
105     this.useNonDefaultConstructor = isProxyClass || ClassUtils.isPortableReflectionClass(peer) || useNonDefaultConstructor;
106     this.logicalSuperClass = logicalSuperClass;
107   }
108
109   public Field getParentField() {
110     return parentField;
111   }
112
113   public boolean isNonStaticInner() {
114     return this.isNonStaticInner;
115   }
116
117   public Class JavaDoc getPeerClass() {
118     return this.peer;
119   }
120
121   private Field findParentField() {
122     Field[] fields = peer.getDeclaredFields();
123     for (int i = 0; i < fields.length; i++) {
124       if (SERIALIZATION_UTIL.isParent(fields[i].getName())) return fields[i];
125     }
126     return null;
127   }
128
129   private TCClass findSuperClass(Class JavaDoc c) {
130     Class JavaDoc superclass = c.getSuperclass();
131     if (superclass != null) { return clazzFactory.getOrCreate(superclass, objectManager); }
132     return null;
133   }
134
135   private ChangeApplicator createApplicator() {
136     return clazzFactory.createApplicatorFor(this, indexed);
137   }
138
139   public void hydrate(TCObject tcObject, DNA dna, Object JavaDoc pojo, boolean force) throws IOException JavaDoc,
140       ClassNotFoundException JavaDoc {
141     // Okay...long story here The application of the DNA used to be a synchronized(applicator) block. As best as Steve
142
// and I could tell, the synchronization was solely a memory boundary and not a mutual exlusion mechanism. For the
143
// time being, we have resolved that we need no synchronization here (either for memory, or exclusion). The memory
144
// barrier aspect isn't known to be a problem and the concurrency is handled by the server (ie. we won't get
145
// concurrent updates). At some point it would be a good idea to detect (and error out) when updates are received
146
// from L2 but local read/writes have been made on the target TCObject
147

148     final long localVersion = tcObject.getVersion();
149     final long dnaVersion = dna.getVersion();
150
151     if (force || (localVersion < dnaVersion)) {
152       tcObject.setVersion(dnaVersion);
153       applicator.hydrate(objectManager, tcObject, dna, pojo);
154     } else {
155       if (logger.isDebugEnabled()) {
156         logger.debug("IGNORING UPDATE, local object at version " + localVersion + ", dna update is version "
157                      + dnaVersion);
158       }
159     }
160
161   }
162
163   public void dehydrate(TCObject tcObject, DNAWriter writer, Object JavaDoc pojo) {
164     try {
165       applicator.dehydrate(objectManager, tcObject, writer, pojo);
166     } catch (ConcurrentModificationException JavaDoc cme) {
167       // try to log some useful stuff about the pojo in question here.
168
// This indicates improper locking, but is certainly possible
169
String JavaDoc type = pojo == null ? "null" : pojo.getClass().getName();
170       String JavaDoc toString = String.valueOf(pojo);
171       int ihc = System.identityHashCode(pojo);
172       logger.error("Shared object (presumably new) modified during dehydrate (type " + type + ", ihc " + ihc + "): "
173                    + toString, cme);
174       throw cme;
175     }
176   }
177
178   public Class JavaDoc getComponentType() {
179     return peer.getComponentType();
180   }
181
182   public boolean isEnum() {
183     return isEnum;
184   }
185
186   public String JavaDoc getName() {
187     if (isProxyClass) { return ProxyInstance.class.getName(); }
188     if (isEnum) { return LiteralValues.ENUM_CLASS_DOTS; }
189     return peer.getName();
190   }
191
192   public String JavaDoc getExtendingClassName() {
193     String JavaDoc className = getName();
194     if (this.logicalExtendingClassName != null) {
195       className = Namespace.createLogicalExtendingClassName(className, logicalExtendingClassName);
196     }
197     return className;
198   }
199
200   public TCClass getSuperclass() {
201     return superclazz;
202   }
203
204   public synchronized Constructor JavaDoc getConstructor() {
205     if (constructor == null) {
206       // As best as I can tell, the reason for the lazy initialization here is that we don't actually need the cstr
207
// looked up for all of the TCClass instances we cook up. Additionally, the assertions in findConstructor will go
208
// off for a fair number of abstract base classes (eg. java.util.AbstractMap, java.util.Dictionary, etc)
209
constructor = findConstructor();
210     }
211     return constructor;
212   }
213
214   public boolean hasOnLoadExecuteScript() {
215     return onLoadScript != null;
216   }
217
218   public String JavaDoc getOnLoadExecuteScript() {
219     Assert.eval(hasOnLoadExecuteScript());
220     return onLoadScript;
221   }
222
223   public String JavaDoc getOnLoadMethod() {
224     Assert.eval(hasOnLoadMethod());
225     return onLoadMethod;
226   }
227
228   private Constructor JavaDoc findConstructor() {
229     Constructor JavaDoc rv = null;
230
231     if (isCallConstructor || isLogical) {
232       Constructor JavaDoc[] cons = peer.getDeclaredConstructors();
233       for (int i = 0; i < cons.length; i++) {
234         Class JavaDoc[] types = cons[i].getParameterTypes();
235         if (types.length == 0) {
236           rv = cons[i];
237           rv.setAccessible(true);
238           return rv;
239         }
240       }
241     }
242
243     if (rv == null) {
244       rv = ReflectionUtil.newConstructor(peer, logicalSuperClass);
245       rv.setAccessible(true);
246     }
247     return rv;
248   }
249
250   public String JavaDoc getParentFieldName() {
251     return parentFieldName;
252   }
253
254   private void introspectFields(Class JavaDoc clazz, TCFieldFactory fieldFactory) {
255     // Note: this gets us all of the fields declared in the class, static
256
// as well as instance fields.
257
Field[] fields = clazz.equals(Object JavaDoc.class) ? new Field[0] : clazz.getDeclaredFields();
258
259     Field field;
260     TCField tcField;
261     for (int i = 0; i < fields.length; i++) {
262       field = fields[i];
263       // The factory does a bunch of callbacks based on the field type.
264
tcField = fieldFactory.getInstance(this, field);
265       declaredTCFieldsByName.put(field.getName(), tcField);
266       tcFieldsByName.put(tcField.getName(), tcField);
267     }
268   }
269
270   public String JavaDoc toString() {
271     return peer.getName();
272   }
273
274   /**
275    * Expects the field name in the format <classname>. <fieldname>(e.g. com.foo.Bar.baz)
276    */

277   public TCField getField(String JavaDoc name) {
278     TCField rv = (TCField) tcFieldsByName.get(name);
279     if (rv == null && superclazz != null) {
280       rv = superclazz.getField(name);
281     }
282     return rv;
283   }
284
285   public TCField[] getPortableFields() {
286     return portableFields;
287   }
288
289   public TraversedReferences getPortableObjects(Object JavaDoc pojo, TraversedReferences addTo) {
290     return applicator.getPortableObjects(pojo, addTo);
291   }
292
293   private TCField[] createPortableFields() {
294     if (isLogical || !objectManager.isPortableClass(this.peer)) { return new TCField[0]; }
295     LinkedList JavaDoc l = new LinkedList JavaDoc();
296     for (Iterator JavaDoc i = declaredTCFieldsByName.values().iterator(); i.hasNext();) {
297
298       TCField f = (TCField) i.next();
299       if (f.isPortable()) {
300         l.add(f);
301       }
302     }
303     return (TCField[]) l.toArray(new TCField[l.size()]);
304   }
305
306   public Map JavaDoc connectedCopy(Object JavaDoc source, Object JavaDoc dest, Map JavaDoc visited, OptimisticTransactionManager txManager) {
307     return this.applicator.connectedCopy(source, dest, visited, objectManager, txManager);
308   }
309
310   public boolean isIndexed() {
311     return indexed;
312   }
313
314   public String JavaDoc getDefiningLoaderDescription() {
315     return loaderDesc;
316   }
317
318   public boolean isLogical() {
319     return isLogical;
320   }
321
322   public ClientObjectManager getObjectManager() {
323     return objectManager;
324   }
325
326   public TCObject createTCObject(ObjectID id, Object JavaDoc pojo) {
327     if (isLogical) {
328       return new TCObjectLogical(objectManager.getReferenceQueue(), id, pojo, this);
329     } else {
330       return new TCObjectPhysical(objectManager.getReferenceQueue(), id, pojo, this);
331     }
332   }
333
334   public boolean hasOnLoadMethod() {
335     return onLoadMethod != null;
336   }
337
338   public boolean isUseNonDefaultConstructor() {
339     return useNonDefaultConstructor;
340   }
341
342   public Object JavaDoc getNewInstanceFromNonDefaultConstructor(DNA dna) throws IOException JavaDoc, ClassNotFoundException JavaDoc {
343     Object JavaDoc o = applicator.getNewInstance(objectManager, dna);
344
345     if (o == null) { throw new AssertionError JavaDoc("Can't find suitable constructor for class: " + getName() + "."); }
346     return o;
347   }
348
349   public String JavaDoc getFieldNameByOffset(long fieldOffset) {
350     Long JavaDoc fieldOffsetObj = new Long JavaDoc(fieldOffset);
351     if (offsetToFields == null) {
352       offsetToFields = new HashMap JavaDoc();
353       if (unsafe != null) {
354         try {
355           Field[] fields = peer.equals(Object JavaDoc.class) ? new Field[0] : peer.getDeclaredFields();
356           for (int i = 0; i < fields.length; i++) {
357             try {
358               if (!Modifier.isStatic(fields[i].getModifiers())) {
359                 fields[i].setAccessible(true);
360                 offsetToFields.put(new Long JavaDoc(unsafe.objectFieldOffset(fields[i])), fields[i]);
361               }
362             } catch (Exception JavaDoc e) {
363               // Ignore those fields that throw an exception
364
}
365           }
366         } catch (Exception JavaDoc e) {
367           throw new TCRuntimeException(e);
368         }
369       }
370     }
371     Field field = (Field) this.offsetToFields.get(fieldOffsetObj);
372     if (field == null) {
373       if (superclazz != null) {
374         return superclazz.getFieldNameByOffset(fieldOffset);
375       } else {
376         throw new AssertionError JavaDoc("Field does not exist for offset: " + fieldOffset);
377       }
378     } else {
379       StringBuffer JavaDoc sb = new StringBuffer JavaDoc(field.getDeclaringClass().getName());
380       sb.append(".");
381       sb.append(field.getName());
382       return sb.toString();
383     }
384   }
385
386   public boolean isProxyClass() {
387     return isProxyClass;
388   }
389 }
390
Popular Tags