1 package org.apache.ojb.odmg; 2 3 17 18 import java.util.ArrayList ; 19 import java.util.Enumeration ; 20 import java.util.HashMap ; 21 import java.util.Iterator ; 22 import java.util.List ; 23 import java.util.Map ; 24 25 import org.apache.commons.lang.builder.ToStringBuilder; 26 import org.apache.commons.lang.builder.ToStringStyle; 27 import org.apache.commons.lang.SystemUtils; 28 import org.apache.ojb.broker.Identity; 29 import org.apache.ojb.broker.OJBRuntimeException; 30 import org.apache.ojb.broker.OptimisticLockException; 31 import org.apache.ojb.broker.PersistenceBroker; 32 import org.apache.ojb.broker.accesslayer.ConnectionManagerIF; 33 import org.apache.ojb.broker.core.proxy.CollectionProxy; 34 import org.apache.ojb.broker.core.proxy.CollectionProxyDefaultImpl; 35 import org.apache.ojb.broker.core.proxy.IndirectionHandler; 36 import org.apache.ojb.broker.core.proxy.ProxyHelper; 37 import org.apache.ojb.broker.metadata.ClassDescriptor; 38 import org.apache.ojb.broker.metadata.CollectionDescriptor; 39 import org.apache.ojb.broker.metadata.ObjectReferenceDescriptor; 40 import org.apache.ojb.broker.util.BrokerHelper; 41 import org.apache.ojb.broker.util.logging.Logger; 42 import org.apache.ojb.broker.util.logging.LoggerFactory; 43 import org.apache.ojb.odmg.link.LinkEntry; 44 import org.apache.ojb.odmg.link.LinkEntryMtoN; 45 import org.apache.ojb.odmg.states.StateOldClean; 46 import org.odmg.LockNotGrantedException; 47 import org.odmg.ODMGRuntimeException; 48 import org.odmg.Transaction; 49 import org.odmg.TransactionAbortedException; 50 51 61 public class ObjectEnvelopeTable 62 { 63 private Logger log = LoggerFactory.getLogger(ObjectEnvelopeTable.class); 64 private TransactionImpl transaction; 65 66 72 private List newAssociatedIdentites = new ArrayList (); 73 private List m2nLinkList = new ArrayList (); 74 private List m2nUnlinkList = new ArrayList (); 75 private List markedForDeletionList = new ArrayList (); 76 private List markedForInsertList = new ArrayList (); 77 78 79 private Map mhtObjectEnvelopes = new HashMap (); 80 81 86 private ArrayList mvOrderOfIds = new ArrayList (); 87 88 89 private boolean needsCommit = false; 90 91 92 public ObjectEnvelopeTable(TransactionImpl myTransaction) 93 { 94 transaction = myTransaction; 95 } 96 97 TransactionImpl getTransaction() 98 { 99 return transaction; 100 } 101 102 103 public void refresh() 104 { 105 needsCommit = false; 106 mhtObjectEnvelopes = new HashMap (); 107 mvOrderOfIds = new ArrayList (); 108 afterWriteCleanup(); 109 } 110 111 void afterWriteCleanup() 112 { 113 m2nLinkList.clear(); 114 m2nUnlinkList.clear(); 115 newAssociatedIdentites.clear(); 116 markedForDeletionList.clear(); 117 markedForInsertList.clear(); 118 } 119 120 126 public void writeObjects(boolean reuse) throws TransactionAbortedException, LockNotGrantedException 127 { 128 PersistenceBroker broker = transaction.getBroker(); 129 ConnectionManagerIF connMan = broker.serviceConnectionManager(); 130 boolean saveBatchMode = connMan.isBatchMode(); 131 132 try 133 { 134 if(log.isDebugEnabled()) 135 { 136 log.debug( 137 "PB is in internal tx: " 138 + broker.isInTransaction() 139 + " broker was: " 140 + broker); 141 } 142 if(!broker.isInTransaction()) 144 { 145 log.error("PB associated with current odmg-tx is not in tx"); 146 throw new TransactionAbortedException("Underlying PB is not in tx, was begin call done before commit?"); 147 } 148 149 154 connMan.setBatchMode(true); 156 157 checkAllEnvelopes(broker); 160 161 cascadingDependents(); 163 164 upgradeLockIfNeeded(); 167 168 reorder(); 170 177 writeAllEnvelopes(reuse); 179 180 connMan.executeBatch(); 182 183 prepareForReuse(reuse); 185 186 afterWriteCleanup(); 188 189 } 190 catch(Exception e) 191 { 192 connMan.clearBatch(); 193 198 if(e instanceof OptimisticLockException) 199 { 200 log.error("Optimistic lock exception while write objects", e); 202 Object sourceObject = ((OptimisticLockException) e).getSourceObject(); 204 throw new LockNotGrantedException("Optimistic lock exception occur, source object was (" + sourceObject + ")," + 205 " message was (" + e.getMessage() + ")"); 206 } 207 else if(!(e instanceof RuntimeException )) 208 { 209 log.warn("Error while write objects for tx " + transaction, e); 210 throw new ODMGRuntimeException("Unexpected error while write objects: " + e.getMessage()); 211 } 212 else 213 { 214 log.warn("Error while write objects for tx " + transaction, e); 215 throw (RuntimeException ) e; 216 } 217 } 218 finally 219 { 220 needsCommit = false; 221 connMan.setBatchMode(saveBatchMode); 222 } 223 } 224 225 226 private void writeAllEnvelopes(boolean reuse) 227 { 228 performM2NUnlinkEntries(); 230 231 Iterator iter; 232 iter = ((List ) mvOrderOfIds.clone()).iterator(); 234 while(iter.hasNext()) 235 { 236 ObjectEnvelope mod = (ObjectEnvelope) mhtObjectEnvelopes.get(iter.next()); 237 boolean insert = false; 238 if(needsCommit) 239 { 240 insert = mod.needsInsert(); 241 mod.getModificationState().commit(mod); 242 if(reuse && insert) 243 { 244 getTransaction().doSingleLock(mod.getClassDescriptor(), mod.getObject(), mod.getIdentity(), Transaction.WRITE); 245 } 246 } 247 252 mod.cleanup(reuse, insert); 253 } 254 performM2NLinkEntries(); 256 } 257 258 263 private void checkAllEnvelopes(PersistenceBroker broker) 264 { 265 Iterator iter = ((List ) mvOrderOfIds.clone()).iterator(); 266 while(iter.hasNext()) 267 { 268 ObjectEnvelope mod = (ObjectEnvelope) mhtObjectEnvelopes.get(iter.next()); 269 if(!mod.getModificationState().isTransient()) 271 { 272 mod.markReferenceElements(broker); 273 } 274 } 275 } 276 277 281 private void prepareForReuse(boolean reuse) 282 { 283 if(reuse) 284 { 285 Iterator iter = ((List ) mvOrderOfIds.clone()).iterator(); 287 while(iter.hasNext()) 288 { 289 ObjectEnvelope mod = (ObjectEnvelope) mhtObjectEnvelopes.get(iter.next()); 290 if(!needsCommit || (mod.getModificationState() == StateOldClean.getInstance() 291 || mod.getModificationState().isTransient())) 292 { 293 } 295 else 296 { 297 mod.setModificationState(mod.getModificationState().markClean()); 298 } 299 } 300 } 301 } 302 303 308 private void upgradeLockIfNeeded() 309 { 310 Iterator iter = ((List ) mvOrderOfIds.clone()).iterator(); 312 TransactionImpl tx = getTransaction(); 313 ObjectEnvelope mod; 314 while(iter.hasNext()) 315 { 316 mod = (ObjectEnvelope) mhtObjectEnvelopes.get(iter.next()); 317 if(!mod.getModificationState().isTransient()) 319 { 320 324 if(!mod.needsInsert()) 325 { 326 if((mod.needsDelete() || mod.needsUpdate() 327 || mod.hasChanged(tx.getBroker()))) 328 { 329 needsCommit = true; 330 mod.setModificationState(mod.getModificationState().markDirty()); 332 ClassDescriptor cld = mod.getClassDescriptor(); 333 if(!mod.isWriteLocked()) 335 { 336 tx.doSingleLock(cld, mod.getObject(), mod.getIdentity(), Transaction.WRITE); 337 } 338 } 339 } 340 else 341 { 342 needsCommit = true; 343 } 344 } 345 } 346 } 347 348 349 public void rollback() 350 { 351 try 352 { 353 Iterator iter = mvOrderOfIds.iterator(); 354 while(iter.hasNext()) 355 { 356 ObjectEnvelope mod = (ObjectEnvelope) mhtObjectEnvelopes.get(iter.next()); 357 if(log.isDebugEnabled()) 358 log.debug("rollback: " + mod); 359 if(mod.hasChanged(transaction.getBroker())) 361 { 362 mod.setModificationState(mod.getModificationState().markDirty()); 363 } 364 mod.getModificationState().rollback(mod); 365 } 366 } 367 finally 368 { 369 needsCommit = false; 370 } 371 afterWriteCleanup(); 372 } 373 374 375 public void remove(Object pKey) 376 { 377 Identity id; 378 if(pKey instanceof Identity) 379 { 380 id = (Identity) pKey; 381 } 382 else 383 { 384 id = transaction.getBroker().serviceIdentity().buildIdentity(pKey); 385 } 386 mhtObjectEnvelopes.remove(id); 387 mvOrderOfIds.remove(id); 388 } 389 390 396 public Enumeration elements() 397 { 398 return java.util.Collections.enumeration(mhtObjectEnvelopes.values()); 399 } 400 401 402 public ObjectEnvelope getByIdentity(Identity id) 403 { 404 return (ObjectEnvelope) mhtObjectEnvelopes.get(id); 405 } 406 407 413 public ObjectEnvelope get(Object pKey, boolean isNew) 414 { 415 PersistenceBroker broker = transaction.getBroker(); 416 Identity oid = broker.serviceIdentity().buildIdentity(pKey); 417 return get(oid, pKey, isNew); 418 } 419 420 426 public ObjectEnvelope get(Identity oid, Object pKey, boolean isNew) 427 { 428 ObjectEnvelope result = getByIdentity(oid); 429 if(result == null) 430 { 431 result = new ObjectEnvelope(this, oid, pKey, isNew); 432 mhtObjectEnvelopes.put(oid, result); 433 mvOrderOfIds.add(oid); 434 if(log.isDebugEnabled()) 435 log.debug("register: " + result); 436 } 437 return result; 438 } 439 440 441 public String toString() 442 { 443 ToStringBuilder buf = new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE); 444 String eol = SystemUtils.LINE_SEPARATOR; 445 buf.append("# ObjectEnvelopeTable dump:" + eol + "start["); 446 Enumeration en = elements(); 447 while(en.hasMoreElements()) 448 { 449 ObjectEnvelope mod = (ObjectEnvelope) en.nextElement(); 450 buf.append(mod.toString() + eol); 451 } 452 buf.append("]end"); 453 return buf.toString(); 454 } 455 456 457 public boolean contains(Identity oid) 458 { 459 return mhtObjectEnvelopes.containsKey(oid); 461 } 462 463 464 private void reorder() 465 { 466 if(getTransaction().isOrdering() && needsCommit && mhtObjectEnvelopes.size() > 1) 467 { 468 ObjectEnvelopeOrdering ordering = new ObjectEnvelopeOrdering(mvOrderOfIds, mhtObjectEnvelopes); 469 ordering.reorder(); 470 Identity[] newOrder = ordering.getOrdering(); 471 472 mvOrderOfIds.clear(); 473 for(int i = 0; i < newOrder.length; i++) 474 { 475 mvOrderOfIds.add(newOrder[i]); 476 } 477 } 478 } 479 480 void cascadingDependents() 481 { 482 Iterator it = mhtObjectEnvelopes.values().iterator(); 483 ObjectEnvelope mod; 484 while(it.hasNext()) 486 { 487 mod = (ObjectEnvelope) it.next(); 488 if(mod.needsDelete()) 489 { 490 addForDeletionDependent(mod); 491 } 492 else if(mod.needsInsert()) 493 { 494 addForInsertDependent(mod); 495 } 496 } 497 504 cascadeMarkedForDeletion(); 505 cascadeMarkedForInsert(); 506 } 507 508 void addNewAssociatedIdentity(Identity oid) 509 { 510 newAssociatedIdentites.add(oid); 511 } 512 513 boolean isNewAssociatedObject(Identity oid) 514 { 515 return newAssociatedIdentites.contains(oid); 516 } 517 518 void addForInsertDependent(ObjectEnvelope mod) 519 { 520 markedForInsertList.add(mod); 521 } 522 523 524 private void cascadeMarkedForInsert() 525 { 526 List alreadyPrepared = new ArrayList (); 528 for(int i = 0; i < markedForInsertList.size(); i++) 529 { 530 ObjectEnvelope mod = (ObjectEnvelope) markedForInsertList.get(i); 531 if(mod.needsInsert()) 533 { 534 cascadeInsertFor(mod, alreadyPrepared); 535 alreadyPrepared.clear(); 536 } 537 } 538 markedForInsertList.clear(); 539 } 540 541 545 private void cascadeInsertFor(ObjectEnvelope mod, List alreadyPrepared) 546 { 547 if(alreadyPrepared.contains(mod.getIdentity())) return; 549 alreadyPrepared.add(mod.getIdentity()); 550 551 ClassDescriptor cld = getTransaction().getBroker().getClassDescriptor(mod.getObject().getClass()); 552 553 List refs = cld.getObjectReferenceDescriptors(true); 554 cascadeInsertSingleReferences(mod, refs, alreadyPrepared); 555 556 List colls = cld.getCollectionDescriptors(true); 557 cascadeInsertCollectionReferences(mod, colls, alreadyPrepared); 558 } 559 560 private void cascadeInsertSingleReferences(ObjectEnvelope source, List descriptor, List alreadyPrepared) 561 { 562 for(int i = 0; i < descriptor.size(); i++) 563 { 564 ObjectReferenceDescriptor ord = (ObjectReferenceDescriptor) descriptor.get(i); 565 Object depObj = ord.getPersistentField().get(source.getObject()); 566 567 if(depObj != null) 568 { 569 source.addLinkOneToOne(ord, false); 571 572 IndirectionHandler handler = ProxyHelper.getIndirectionHandler(depObj); 573 if(handler == null || handler.alreadyMaterialized()) 575 { 576 RuntimeObject rt; 577 if(handler != null) 579 { 580 rt = new RuntimeObject(handler.getRealSubject(), getTransaction(), false); 581 } 582 else 583 { 584 rt = new RuntimeObject(depObj, getTransaction()); 585 } 586 Identity oid = rt.getIdentity(); 587 if(!alreadyPrepared.contains(oid)) 588 { 589 ObjectEnvelope depMod = getByIdentity(oid); 590 if(depMod == null && rt.isNew()) 593 { 594 getTransaction().lockAndRegister(rt, Transaction.WRITE, false, getTransaction().getRegistrationList()); 595 depMod = getByIdentity(oid); 596 cascadeInsertFor(depMod, alreadyPrepared); 597 } 598 } 599 } 600 } 601 } 602 } 603 604 private void cascadeInsertCollectionReferences(ObjectEnvelope source, List descriptor, List alreadyPrepared) 605 { 606 for(int i = 0; i < descriptor.size(); i++) 608 { 609 CollectionDescriptor col = (CollectionDescriptor) descriptor.get(i); 610 Object collOrArray = col.getPersistentField().get(source.getObject()); 611 CollectionProxy proxy = ProxyHelper.getCollectionProxy(collOrArray); 612 617 if(proxy == null && collOrArray != null) 618 { 619 Iterator it = BrokerHelper.getCollectionIterator(collOrArray); 620 while(it.hasNext()) 621 { 622 Object colObj = it.next(); 623 if(colObj != null) 624 { 625 RuntimeObject rt = new RuntimeObject(colObj, getTransaction()); 626 Identity oid = rt.getIdentity(); 627 633 if(source.needsInsert()) 634 { 635 642 colObj = ProxyHelper.getRealObject(colObj); 643 ObjectEnvelope oe = getByIdentity(oid); 644 if(oe == null) 645 { 646 getTransaction().lockAndRegister(rt, Transaction.WRITE, false, getTransaction().getRegistrationList()); 647 oe = getByIdentity(oid); 648 } 649 if(col.isMtoNRelation()) 650 { 651 addM2NLinkEntry(col, source.getObject(), colObj); 653 } 654 else 655 { 656 oe.addLinkOneToN(col, source.getObject(), false); 658 662 oe.setModificationState(oe.getModificationState().markDirty()); 663 } 664 cascadeInsertFor(oe, alreadyPrepared); 665 } 666 } 667 } 668 } 669 } 670 } 671 672 void addForDeletionDependent(ObjectEnvelope mod) 673 { 674 markedForDeletionList.add(mod); 675 } 676 677 678 private void cascadeMarkedForDeletion() 679 { 680 List alreadyPrepared = new ArrayList (); 681 for(int i = 0; i < markedForDeletionList.size(); i++) 682 { 683 ObjectEnvelope mod = (ObjectEnvelope) markedForDeletionList.get(i); 684 if(!isNewAssociatedObject(mod.getIdentity())) 686 { 687 cascadeDeleteFor(mod, alreadyPrepared); 688 alreadyPrepared.clear(); 689 } 690 } 691 markedForDeletionList.clear(); 692 } 693 694 698 private void cascadeDeleteFor(ObjectEnvelope mod, List alreadyPrepared) 699 { 700 if(alreadyPrepared.contains(mod.getIdentity())) return; 702 703 alreadyPrepared.add(mod.getIdentity()); 704 705 ClassDescriptor cld = getTransaction().getBroker().getClassDescriptor(mod.getObject().getClass()); 706 707 List refs = cld.getObjectReferenceDescriptors(true); 708 cascadeDeleteSingleReferences(mod, refs, alreadyPrepared); 709 710 List colls = cld.getCollectionDescriptors(true); 711 cascadeDeleteCollectionReferences(mod, colls, alreadyPrepared); 712 } 713 714 private void cascadeDeleteSingleReferences(ObjectEnvelope source, List descriptor, List alreadyPrepared) 715 { 716 for(int i = 0; i < descriptor.size(); i++) 717 { 718 ObjectReferenceDescriptor ord = (ObjectReferenceDescriptor) descriptor.get(i); 719 if(getTransaction().cascadeDeleteFor(ord)) 720 { 721 Object depObj = ord.getPersistentField().get(source.getObject()); 722 if(depObj != null) 723 { 724 Identity oid = getTransaction().getBroker().serviceIdentity().buildIdentity(depObj); 725 if(!isNewAssociatedObject(oid)) 728 { 729 ObjectEnvelope depMod = get(oid, depObj, false); 730 depMod.setModificationState(depMod.getModificationState().markDelete()); 731 cascadeDeleteFor(depMod, alreadyPrepared); 732 } 733 } 734 } 735 } 736 } 737 738 private void cascadeDeleteCollectionReferences(ObjectEnvelope source, List descriptor, List alreadyPrepared) 739 { 740 PersistenceBroker pb = getTransaction().getBroker(); 741 for(int i = 0; i < descriptor.size(); i++) 742 { 743 CollectionDescriptor col = (CollectionDescriptor) descriptor.get(i); 744 boolean cascadeDelete = getTransaction().cascadeDeleteFor(col); 745 Object collOrArray = col.getPersistentField().get(source.getObject()); 746 CollectionProxyDefaultImpl proxy = (CollectionProxyDefaultImpl) ProxyHelper.getCollectionProxy(collOrArray); 748 if(proxy != null) 750 { 751 collOrArray = proxy.getData(); 752 } 753 if(collOrArray != null) 754 { 755 Iterator it = BrokerHelper.getCollectionIterator(collOrArray); 756 while(it.hasNext()) 757 { 758 Object colObj = ProxyHelper.getRealObject(it.next()); 759 Identity oid = pb.serviceIdentity().buildIdentity(colObj); 760 ObjectEnvelope colMod = get(oid, colObj, false); 761 if(cascadeDelete) 762 { 763 colMod.setModificationState(colMod.getModificationState().markDelete()); 764 cascadeDeleteFor(colMod, alreadyPrepared); 765 } 766 else 767 { 768 if(!col.isMtoNRelation()) 769 { 770 colMod.addLinkOneToN(col, source.getObject(), true); 771 colMod.setModificationState(colMod.getModificationState().markDirty()); 772 } 773 } 774 if(col.isMtoNRelation()) 775 { 776 addM2NUnlinkEntry(col, source.getObject(), colObj); 777 } 778 } 779 } 780 } 781 } 782 783 void addM2NLinkEntry(CollectionDescriptor cod, Object leftSource, Object rightSource) 784 { 785 if(!cod.isMtoNRelation()) throw new OJBRuntimeException("Expect a m:n releation, but specified a 1:n"); 786 m2nLinkList.add(new LinkEntryMtoN(leftSource, cod, rightSource, false)); 787 } 788 789 void performM2NLinkEntries() 790 { 791 PersistenceBroker broker = getTransaction().getBroker(); 792 LinkEntry entry; 793 for(int i = 0; i < m2nLinkList.size(); i++) 794 { 795 entry = (LinkEntry) m2nLinkList.get(i); 796 entry.execute(broker); 797 } 798 } 799 800 void addM2NUnlinkEntry(CollectionDescriptor cod, Object leftSource, Object rightSource) 801 { 802 if(!cod.isMtoNRelation()) throw new OJBRuntimeException("Expect a m:n releation, but specified a 1:n"); 803 m2nUnlinkList.add(new LinkEntryMtoN(leftSource, cod, rightSource, true)); 804 } 805 806 void performM2NUnlinkEntries() 807 { 808 PersistenceBroker broker = getTransaction().getBroker(); 809 LinkEntry entry; 810 for(int i = 0; i < m2nUnlinkList.size(); i++) 811 { 812 entry = (LinkEntry) m2nUnlinkList.get(i); 813 entry.execute(broker); 814 } 815 } 816 817 825 boolean replaceRegisteredIdentity(Identity newOid, Identity oldOid) 826 { 827 830 boolean result = false; 831 Object oe = mhtObjectEnvelopes.remove(oldOid); 832 if(oe != null) 833 { 834 mhtObjectEnvelopes.put(newOid, oe); 835 int index = mvOrderOfIds.indexOf(oldOid); 836 mvOrderOfIds.remove(index); 837 mvOrderOfIds.add(index, newOid); 838 result = true; 839 if(log.isDebugEnabled()) log.debug("Replace identity: " + oldOid + " --replaced-by--> " + newOid); 840 } 841 else 842 { 843 log.warn("Can't replace unregistered object identity (" + oldOid + ") with new identity (" + newOid + ")"); 844 } 845 return result; 846 } 847 } | Popular Tags |