KickJava   Java API By Example, From Geeks To Geeks.

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


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

5 package com.tc.object;
6
7 import com.tc.exception.TCClassNotFoundException;
8 import com.tc.exception.TCNonPortableObjectError;
9 import com.tc.exception.TCRuntimeException;
10 import com.tc.logging.ChannelIDLogger;
11 import com.tc.logging.CustomerLogging;
12 import com.tc.logging.TCLogger;
13 import com.tc.logging.TCLogging;
14 import com.tc.net.protocol.tcm.ChannelIDProvider;
15 import com.tc.object.appevent.NonPortableEventContext;
16 import com.tc.object.appevent.NonPortableEventContextFactory;
17 import com.tc.object.appevent.NonPortableFieldSetContext;
18 import com.tc.object.appevent.NonPortableObjectEvent;
19 import com.tc.object.bytecode.Manageable;
20 import com.tc.object.bytecode.ManagerUtil;
21 import com.tc.object.bytecode.TransparentAccess;
22 import com.tc.object.cache.CacheStats;
23 import com.tc.object.cache.Evictable;
24 import com.tc.object.cache.EvictionPolicy;
25 import com.tc.object.config.DSOClientConfigHelper;
26 import com.tc.object.dna.api.DNA;
27 import com.tc.object.idprovider.api.ObjectIDProvider;
28 import com.tc.object.loaders.ClassProvider;
29 import com.tc.object.loaders.Namespace;
30 import com.tc.object.logging.RuntimeLogger;
31 import com.tc.object.msg.JMXMessage;
32 import com.tc.object.net.DSOClientMessageChannel;
33 import com.tc.object.tx.ClientTransactionManager;
34 import com.tc.object.tx.optimistic.OptimisticTransactionManager;
35 import com.tc.object.tx.optimistic.TCObjectClone;
36 import com.tc.object.util.IdentityWeakHashMap;
37 import com.tc.object.walker.ObjectGraphWalker;
38 import com.tc.text.ConsoleNonPortableReasonFormatter;
39 import com.tc.text.ConsoleParagraphFormatter;
40 import com.tc.text.NonPortableReasonFormatter;
41 import com.tc.text.ParagraphFormatter;
42 import com.tc.text.StringFormatter;
43 import com.tc.util.Assert;
44 import com.tc.util.NonPortableReason;
45 import com.tc.util.State;
46 import com.tc.util.Util;
47 import com.tc.util.concurrent.StoppableThread;
48
49 import java.io.PrintWriter JavaDoc;
50 import java.io.StringWriter JavaDoc;
51 import java.lang.ref.ReferenceQueue JavaDoc;
52 import java.lang.reflect.Array JavaDoc;
53 import java.lang.reflect.InvocationHandler JavaDoc;
54 import java.lang.reflect.InvocationTargetException JavaDoc;
55 import java.lang.reflect.Method JavaDoc;
56 import java.lang.reflect.Proxy JavaDoc;
57 import java.util.ArrayList JavaDoc;
58 import java.util.Collection JavaDoc;
59 import java.util.Collections JavaDoc;
60 import java.util.HashMap JavaDoc;
61 import java.util.HashSet JavaDoc;
62 import java.util.IdentityHashMap JavaDoc;
63 import java.util.Iterator JavaDoc;
64 import java.util.List JavaDoc;
65 import java.util.Map JavaDoc;
66 import java.util.Set JavaDoc;
67
68 public class ClientObjectManagerImpl implements ClientObjectManager, PortableObjectProvider, Evictable {
69
70   private static final State PAUSED = new State("PAUSED");
71   private static final State STARTING = new State("STARTING");
72   private static final State RUNNING = new State("RUNNING");
73
74   private static final LiteralValues literals = new LiteralValues();
75   private static final TCLogger staticLogger = TCLogging.getLogger(ClientObjectManager.class);
76
77   private static final long POLL_TIME = 1000;
78   private static final long STOP_WAIT = POLL_TIME * 3;
79
80   private static final int NO_DEPTH = 0;
81
82   private static final int COMMIT_SIZE = 100;
83
84   private State state = RUNNING;
85   private final Object JavaDoc shutdownLock = new Object JavaDoc();
86   private final Map roots = new HashMap();
87   private final Map idToManaged = new HashMap();
88   private final Map pojoToManaged = new IdentityWeakHashMap();
89   private final ClassProvider classProvider;
90   private final RemoteObjectManager remoteObjectManager;
91   private final EvictionPolicy cache;
92   private final Traverser traverser;
93   private final Traverser shareObjectsTraverser;
94   private final TraverseTest traverseTest;
95   private final DSOClientConfigHelper clientConfiguration;
96   private final TCClassFactory clazzFactory;
97   private final Set JavaDoc objectLookupsInProgress = new HashSet JavaDoc();
98   private final Set JavaDoc rootLookupsInProgress = new HashSet JavaDoc();
99   private final ObjectIDProvider idProvider;
100   private final ReferenceQueue JavaDoc referenceQueue = new ReferenceQueue JavaDoc();
101   private final TCObjectFactory factory;
102
103   private ClientTransactionManager txManager;
104
105   private StoppableThread reaper = null;
106   private final TCLogger logger;
107   private final RuntimeLogger runtimeLogger;
108   private final NonPortableEventContextFactory nonPortableContextFactory;
109   private final ThreadLocal JavaDoc localCreationInProgress = new ThreadLocal JavaDoc();
110   private final Set JavaDoc pendingCreateTCObjects = new HashSet JavaDoc();
111   private final Portability portability;
112   private final DSOClientMessageChannel channel;
113
114   public ClientObjectManagerImpl(RemoteObjectManager remoteObjectManager, DSOClientConfigHelper clientConfiguration,
115                                  ObjectIDProvider idProvider, EvictionPolicy cache, RuntimeLogger runtimeLogger,
116                                  ChannelIDProvider provider, ClassProvider classProvider, TCClassFactory classFactory,
117                                  TCObjectFactory objectFactory, Portability portability, DSOClientMessageChannel channel) {
118     this.remoteObjectManager = remoteObjectManager;
119     this.cache = cache;
120     this.clientConfiguration = clientConfiguration;
121     this.idProvider = idProvider;
122     this.runtimeLogger = runtimeLogger;
123     this.portability = portability;
124     this.channel = channel;
125     this.logger = new ChannelIDLogger(provider, TCLogging.getLogger(ClientObjectManager.class));
126     this.classProvider = classProvider;
127     this.traverseTest = new NewObjectTraverseTest();
128     this.traverser = new Traverser(new AddManagedObjectAction(), this);
129     this.shareObjectsTraverser = new Traverser(new SharedObjectsAction(), this);
130     this.clazzFactory = classFactory;
131     this.factory = objectFactory;
132     this.factory.setObjectManager(this);
133     this.nonPortableContextFactory = new NonPortableEventContextFactory(provider);
134
135     if (logger.isDebugEnabled()) {
136       logger.debug("Starting up ClientObjectManager:" + System.identityHashCode(this) + ". Cache SIZE = "
137                    + cache.getCacheCapacity());
138     }
139     startReaper();
140   }
141
142   public Class JavaDoc getClassFor(String JavaDoc className, String JavaDoc loaderDesc) throws ClassNotFoundException JavaDoc {
143     return classProvider.getClassFor(className, loaderDesc);
144   }
145
146   public synchronized void pause() {
147     assertNotPaused("Attempt to pause while PAUSED");
148     state = PAUSED;
149     notifyAll();
150   }
151
152   public synchronized void starting() {
153     assertPaused("Attempt to start while not PAUSED");
154     state = STARTING;
155     notifyAll();
156   }
157
158   public synchronized void unpause() {
159     assertStarting("Attempt to unpause while not STARTING");
160     state = RUNNING;
161     notifyAll();
162   }
163
164   public Object JavaDoc createParentCopyInstanceIfNecessary(Map visited, Map cloned, Object JavaDoc v) {
165     TCClass tcc = getOrCreateClass(v.getClass());
166     Object JavaDoc parent = null;
167     if (tcc.isNonStaticInner()) {
168       TransparentAccess access = (TransparentAccess) v;
169       Map m = new HashMap();
170       access.__tc_getallfields(m);
171       Object JavaDoc p = m.get(tcc.getParentFieldName());
172       parent = visited.containsKey(p) ? visited.get(p) : createNewCopyInstance(p, null);
173       visited.put(p, parent);
174       cloned.put(p, parent);
175     }
176     return parent;
177   }
178
179   private void waitUntilRunning() {
180     boolean isInterrupted = false;
181
182     while (state != RUNNING) {
183       try {
184         wait();
185       } catch (InterruptedException JavaDoc e) {
186         isInterrupted = true;
187       }
188     }
189     Util.selfInterruptIfNeeded(isInterrupted);
190   }
191
192   private void assertPaused(Object JavaDoc message) {
193     if (state != PAUSED) throw new AssertionError JavaDoc(message + ": " + state);
194   }
195
196   private void assertStarting(Object JavaDoc message) {
197     if (state != STARTING) throw new AssertionError JavaDoc(message + ": " + state);
198   }
199
200   private void assertNotPaused(Object JavaDoc message) {
201     if (state == PAUSED) throw new AssertionError JavaDoc(message + ": " + state);
202   }
203
204   public TraversedReferences getPortableObjects(Class JavaDoc clazz, Object JavaDoc start, TraversedReferences addTo) {
205     TCClass tcc = clazzFactory.getOrCreate(clazz, this);
206     return tcc.getPortableObjects(start, addTo);
207   }
208
209   public void setTransactionManager(ClientTransactionManager txManager) {
210     this.txManager = txManager;
211   }
212
213   public ClientTransactionManager getTransactionManager() {
214     return txManager;
215   }
216
217   /**
218    * Deep connected copy used to create stable views on collections of objects. While inefficient this should do that
219    * job. It is important that this method be called holding a distributed lock in order to prevent an unstable view. It
220    * also must be called in an optimistic transaction I'll probably move this out of the client object manager at some
221    * point but we'll see.
222    */

223   public Object JavaDoc deepCopy(Object JavaDoc source, OptimisticTransactionManager optimisticTxManager) {
224     IdentityHashMap JavaDoc cloned = new IdentityHashMap JavaDoc();
225     IdentityHashMap JavaDoc visited = new IdentityHashMap JavaDoc();
226
227     Object JavaDoc parent = this.createParentCopyInstanceIfNecessary(visited, cloned, source);
228     Object JavaDoc copy = createNewCopyInstance(source, parent);
229
230     Assert.eval(copy != null);
231
232     visited.put(source, copy);
233     optimisticTxManager.addClonesToTransaction(visited);
234
235     cloneAndUpdate(optimisticTxManager, cloned, visited, source, copy);
236     while (!cloned.isEmpty()) {
237       Object JavaDoc original = cloned.keySet().iterator().next(); // ick
238
Object JavaDoc clone = cloned.get(original);
239       cloned.remove(original);
240       cloneAndUpdate(optimisticTxManager, cloned, visited, original, clone);
241     }
242
243     return copy;
244   }
245
246   /**
247    * While holding the resolve lock to protect against the cleaner create a new copy of the original that is connected
248    * to the copy and has any references replaced with either an existing clone or a new clone where needed. New clones
249    * created in the connected copy are returned so that they can be properly updated from their originals. The reason
250    * for this strategy is to avoid recurrsion (and stack over flows)
251    */

252   private void cloneAndUpdate(OptimisticTransactionManager optimisticTxManager, IdentityHashMap JavaDoc cloned,
253                               IdentityHashMap JavaDoc visited, Object JavaDoc original, Object JavaDoc clone) {
254     TCClass tcc;
255     TCObject tco;
256     tcc = this.getOrCreateClass(original.getClass());
257     tco = this.lookupExistingOrNull(original);
258     synchronized (tco.getResolveLock()) {
259       tco.resolveAllReferences();
260       Map c = tcc.connectedCopy(original, clone, visited, optimisticTxManager);
261       optimisticTxManager.addClonesToTransaction(c);
262       cloned.putAll(c);
263     }
264   }
265
266   private TCObject create(Object JavaDoc pojo, NonPortableEventContext context) {
267     addToManagedFromRoot(pojo, context);
268     return basicLookup(pojo);
269   }
270
271   private TCObject share(Object JavaDoc pojo, NonPortableEventContext context) {
272     addToSharedFromRoot(pojo, context);
273     return basicLookup(pojo);
274   }
275
276   public ReferenceQueue JavaDoc getReferenceQueue() {
277     return referenceQueue;
278   }
279
280   public void shutdown() {
281     synchronized (shutdownLock) {
282       if (reaper != null) {
283         try {
284           stopThread(reaper);
285         } finally {
286           reaper = null;
287         }
288       }
289     }
290   }
291
292   private static void stopThread(StoppableThread thread) {
293     try {
294       thread.stopAndWait(STOP_WAIT);
295     } finally {
296       if (thread.isAlive()) {
297         staticLogger.warn(thread.getName() + " is still alive");
298       }
299     }
300   }
301
302   public TCObject lookupOrCreate(Object JavaDoc pojo) {
303     if (pojo == null) return TCObjectFactory.NULL_TC_OBJECT;
304     return lookupOrCreateIfNecesary(pojo, this.nonPortableContextFactory.createNonPortableEventContext(pojo.getClass()
305         .getName()));
306   }
307
308   private TCObject lookupOrCreate(Object JavaDoc pojo, NonPortableEventContext context) {
309     if (pojo == null) return TCObjectFactory.NULL_TC_OBJECT;
310     return lookupOrCreateIfNecesary(pojo, context);
311   }
312
313   public TCObject lookupOrShare(Object JavaDoc pojo) {
314     if (pojo == null) return TCObjectFactory.NULL_TC_OBJECT;
315     return lookupOrShareIfNecesary(pojo, this.nonPortableContextFactory.createNonPortableEventContext(pojo.getClass()
316         .getName()));
317   }
318
319   private TCObject lookupOrShareIfNecesary(Object JavaDoc pojo, NonPortableEventContext context) {
320     Assert.assertNotNull(pojo);
321     TCObject obj = basicLookup(pojo);
322     if (obj == null || obj.isNew()) {
323       obj = share(pojo, context);
324     }
325     return obj;
326   }
327
328   private TCObject lookupOrCreateIfNecesary(Object JavaDoc pojo, NonPortableEventContext context) {
329     Assert.assertNotNull(pojo);
330     TCObject obj = basicLookup(pojo);
331     if (obj == null || obj.isNew()) {
332       obj = create(pojo, context);
333     }
334     return obj;
335   }
336
337   /**
338    * This method is created for situations in which a method needs to be taken place when an object moved from
339    * non-shared to shared. The method could be an instrumented method. For instance, for ConcurrentHashMap, we need to
340    * re-hash the objects already in the map because the hashing algorithm is different when a ConcurrentHashMap is
341    * shared. The rehash method is an instrumented method. This should be executed only once.
342    */

343   private void executePostCreateMethod(Object JavaDoc pojo) {
344     // This method used to use beanshell, but I changed it to reflection to hopefully avoid a deadlock -- CDV-130
345

346     String JavaDoc onLookupMethodName = clientConfiguration.getPostCreateMethodIfDefined(pojo.getClass().getName());
347     if (onLookupMethodName != null) {
348       try {
349         Method JavaDoc m = pojo.getClass().getDeclaredMethod(onLookupMethodName, new Class JavaDoc[] {});
350         m.setAccessible(true);
351         m.invoke(pojo, new Object JavaDoc[] {});
352       } catch (Throwable JavaDoc t) {
353         if (t instanceof InvocationTargetException JavaDoc) {
354           t = t.getCause();
355         }
356         logger.warn("postCreate method (" + onLookupMethodName + ") failed on object of " + pojo.getClass(), t);
357         throw new RuntimeException JavaDoc(t);
358       }
359     }
360   }
361
362   private TCObject lookupExistingLiteralRootOrNull(String JavaDoc rootName) {
363     ObjectID rootID = (ObjectID) roots.get(rootName);
364     return basicLookupByID(rootID);
365   }
366
367   public TCObject lookupExistingOrNull(Object JavaDoc pojo) {
368     return basicLookup(pojo);
369   }
370
371   public synchronized ObjectID lookupExistingObjectID(Object JavaDoc pojo) {
372     TCObject obj = basicLookup(pojo);
373     if (obj == null) { throw new AssertionError JavaDoc("Missing object ID for:" + pojo); }
374     return obj.getObjectID();
375   }
376
377   public void markReferenced(TCObject tcobj) {
378     cache.markReferenced(tcobj);
379   }
380
381   public Object JavaDoc lookupObjectNoDepth(ObjectID id) throws ClassNotFoundException JavaDoc {
382     return lookupObject(id, true);
383   }
384
385   public Object JavaDoc lookupObject(ObjectID objectID) throws ClassNotFoundException JavaDoc {
386     return lookupObject(objectID, false);
387   }
388
389   private Object JavaDoc lookupObject(ObjectID objectID, boolean noDepth) throws ClassNotFoundException JavaDoc {
390     if (objectID.isNull()) return null;
391     Object JavaDoc o = null;
392     while (o == null) {
393       final TCObject tco = lookup(objectID, noDepth);
394       if (tco == null) throw new AssertionError JavaDoc("TCObject was null for " + objectID);// continue;
395

396       o = tco.getPeerObject();
397       if (o == null) {
398         reap(objectID);
399       }
400     }
401     return o;
402   }
403
404   private void reap(ObjectID objectID) {
405     synchronized (this) {
406       if (!basicHasLocal(objectID)) {
407         if (logger.isDebugEnabled()) logger.debug(System.identityHashCode(this)
408                                                   + " Entry removed before reaper got the chance: " + objectID);
409       } else {
410         TCObjectImpl tcobj = (TCObjectImpl) basicLookupByID(objectID);
411         if (tcobj.isNull()) {
412           idToManaged.remove(objectID);
413           cache.remove(tcobj);
414           remoteObjectManager.removed(objectID);
415         }
416       }
417     }
418   }
419
420   public boolean isManaged(Object JavaDoc pojo) {
421     return pojo != null && !literals.isLiteral(pojo.getClass().getName()) && lookupExistingOrNull(pojo) != null;
422   }
423
424   public boolean isCreationInProgress() {
425     Map m = (Map) localCreationInProgress.get();
426     return (m != null) && (m.size() > 0);
427   }
428
429   // Dealing with the case where a map contains a map. The faulting will deadlock without this stuff
430
private TCObject getCreationInProgress(ObjectID id) {
431     Map m = (Map) localCreationInProgress.get();
432     if (m == null) return null;
433     return (TCObject) m.get(id);
434   }
435
436   private void setCreationInProgress(ObjectID id, Object JavaDoc obj) {
437     Map m = (Map) localCreationInProgress.get();
438     if (m == null) {
439       m = new HashMap();
440       localCreationInProgress.set(m);
441     }
442     m.put(id, obj);
443     txManager.disableTransactionLogging(); // We dont want to log changes to transaction until we hydrate the new
444
// object.
445
}
446
447   private void removeCreationInProgress(ObjectID id) {
448     Map m = (Map) localCreationInProgress.get();
449     Assert.assertNotNull(m);
450     m.remove(id);
451     txManager.enableTransactionLogging();
452   }
453
454   // Done
455

456   public TCObject lookup(ObjectID id) throws ClassNotFoundException JavaDoc {
457     return lookup(id, false);
458   }
459
460   private TCObject lookup(ObjectID id, boolean noDepth) throws ClassNotFoundException JavaDoc {
461     TCObject obj = null;
462     boolean retrieveNeeded = false;
463     boolean isInterrupted = false;
464
465     synchronized (this) {
466       while (obj == null) {
467         obj = basicLookupByID(id);
468         if (obj != null) return obj;
469         obj = getCreationInProgress(id);
470         if (obj != null) return obj;
471         if (!objectLookupInProgress(id)) {
472           retrieveNeeded = true;
473           markObjectLookupInProgress(id);
474           break;
475         } else {
476           try {
477             wait();
478           } catch (InterruptedException JavaDoc ie) {
479             isInterrupted = true;
480           }
481         }
482       }
483     }
484     Util.selfInterruptIfNeeded(isInterrupted);
485
486     if (retrieveNeeded) {
487       try {
488         DNA dna = noDepth ? remoteObjectManager.retrieve(id, NO_DEPTH) : remoteObjectManager.retrieve(id);
489         // obj = factory.getNewInstance(id, classProvider.getClassFor(dna.getTypeName(), dna
490
// .getDefiningLoaderDescription()));
491
obj = factory.getNewInstance(id, classProvider.getClassFor(Namespace.parseClassNameIfNecessary(dna
492             .getTypeName()), dna.getDefiningLoaderDescription()));
493         setCreationInProgress(id, obj);
494         Assert.assertFalse(dna.isDelta());
495         obj.hydrate(dna, false);
496       } catch (ClassNotFoundException JavaDoc e) {
497         logger.warn("Exception: ", e);
498         throw e;
499       } finally {
500         if (obj != null) removeCreationInProgress(id);
501       }
502       basicAddLocal(obj);
503     }
504     return obj;
505
506   }
507
508   public synchronized TCObject lookupIfLocal(ObjectID id) {
509     return basicLookupByID(id);
510   }
511
512   public synchronized Collection JavaDoc getAllObjectIDsAndClear(Collection JavaDoc c) {
513     assertStarting("Called when not in STARTING state !");
514     for (Iterator JavaDoc i = idToManaged.keySet().iterator(); i.hasNext();) {
515       c.add(i.next());
516     }
517     remoteObjectManager.clear();
518     return c;
519   }
520
521   public Object JavaDoc lookupRoot(String JavaDoc rootName) {
522     try {
523       return lookupRootOptionallyCreateOrReplace(rootName, null, false, true, false);
524     } catch (ClassNotFoundException JavaDoc e) {
525       throw new TCClassNotFoundException(e);
526     }
527   }
528
529   /**
530    * Check to see if the root is already in existence on the server. If it is then get it if not then create it.
531    */

532   public Object JavaDoc lookupOrCreateRoot(String JavaDoc rootName, Object JavaDoc root) {
533     try {
534       return lookupOrCreateRoot(rootName, root, true, false);
535     } catch (ClassNotFoundException JavaDoc e) {
536       throw new TCClassNotFoundException(e);
537     }
538   }
539
540   /**
541    * This method must be called within a DSO synchronized context. Currently, this is called in a setter method of a
542    * replaceable root.
543    */

544   public Object JavaDoc createOrReplaceRoot(String JavaDoc rootName, Object JavaDoc root) {
545     Object JavaDoc existingRoot = lookupRoot(rootName);
546     if (existingRoot == null) {
547       return lookupOrCreateRoot(rootName, root, false);
548     } else if (isLiteralPojo(root)) {
549       TCObject tcObject = lookupExistingLiteralRootOrNull(rootName);
550       tcObject.literalValueChanged(root, existingRoot);
551       return root;
552     } else {
553       return lookupOrCreateRoot(rootName, root, false);
554     }
555   }
556
557   public Object JavaDoc lookupOrCreateRootNoDepth(String JavaDoc rootName, Object JavaDoc root) {
558     try {
559       return lookupOrCreateRoot(rootName, root, true, true);
560     } catch (ClassNotFoundException JavaDoc e) {
561       throw new TCClassNotFoundException(e);
562     }
563   }
564
565   public Object JavaDoc lookupOrCreateRoot(String JavaDoc rootName, Object JavaDoc root, boolean dsoFinal) {
566     try {
567       return lookupOrCreateRoot(rootName, root, dsoFinal, false);
568     } catch (ClassNotFoundException JavaDoc e) {
569       throw new TCClassNotFoundException(e);
570     }
571   }
572
573   private boolean isLiteralPojo(Object JavaDoc pojo) {
574     return !(pojo instanceof Class JavaDoc) && literals.isLiteralInstance(pojo);
575   }
576
577   private Object JavaDoc lookupOrCreateRoot(String JavaDoc rootName, Object JavaDoc root, boolean dsoFinal, boolean noDepth)
578       throws ClassNotFoundException JavaDoc {
579     if (root != null) {
580       // this will throw an exception if root is not portable
581
this.checkPortabilityOfRoot(root, rootName, root.getClass());
582     }
583
584     return lookupRootOptionallyCreateOrReplace(rootName, root, true, dsoFinal, noDepth);
585   }
586
587   private void checkPortabilityOfTraversedReference(TraversedReference reference, Class JavaDoc referringClass,
588                                                     NonPortableEventContext context) {
589     NonPortableReason reason = checkPortabilityOf(reference.getValue());
590     if (reason != null) {
591       reason.addDetail("Referring class", referringClass.getName());
592       if (!reference.isAnonymous()) {
593         String JavaDoc fullyQualifiedFieldname = reference.getFullyQualifiedReferenceName();
594         reason.setUltimateNonPortableFieldName(fullyQualifiedFieldname);
595       }
596       throwNonPortableException(reference.getValue(), reason, context,
597                                 "Attempt to share an instance of a non-portable class referenced by a portable class.");
598     }
599   }
600
601   private void checkPortabilityOfRoot(Object JavaDoc root, String JavaDoc rootName, Class JavaDoc rootType) throws TCNonPortableObjectError {
602     NonPortableReason reason = checkPortabilityOf(root);
603     if (reason != null) {
604       NonPortableFieldSetContext context = this.nonPortableContextFactory.createNonPortableFieldSetContext(rootType
605           .getName(), rootName, true);
606       dumpObjectHierarchy(root);
607       throwNonPortableException(root, reason, context,
608                                 "Attempt to share an instance of a non-portable class by assigning it to a root.");
609     }
610   }
611
612   public void checkPortabilityOfField(Object JavaDoc value, String JavaDoc fieldName, Class JavaDoc targetClass)
613       throws TCNonPortableObjectError {
614     NonPortableReason reason = checkPortabilityOf(value);
615     if (reason != null) {
616       NonPortableFieldSetContext context = this.nonPortableContextFactory.createNonPortableFieldSetContext(targetClass
617           .getName(), fieldName, false);
618       dumpObjectHierarchy(value);
619       throwNonPortableException(value, reason, context,
620                                 "Attempt to set the field of a shared object to an instance of a non-portable class.");
621     }
622   }
623
624   public void checkPortabilityOfLogicalAction(Object JavaDoc param, String JavaDoc methodName, Class JavaDoc logicalType)
625       throws TCNonPortableObjectError {
626     NonPortableReason reason = checkPortabilityOf(param);
627     if (reason != null) {
628       dumpObjectHierarchy(param);
629       NonPortableEventContext context = this.nonPortableContextFactory
630           .createNonPortableLogicalInvokeContext(logicalType.getName(), methodName);
631       throwNonPortableException(param, reason, context,
632                                 "Attempt to share an instance of a non-portable class by"
633                                     + " passing it as an argument to a method of a logically-managed class.");
634     }
635   }
636
637   private void throwNonPortableException(Object JavaDoc obj, NonPortableReason reason, NonPortableEventContext context,
638                                          String JavaDoc message) throws TCNonPortableObjectError {
639     // XXX: The message should probably be part of the context
640
reason.setMessage(message);
641     context.addDetailsTo(reason);
642
643     // Send this event to L2
644
JMXMessage jmxMsg = channel.getJMXMessage();
645     jmxMsg.setJMXObject(new NonPortableObjectEvent(context, reason));
646     jmxMsg.send();
647
648     StringWriter JavaDoc formattedReason = new StringWriter JavaDoc();
649     PrintWriter JavaDoc out = new PrintWriter JavaDoc(formattedReason);
650     StringFormatter sf = new StringFormatter();
651
652     ParagraphFormatter pf = new ConsoleParagraphFormatter(80, sf);
653     NonPortableReasonFormatter reasonFormatter = new ConsoleNonPortableReasonFormatter(out, ": ", sf, pf);
654     reason.accept(reasonFormatter);
655     reasonFormatter.flush();
656
657     TCNonPortableObjectError ex = new TCNonPortableObjectError(formattedReason.getBuffer().toString());
658     // This is printed here so that even if the user catches any knid of exception and ignores it for some reason, there
659
// is a log of this exception.
660
ex.printStackTrace();
661     logger.error(ex);
662     throw ex;
663   }
664
665   private NonPortableReason checkPortabilityOf(Object JavaDoc obj) {
666     if (!isPortableInstance(obj)) { return portability.getNonPortableReason(obj.getClass()); }
667     return null;
668   }
669
670   private boolean rootLookupInProgress(String JavaDoc rootName) {
671     return rootLookupsInProgress.contains(rootName);
672   }
673
674   private void markRootLookupInProgress(String JavaDoc rootName) {
675     boolean wasAdded = rootLookupsInProgress.add(rootName);
676     if (!wasAdded) throw new AssertionError JavaDoc("Attempt to mark a root lookup that is already in progress.");
677   }
678
679   private void markRootLookupNotInProgress(String JavaDoc rootName) {
680     boolean removed = rootLookupsInProgress.remove(rootName);
681     if (!removed) throw new AssertionError JavaDoc("Attempt to unmark a root lookup that wasn't in progress.");
682   }
683
684   public synchronized void replaceRootIDIfNecessary(String JavaDoc rootName, ObjectID newRootID) {
685     waitUntilRunning();
686
687     ObjectID oldRootID = (ObjectID) roots.get(rootName);
688     if (oldRootID == null || oldRootID.equals(newRootID)) { return; }
689
690     roots.put(rootName, newRootID);
691   }
692
693   private Object JavaDoc lookupRootOptionallyCreateOrReplace(String JavaDoc rootName, Object JavaDoc rootPojo, boolean create,
694                                                      boolean dsoFinal, boolean noDepth) throws ClassNotFoundException JavaDoc {
695     boolean replaceRootIfExistWhenCreate = !dsoFinal && create;
696
697     ObjectID rootID = null;
698
699     boolean retrieveNeeded = false;
700     boolean isNew = false;
701     boolean lookupInProgress = false;
702     boolean isInterrupted = false;
703
704     synchronized (this) {
705       while (true) {
706         if (!replaceRootIfExistWhenCreate) {
707           rootID = (ObjectID) roots.get(rootName);
708           if (rootID != null) {
709             break;
710           }
711         } else {
712           rootID = ObjectID.NULL_ID;
713         }
714         if (!rootLookupInProgress(rootName)) {
715           lookupInProgress = true;
716           markRootLookupInProgress(rootName);
717           break;
718         } else {
719           try {
720             wait();
721           } catch (InterruptedException JavaDoc e) {
722             e.printStackTrace();
723             isInterrupted = true;
724           }
725         }
726       }
727     }
728     Util.selfInterruptIfNeeded(isInterrupted);
729
730     retrieveNeeded = lookupInProgress && !replaceRootIfExistWhenCreate;
731
732     isNew = retrieveNeeded || (rootID.isNull() && create);
733
734     if (retrieveNeeded) {
735       rootID = remoteObjectManager.retrieveRootID(rootName);
736     }
737
738     if (rootID.isNull() && create) {
739       Assert.assertNotNull(rootPojo);
740       // TODO:: Optimize this, do lazy instantiation
741
TCObject root = null;
742       if (isLiteralPojo(rootPojo)) {
743         root = basicCreateIfNecessary(rootPojo);
744       } else {
745         root = lookupOrCreate(rootPojo, this.nonPortableContextFactory.createNonPortableFieldSetContext(rootPojo
746             .getClass().getName(), rootName, true));
747       }
748       rootID = root.getObjectID();
749       txManager.createRoot(rootName, rootID);
750     }
751
752     synchronized (this) {
753       if (isNew && !rootID.isNull()) roots.put(rootName, rootID);
754       if (lookupInProgress) {
755         markRootLookupNotInProgress(rootName);
756         notifyAll();
757       }
758     }
759
760     return lookupObject(rootID, noDepth);
761   }
762
763   private TCObject basicLookupByID(ObjectID id) {
764     return (TCObject) idToManaged.get(id);
765   }
766
767   private boolean basicHasLocal(ObjectID id) {
768     return basicLookupByID(id) != null;
769   }
770
771   private TCObject basicLookup(Object JavaDoc obj) {
772     TCObject tcobj;
773     if (obj instanceof Manageable) {
774       tcobj = ((Manageable) obj).__tc_managed();
775     } else {
776       synchronized (pojoToManaged) {
777         tcobj = (TCObject) pojoToManaged.get(obj);
778       }
779     }
780     return tcobj;
781   }
782
783   private void basicAddLocal(TCObject obj) {
784     synchronized (this) {
785       Assert.eval(!(obj instanceof TCObjectClone));
786       if (basicHasLocal(obj.getObjectID())) { throw Assert.failure("Attempt to add an object that already exists: "
787                                                                    + obj); }
788       idToManaged.put(obj.getObjectID(), obj);
789
790       Object JavaDoc pojo = obj.getPeerObject();
791
792       if (pojo != null) {
793         if (pojo.getClass().isArray()) {
794           ManagerUtil.register(pojo, obj);
795         }
796
797         synchronized (pojoToManaged) {
798           if (pojo instanceof Manageable) {
799             Manageable m = (Manageable) pojo;
800             if (m.__tc_managed() == null) {
801               m.__tc_managed(obj);
802             } else {
803               Assert.assertTrue(m.__tc_managed() == obj);
804             }
805           } else {
806             if (!isLiteralPojo(pojo)) {
807               pojoToManaged.put(obj.getPeerObject(), obj);
808             }
809           }
810         }
811       }
812       cache.add(obj);
813       markObjectLookupNotInProgress(obj.getObjectID());
814       notifyAll();
815     }
816   }
817
818   private void addToManagedFromRoot(Object JavaDoc root, NonPortableEventContext context) {
819     try {
820       traverser.traverse(root, traverseTest, context);
821     } catch (TCNonPortableObjectError e) {
822       dumpObjectHierarchy(root);
823       throw e;
824     }
825   }
826
827   private void dumpObjectHierarchy(Object JavaDoc root) {
828     // the catch is not in the called method so that when/if there is an OOME, the logging might have a chance of
829
// actually working (as opposed to just throwing another OOME)
830
try {
831       dumpObjectHierarchy0(root);
832     } catch (Throwable JavaDoc t) {
833       logger.error("error walking non-portable object instance of type " + root.getClass().getName(), t);
834     }
835   }
836
837   private void dumpObjectHierarchy0(Object JavaDoc root) {
838     if (runtimeLogger.nonPortableDump()) {
839       NonPortableWalkVisitor visitor = new NonPortableWalkVisitor(CustomerLogging.getDSORuntimeLogger(), this,
840                                                                   this.clientConfiguration, root);
841       ObjectGraphWalker walker = new ObjectGraphWalker(root, visitor, visitor);
842       walker.walk();
843     }
844   }
845
846   private void addToSharedFromRoot(Object JavaDoc root, NonPortableEventContext context) {
847     shareObjectsTraverser.traverse(root, traverseTest, context);
848   }
849
850   private class AddManagedObjectAction implements TraversalAction {
851     public void visit(List JavaDoc objects) {
852       List JavaDoc tcObjects = basicCreateIfNecessary(objects);
853       for (Iterator JavaDoc i = tcObjects.iterator(); i.hasNext();) {
854         txManager.createObject((TCObject) i.next());
855       }
856     }
857   }
858
859   private class SharedObjectsAction implements TraversalAction {
860     public void visit(List JavaDoc objects) {
861       basicShareObjectsIfNecessary(objects);
862     }
863   }
864
865   private class NewObjectTraverseTest implements TraverseTest {
866
867     public boolean shouldTraverse(Object JavaDoc object) {
868       // literals should be skipped -- without this check, literal members (field values, array element values, in
869
// collection, etc) of newly shared instances would get TCObjects and ObjectIDs assigned to them.
870
if (literals.isLiteralInstance(object)) { return false; }
871
872       TCObject tco = basicLookup(object);
873       if (tco == null) { return true; }
874       return tco.isNew();
875     }
876
877     public void checkPortability(TraversedReference reference, Class JavaDoc referringClass, NonPortableEventContext context)
878         throws TCNonPortableObjectError {
879       ClientObjectManagerImpl.this.checkPortabilityOfTraversedReference(reference, referringClass, context);
880     }
881   }
882
883   private TCObject basicCreateIfNecessary(Object JavaDoc pojo) {
884     TCObject obj = null;
885
886     if ((obj = basicLookup(pojo)) == null) {
887       obj = factory.getNewInstance(nextObjectID(), pojo, pojo.getClass());
888       obj.setIsNew();
889       txManager.createObject(obj);
890       basicAddLocal(obj);
891       executePostCreateMethod(pojo);
892     }
893     return obj;
894   }
895
896   private synchronized List JavaDoc basicCreateIfNecessary(List JavaDoc pojos) {
897     waitUntilRunning();
898     List JavaDoc tcObjects = new ArrayList JavaDoc(pojos.size());
899     for (Iterator JavaDoc i = pojos.iterator(); i.hasNext();) {
900       tcObjects.add(basicCreateIfNecessary(i.next()));
901     }
902     return tcObjects;
903   }
904
905   private TCObject basicShareObjectIfNecessary(Object JavaDoc pojo) {
906     TCObject obj = null;
907
908     if ((obj = basicLookup(pojo)) == null) {
909       obj = factory.getNewInstance(nextObjectID(), pojo, pojo.getClass());
910       obj.setIsNew();
911       pendingCreateTCObjects.add(obj);
912       basicAddLocal(obj);
913     }
914     return obj;
915   }
916
917   private synchronized List JavaDoc basicShareObjectsIfNecessary(List JavaDoc pojos) {
918     waitUntilRunning();
919     List JavaDoc tcObjects = new ArrayList JavaDoc(pojos.size());
920     for (Iterator JavaDoc i = pojos.iterator(); i.hasNext();) {
921       tcObjects.add(basicShareObjectIfNecessary(i.next()));
922     }
923     return tcObjects;
924   }
925
926   public synchronized void addPendingCreateObjectsToTransaction() {
927     for (Iterator JavaDoc i = pendingCreateTCObjects.iterator(); i.hasNext();) {
928       TCObject tcObject = (TCObject) i.next();
929       txManager.createObject(tcObject);
930     }
931     pendingCreateTCObjects.clear();
932   }
933
934   public synchronized boolean hasPendingCreateObjects() {
935     return !pendingCreateTCObjects.isEmpty();
936   }
937
938   private ObjectID nextObjectID() {
939     return idProvider.next();
940   }
941
942   private boolean objectLookupInProgress(ObjectID id) {
943     return objectLookupsInProgress.contains(id);
944   }
945
946   private void markObjectLookupInProgress(ObjectID id) {
947     objectLookupsInProgress.add(id);
948   }
949
950   private void markObjectLookupNotInProgress(ObjectID id) {
951     objectLookupsInProgress.remove(id);
952   }
953
954   public WeakObjectReference createNewPeer(TCClass clazz, DNA dna) {
955     if (clazz.isUseNonDefaultConstructor()) {
956       try {
957         return new WeakObjectReference(dna.getObjectID(), factory.getNewPeerObject(clazz, dna), referenceQueue);
958       } catch (Exception JavaDoc e) {
959         throw new TCRuntimeException(e);
960       }
961     } else {
962       return createNewPeer(clazz, dna.getArraySize(), dna.getObjectID(), dna.getParentObjectID());
963     }
964   }
965
966   /**
967    * Deep Clone support
968    */

969   public Object JavaDoc createNewCopyInstance(Object JavaDoc source, Object JavaDoc parent) {
970     Assert.eval(!isLiteralPojo(source));
971
972     TCClass clazz = this.getOrCreateClass(source.getClass());
973
974     try {
975       if (clazz.isProxyClass()) {
976         InvocationHandler JavaDoc srcHandler = Proxy.getInvocationHandler(source);
977         Class JavaDoc peerClass = clazz.getPeerClass();
978         return Proxy.newProxyInstance(peerClass.getClassLoader(), peerClass.getInterfaces(), srcHandler);
979       } else if (clazz.isIndexed()) {
980         int size = Array.getLength(source);
981         return factory.getNewArrayInstance(clazz, size);
982       } else if (clazz.isNonStaticInner()) {
983         Assert.eval(parent != null);
984         return factory.getNewPeerObject(clazz, parent);
985       } else {
986         Assert.eval(parent == null);
987         Object JavaDoc o = factory.getNewPeerObject(clazz);
988         return o;
989       }
990     } catch (Exception JavaDoc e) {
991       throw new TCRuntimeException(e);
992     }
993   }
994
995   public WeakObjectReference createNewPeer(TCClass clazz, int size, ObjectID id, ObjectID parentID) {
996     try {
997       if (clazz.isIndexed()) {
998         Object JavaDoc array = factory.getNewArrayInstance(clazz, size);
999         return new WeakObjectReference(id, array, referenceQueue);
1000      } else if (parentID.isNull()) {
1001        return new WeakObjectReference(id, factory.getNewPeerObject(clazz), referenceQueue);
1002      } else {
1003        return new WeakObjectReference(id, factory.getNewPeerObject(clazz, lookupObject(parentID)), referenceQueue);
1004      }
1005    } catch (Exception JavaDoc e) {
1006      throw new TCRuntimeException(e);
1007    }
1008  }
1009
1010  public TCClass getOrCreateClass(Class JavaDoc clazz) {
1011    return clazzFactory.getOrCreate(clazz, this);
1012  }
1013
1014  public boolean isPortableClass(Class JavaDoc clazz) {
1015    return portability.isPortableClass(clazz);
1016  }
1017
1018  public boolean isPortableInstance(Object JavaDoc obj) {
1019    return portability.isPortableInstance(obj);
1020  }
1021
1022  private void startReaper() {
1023    reaper = new StoppableThread("Reaper") {
1024      public void run() {
1025        while (true) {
1026          try {
1027            if (isStopRequested()) { return; }
1028
1029            WeakObjectReference wor = (WeakObjectReference) referenceQueue.remove(POLL_TIME);
1030
1031            if (wor != null) {
1032              ObjectID objectID = wor.getObjectID();
1033              reap(objectID);
1034            }
1035          } catch (InterruptedException JavaDoc e) {
1036            return;
1037          }
1038        }
1039      }
1040    };
1041    reaper.setDaemon(true);
1042    reaper.start();
1043  }
1044
1045  // XXX::: Cache eviction doesnt clear it from the cache. it happens in reap().
1046
public void evictCache(CacheStats stat) {
1047    int size = idToManaged_size();
1048    int toEvict = stat.getObjectCountToEvict(size);
1049    if (toEvict <= 0) return;
1050    // Cache is full
1051
boolean debug = logger.isDebugEnabled();
1052    int totalReferencesCleared = 0;
1053    int toClear = toEvict;
1054    while (toEvict > 0 && toClear > 0) {
1055      int maxCount = Math.min(COMMIT_SIZE, toClear);
1056      Collection JavaDoc removalCandidates = cache.getRemovalCandidates(maxCount);
1057      if (removalCandidates.isEmpty()) break; // couldnt find any more
1058
for (Iterator JavaDoc i = removalCandidates.iterator(); i.hasNext() && toClear > 0;) {
1059        TCObject removed = (TCObject) i.next();
1060        if (removed != null) {
1061          Object JavaDoc pr = removed.getPeerObject();
1062          if (pr != null) {
1063            int cleared = removed.clearReferences(toClear);
1064            totalReferencesCleared += cleared;
1065            if (debug) {
1066              logger.debug("Clearing:" + removed.getObjectID() + " class:" + pr.getClass() + " Total cleared = "
1067                           + totalReferencesCleared);
1068            }
1069            toClear -= cleared;
1070          }
1071        }
1072      }
1073      toEvict -= removalCandidates.size();
1074    }
1075    // TODO:: Send the correct set of targetObjects2GC
1076
stat.objectEvicted(totalReferencesCleared, idToManaged_size(), Collections.EMPTY_LIST);
1077  }
1078
1079  // XXX:: Not synchronizing to improve performance, should be called only during cache eviction
1080
private int idToManaged_size() {
1081    return idToManaged.size();
1082  }
1083
1084}
1085
Popular Tags