1 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 ; 50 import java.io.StringWriter ; 51 import java.lang.ref.ReferenceQueue ; 52 import java.lang.reflect.Array ; 53 import java.lang.reflect.InvocationHandler ; 54 import java.lang.reflect.InvocationTargetException ; 55 import java.lang.reflect.Method ; 56 import java.lang.reflect.Proxy ; 57 import java.util.ArrayList ; 58 import java.util.Collection ; 59 import java.util.Collections ; 60 import java.util.HashMap ; 61 import java.util.HashSet ; 62 import java.util.IdentityHashMap ; 63 import java.util.Iterator ; 64 import java.util.List ; 65 import java.util.Map ; 66 import java.util.Set ; 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 shutdownLock = new Object (); 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 objectLookupsInProgress = new HashSet (); 98 private final Set rootLookupsInProgress = new HashSet (); 99 private final ObjectIDProvider idProvider; 100 private final ReferenceQueue referenceQueue = new ReferenceQueue (); 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 localCreationInProgress = new ThreadLocal (); 110 private final Set pendingCreateTCObjects = new HashSet (); 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 getClassFor(String className, String loaderDesc) throws ClassNotFoundException { 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 createParentCopyInstanceIfNecessary(Map visited, Map cloned, Object v) { 165 TCClass tcc = getOrCreateClass(v.getClass()); 166 Object parent = null; 167 if (tcc.isNonStaticInner()) { 168 TransparentAccess access = (TransparentAccess) v; 169 Map m = new HashMap(); 170 access.__tc_getallfields(m); 171 Object 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 e) { 186 isInterrupted = true; 187 } 188 } 189 Util.selfInterruptIfNeeded(isInterrupted); 190 } 191 192 private void assertPaused(Object message) { 193 if (state != PAUSED) throw new AssertionError (message + ": " + state); 194 } 195 196 private void assertStarting(Object message) { 197 if (state != STARTING) throw new AssertionError (message + ": " + state); 198 } 199 200 private void assertNotPaused(Object message) { 201 if (state == PAUSED) throw new AssertionError (message + ": " + state); 202 } 203 204 public TraversedReferences getPortableObjects(Class clazz, Object 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 223 public Object deepCopy(Object source, OptimisticTransactionManager optimisticTxManager) { 224 IdentityHashMap cloned = new IdentityHashMap (); 225 IdentityHashMap visited = new IdentityHashMap (); 226 227 Object parent = this.createParentCopyInstanceIfNecessary(visited, cloned, source); 228 Object 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 original = cloned.keySet().iterator().next(); Object clone = cloned.get(original); 239 cloned.remove(original); 240 cloneAndUpdate(optimisticTxManager, cloned, visited, original, clone); 241 } 242 243 return copy; 244 } 245 246 252 private void cloneAndUpdate(OptimisticTransactionManager optimisticTxManager, IdentityHashMap cloned, 253 IdentityHashMap visited, Object original, Object 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 pojo, NonPortableEventContext context) { 267 addToManagedFromRoot(pojo, context); 268 return basicLookup(pojo); 269 } 270 271 private TCObject share(Object pojo, NonPortableEventContext context) { 272 addToSharedFromRoot(pojo, context); 273 return basicLookup(pojo); 274 } 275 276 public ReferenceQueue 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 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 pojo, NonPortableEventContext context) { 309 if (pojo == null) return TCObjectFactory.NULL_TC_OBJECT; 310 return lookupOrCreateIfNecesary(pojo, context); 311 } 312 313 public TCObject lookupOrShare(Object 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 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 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 343 private void executePostCreateMethod(Object pojo) { 344 346 String onLookupMethodName = clientConfiguration.getPostCreateMethodIfDefined(pojo.getClass().getName()); 347 if (onLookupMethodName != null) { 348 try { 349 Method m = pojo.getClass().getDeclaredMethod(onLookupMethodName, new Class [] {}); 350 m.setAccessible(true); 351 m.invoke(pojo, new Object [] {}); 352 } catch (Throwable t) { 353 if (t instanceof InvocationTargetException ) { 354 t = t.getCause(); 355 } 356 logger.warn("postCreate method (" + onLookupMethodName + ") failed on object of " + pojo.getClass(), t); 357 throw new RuntimeException (t); 358 } 359 } 360 } 361 362 private TCObject lookupExistingLiteralRootOrNull(String rootName) { 363 ObjectID rootID = (ObjectID) roots.get(rootName); 364 return basicLookupByID(rootID); 365 } 366 367 public TCObject lookupExistingOrNull(Object pojo) { 368 return basicLookup(pojo); 369 } 370 371 public synchronized ObjectID lookupExistingObjectID(Object pojo) { 372 TCObject obj = basicLookup(pojo); 373 if (obj == null) { throw new AssertionError ("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 lookupObjectNoDepth(ObjectID id) throws ClassNotFoundException { 382 return lookupObject(id, true); 383 } 384 385 public Object lookupObject(ObjectID objectID) throws ClassNotFoundException { 386 return lookupObject(objectID, false); 387 } 388 389 private Object lookupObject(ObjectID objectID, boolean noDepth) throws ClassNotFoundException { 390 if (objectID.isNull()) return null; 391 Object o = null; 392 while (o == null) { 393 final TCObject tco = lookup(objectID, noDepth); 394 if (tco == null) throw new AssertionError ("TCObject was null for " + objectID); 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 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 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 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(); } 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 456 public TCObject lookup(ObjectID id) throws ClassNotFoundException { 457 return lookup(id, false); 458 } 459 460 private TCObject lookup(ObjectID id, boolean noDepth) throws ClassNotFoundException { 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 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(Namespace.parseClassNameIfNecessary(dna 492 .getTypeName()), dna.getDefiningLoaderDescription())); 493 setCreationInProgress(id, obj); 494 Assert.assertFalse(dna.isDelta()); 495 obj.hydrate(dna, false); 496 } catch (ClassNotFoundException 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 getAllObjectIDsAndClear(Collection c) { 513 assertStarting("Called when not in STARTING state !"); 514 for (Iterator i = idToManaged.keySet().iterator(); i.hasNext();) { 515 c.add(i.next()); 516 } 517 remoteObjectManager.clear(); 518 return c; 519 } 520 521 public Object lookupRoot(String rootName) { 522 try { 523 return lookupRootOptionallyCreateOrReplace(rootName, null, false, true, false); 524 } catch (ClassNotFoundException e) { 525 throw new TCClassNotFoundException(e); 526 } 527 } 528 529 532 public Object lookupOrCreateRoot(String rootName, Object root) { 533 try { 534 return lookupOrCreateRoot(rootName, root, true, false); 535 } catch (ClassNotFoundException e) { 536 throw new TCClassNotFoundException(e); 537 } 538 } 539 540 544 public Object createOrReplaceRoot(String rootName, Object root) { 545 Object 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 lookupOrCreateRootNoDepth(String rootName, Object root) { 558 try { 559 return lookupOrCreateRoot(rootName, root, true, true); 560 } catch (ClassNotFoundException e) { 561 throw new TCClassNotFoundException(e); 562 } 563 } 564 565 public Object lookupOrCreateRoot(String rootName, Object root, boolean dsoFinal) { 566 try { 567 return lookupOrCreateRoot(rootName, root, dsoFinal, false); 568 } catch (ClassNotFoundException e) { 569 throw new TCClassNotFoundException(e); 570 } 571 } 572 573 private boolean isLiteralPojo(Object pojo) { 574 return !(pojo instanceof Class ) && literals.isLiteralInstance(pojo); 575 } 576 577 private Object lookupOrCreateRoot(String rootName, Object root, boolean dsoFinal, boolean noDepth) 578 throws ClassNotFoundException { 579 if (root != null) { 580 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 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 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 root, String rootName, Class 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 value, String fieldName, Class 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 param, String methodName, Class 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 obj, NonPortableReason reason, NonPortableEventContext context, 638 String message) throws TCNonPortableObjectError { 639 reason.setMessage(message); 641 context.addDetailsTo(reason); 642 643 JMXMessage jmxMsg = channel.getJMXMessage(); 645 jmxMsg.setJMXObject(new NonPortableObjectEvent(context, reason)); 646 jmxMsg.send(); 647 648 StringWriter formattedReason = new StringWriter (); 649 PrintWriter out = new PrintWriter (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 ex.printStackTrace(); 661 logger.error(ex); 662 throw ex; 663 } 664 665 private NonPortableReason checkPortabilityOf(Object obj) { 666 if (!isPortableInstance(obj)) { return portability.getNonPortableReason(obj.getClass()); } 667 return null; 668 } 669 670 private boolean rootLookupInProgress(String rootName) { 671 return rootLookupsInProgress.contains(rootName); 672 } 673 674 private void markRootLookupInProgress(String rootName) { 675 boolean wasAdded = rootLookupsInProgress.add(rootName); 676 if (!wasAdded) throw new AssertionError ("Attempt to mark a root lookup that is already in progress."); 677 } 678 679 private void markRootLookupNotInProgress(String rootName) { 680 boolean removed = rootLookupsInProgress.remove(rootName); 681 if (!removed) throw new AssertionError ("Attempt to unmark a root lookup that wasn't in progress."); 682 } 683 684 public synchronized void replaceRootIDIfNecessary(String 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 lookupRootOptionallyCreateOrReplace(String rootName, Object rootPojo, boolean create, 694 boolean dsoFinal, boolean noDepth) throws ClassNotFoundException { 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 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 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 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 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 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 root) { 828 try { 831 dumpObjectHierarchy0(root); 832 } catch (Throwable t) { 833 logger.error("error walking non-portable object instance of type " + root.getClass().getName(), t); 834 } 835 } 836 837 private void dumpObjectHierarchy0(Object 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 root, NonPortableEventContext context) { 847 shareObjectsTraverser.traverse(root, traverseTest, context); 848 } 849 850 private class AddManagedObjectAction implements TraversalAction { 851 public void visit(List objects) { 852 List tcObjects = basicCreateIfNecessary(objects); 853 for (Iterator 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 objects) { 861 basicShareObjectsIfNecessary(objects); 862 } 863 } 864 865 private class NewObjectTraverseTest implements TraverseTest { 866 867 public boolean shouldTraverse(Object object) { 868 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 referringClass, NonPortableEventContext context) 878 throws TCNonPortableObjectError { 879 ClientObjectManagerImpl.this.checkPortabilityOfTraversedReference(reference, referringClass, context); 880 } 881 } 882 883 private TCObject basicCreateIfNecessary(Object 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 basicCreateIfNecessary(List pojos) { 897 waitUntilRunning(); 898 List tcObjects = new ArrayList (pojos.size()); 899 for (Iterator i = pojos.iterator(); i.hasNext();) { 900 tcObjects.add(basicCreateIfNecessary(i.next())); 901 } 902 return tcObjects; 903 } 904 905 private TCObject basicShareObjectIfNecessary(Object 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 basicShareObjectsIfNecessary(List pojos) { 918 waitUntilRunning(); 919 List tcObjects = new ArrayList (pojos.size()); 920 for (Iterator 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 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 e) { 959 throw new TCRuntimeException(e); 960 } 961 } else { 962 return createNewPeer(clazz, dna.getArraySize(), dna.getObjectID(), dna.getParentObjectID()); 963 } 964 } 965 966 969 public Object createNewCopyInstance(Object source, Object parent) { 970 Assert.eval(!isLiteralPojo(source)); 971 972 TCClass clazz = this.getOrCreateClass(source.getClass()); 973 974 try { 975 if (clazz.isProxyClass()) { 976 InvocationHandler srcHandler = Proxy.getInvocationHandler(source); 977 Class 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 o = factory.getNewPeerObject(clazz); 988 return o; 989 } 990 } catch (Exception 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 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 e) { 1006 throw new TCRuntimeException(e); 1007 } 1008 } 1009 1010 public TCClass getOrCreateClass(Class clazz) { 1011 return clazzFactory.getOrCreate(clazz, this); 1012 } 1013 1014 public boolean isPortableClass(Class clazz) { 1015 return portability.isPortableClass(clazz); 1016 } 1017 1018 public boolean isPortableInstance(Object 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 e) { 1036 return; 1037 } 1038 } 1039 } 1040 }; 1041 reaper.setDaemon(true); 1042 reaper.start(); 1043 } 1044 1045 public void evictCache(CacheStats stat) { 1047 int size = idToManaged_size(); 1048 int toEvict = stat.getObjectCountToEvict(size); 1049 if (toEvict <= 0) return; 1050 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 removalCandidates = cache.getRemovalCandidates(maxCount); 1057 if (removalCandidates.isEmpty()) break; for (Iterator i = removalCandidates.iterator(); i.hasNext() && toClear > 0;) { 1059 TCObject removed = (TCObject) i.next(); 1060 if (removed != null) { 1061 Object 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 stat.objectEvicted(totalReferencesCleared, idToManaged_size(), Collections.EMPTY_LIST); 1077 } 1078 1079 private int idToManaged_size() { 1081 return idToManaged.size(); 1082 } 1083 1084} 1085 | Popular Tags |