1 package org.apache.ojb.odmg; 2 3 17 23 24 import java.util.HashMap ; 25 import java.util.Iterator ; 26 import java.util.Map ; 27 import java.util.List ; 28 import java.util.ArrayList ; 29 30 import org.apache.commons.lang.builder.ToStringBuilder; 31 import org.apache.ojb.broker.Identity; 32 import org.apache.ojb.broker.PersistenceBroker; 33 import org.apache.ojb.broker.PersistenceBrokerException; 34 import org.apache.ojb.broker.OJBRuntimeException; 35 import org.apache.ojb.broker.PersistenceBrokerInternal; 36 import org.apache.ojb.broker.core.proxy.IndirectionHandler; 37 import org.apache.ojb.broker.core.proxy.ProxyHelper; 38 import org.apache.ojb.broker.metadata.ClassDescriptor; 39 import org.apache.ojb.broker.metadata.CollectionDescriptor; 40 import org.apache.ojb.broker.metadata.FieldDescriptor; 41 import org.apache.ojb.broker.metadata.ObjectReferenceDescriptor; 42 import org.apache.ojb.broker.util.BrokerHelper; 43 import org.apache.ojb.broker.util.ObjectModification; 44 import org.apache.ojb.broker.util.logging.Logger; 45 import org.apache.ojb.broker.util.logging.LoggerFactory; 46 import org.apache.ojb.odmg.states.ModificationState; 47 import org.apache.ojb.odmg.states.StateNewDirty; 48 import org.apache.ojb.odmg.states.StateOldClean; 49 import org.apache.ojb.odmg.states.StateOldDirty; 50 import org.apache.ojb.odmg.link.LinkEntry; 51 import org.apache.ojb.odmg.link.LinkEntryOneToOne; 52 import org.apache.ojb.odmg.link.LinkEntryOneToN; 53 54 59 public class ObjectEnvelope implements ObjectModification, Image.ImageListener 60 { 61 private Logger log = LoggerFactory.getLogger(ObjectEnvelope.class); 62 63 static final long serialVersionUID = -829177767933340522L; 64 65 static final int IS_MATERIALIZED_OBJECT = 11; 66 static final int IS_MATERIALIZED_PROXY = 13; 67 static final int IS_UNMATERIALIZED_PROXY = 17; 68 69 72 private ModificationState modificationState = null; 73 private Identity oid; 74 private Boolean hasChanged; 75 private boolean writeLocked; 76 77 80 private Object myObj; 81 82 88 private Map beforeImage; 89 private Map currentImage; 90 private ObjectEnvelopeTable buffer; 91 private List linkEntryList; 93 94 97 public ObjectEnvelope(ObjectEnvelopeTable buffer, Identity oid, Object obj, boolean isNewObject) 98 { 99 this.linkEntryList = new ArrayList (); 100 this.buffer = buffer; 101 this.oid = oid; 102 myObj = ProxyHelper.getRealObject(obj); 104 prepareInitialState(isNewObject); 105 110 beforeImage = buildObjectImage(getBroker()); 111 } 112 113 public PersistenceBrokerInternal getBroker() 114 { 115 return buffer.getTransaction().getBrokerInternal(); 116 } 117 118 TransactionImpl getTx() 119 { 120 return buffer.getTransaction(); 121 } 122 123 ObjectEnvelopeTable getEnvelopeTable() 124 { 125 return buffer; 126 } 127 128 public Map getBeforeImage() 129 { 130 if(beforeImage == null) 131 { 132 beforeImage = buildObjectImage(getBroker()); 133 } 134 return beforeImage; 135 } 136 137 public Map getCurrentImage() 138 { 139 if(currentImage == null) 140 { 141 currentImage = buildObjectImage(getBroker()); 142 } 143 return currentImage; 144 } 145 146 151 public void cleanup(boolean reuse, boolean wasInsert) 152 { 153 if(currentImage != null) 154 { 155 performImageCleanup(currentImage, reuse); 156 } 157 if(beforeImage != null) 158 { 159 performImageCleanup(beforeImage, false); 161 } 162 if(reuse) 163 { 164 refreshObjectImage(wasInsert); 165 } 166 else 167 { 168 myObj = null; 169 } 170 } 171 172 private void performImageCleanup(Map imageMap, boolean reuse) 173 { 174 Iterator iterator = imageMap.values().iterator(); 175 while(iterator.hasNext()) 176 { 177 Image base = (Image) iterator.next(); 178 if(base != null) base.cleanup(reuse); 179 } 180 } 181 182 private void refreshObjectImage(boolean wasInsert) 183 { 184 try 185 { 186 if(getIdentity().isTransient()) 191 { 192 refreshIdentity(); 193 } 194 if(currentImage != null) 195 { 196 beforeImage = currentImage; 197 } 198 else 199 { 200 if(beforeImage == null) 201 { 202 beforeImage = buildObjectImage(getBroker()); 203 } 204 } 205 currentImage = null; 206 hasChanged = null; 207 if(wasInsert) 208 { 209 213 refreshPKFields(); 214 } 215 refreshLockingFields(); 218 } 219 catch(PersistenceBrokerException e) 220 { 221 beforeImage = null; 222 currentImage = null; 223 hasChanged = null; 224 log.error("Can't refresh object image for " + getIdentity(), e); 225 throw e; 226 } 227 } 228 229 private void refreshPKFields() 230 { 231 FieldDescriptor[] flds = getClassDescriptor().getPkFields(); 232 for(int i = 0; i < flds.length; i++) 233 { 234 FieldDescriptor fld = flds[i]; 235 addFieldImage(beforeImage, fld); 236 } 237 } 238 239 private void refreshLockingFields() 240 { 241 if(getClassDescriptor().isLocking()) 242 { 243 FieldDescriptor[] flds = getClassDescriptor().getLockingFields(); 244 for(int i = 0; i < flds.length; i++) 245 { 246 FieldDescriptor fld = flds[i]; 247 addFieldImage(beforeImage, fld); 248 } 249 } 250 } 251 252 256 public Identity refreshIdentity() 257 { 258 Identity oldOid = getIdentity(); 259 this.oid = getBroker().serviceIdentity().buildIdentity(myObj); 260 return oldOid; 261 } 262 263 public Identity getIdentity() 264 { 265 if(oid == null) 266 { 267 oid = getBroker().serviceIdentity().buildIdentity(getObject()); 268 } 269 return oid; 270 } 271 272 275 public Object getObject() 276 { 277 return myObj; 278 } 279 280 public Object getRealObject() 281 { 282 return ProxyHelper.getRealObject(getObject()); 283 } 284 285 public void refreshObjectIfNeeded(Object obj) 286 { 287 if(this.myObj != obj) 288 { 289 this.myObj = obj; 290 } 291 } 292 293 311 public void beforeCommit() 312 { 313 if(myObj instanceof TransactionAware) 314 { 315 TransactionAware ta = (TransactionAware) myObj; 316 ta.beforeCommit(); 317 } 318 } 319 320 323 public void afterCommit() 324 { 325 if(myObj instanceof TransactionAware) 326 { 327 TransactionAware ta = (TransactionAware) myObj; 328 ta.afterCommit(); 329 } 330 } 331 332 335 public void beforeAbort() 336 { 337 if(myObj instanceof TransactionAware) 338 { 339 TransactionAware ta = (TransactionAware) myObj; 340 ta.beforeAbort(); 341 } 342 } 343 344 347 public void afterAbort() 348 { 349 if(myObj instanceof TransactionAware) 350 { 351 TransactionAware ta = (TransactionAware) myObj; 352 ta.afterAbort(); 353 } 354 } 355 356 359 private Map buildObjectImage(PersistenceBroker broker) throws PersistenceBrokerException 360 { 361 Map imageMap = new HashMap (); 362 ClassDescriptor cld = broker.getClassDescriptor(getObject().getClass()); 363 buildImageForSingleReferences(imageMap, cld); 366 buildImageForFields(imageMap, cld); 368 buildImageForCollectionReferences(imageMap, cld); 370 return imageMap; 371 } 372 373 private void buildImageForSingleReferences(Map imageMap, ClassDescriptor cld) 374 { 375 Iterator iter = cld.getObjectReferenceDescriptors(true).iterator(); 377 ObjectReferenceDescriptor rds; 378 while(iter.hasNext()) 379 { 380 rds = (ObjectReferenceDescriptor) iter.next(); 381 386 if(!rds.isSuperReferenceDescriptor()) 387 { 388 Object referenceObject = rds.getPersistentField().get(myObj); 389 390 IndirectionHandler handler = ProxyHelper.getIndirectionHandler(referenceObject); 391 397 if(handler == null && referenceObject != null 398 && BrokerHelper.hasAnonymousKeyReference(rds.getClassDescriptor(), rds)) 399 { 400 getBroker().serviceBrokerHelper().link(myObj, rds, false); 401 } 402 Image.SingleRef singleRef = new Image.SingleRef(this, rds, referenceObject); 403 imageMap.put(rds, singleRef); 404 } 405 } 406 } 407 408 private void buildImageForFields(Map imageMap, ClassDescriptor cld) 409 { 410 FieldDescriptor[] fieldDescs = cld.getFieldDescriptor(true); 412 for(int i = 0; i < fieldDescs.length; i++) 413 { 414 addFieldImage(imageMap, fieldDescs[i]); 415 } 416 } 417 418 private void addFieldImage(Map imageMap, FieldDescriptor fld) 419 { 420 Object value = fld.getPersistentField().get(myObj); 422 value = fld.getFieldConversion().javaToSql(value); 424 value = fld.getJdbcType().getFieldType().copy(value); 426 imageMap.put(fld.getPersistentField().getName(), new Image.Field(fld.getJdbcType().getFieldType(), value)); 429 } 430 431 private void buildImageForCollectionReferences(Map imageMap, ClassDescriptor cld) 432 { 433 Iterator collections = cld.getCollectionDescriptors(true).iterator(); 435 CollectionDescriptor cds; 436 while(collections.hasNext()) 437 { 438 cds = (CollectionDescriptor) collections.next(); 439 Object collectionOrArray = cds.getPersistentField().get(myObj); 440 Image.MultipleRef colRef = new Image.MultipleRef(this, cds, collectionOrArray); 441 imageMap.put(cds, colRef); 442 } 443 } 444 445 449 public ModificationState getModificationState() 450 { 451 return modificationState; 452 } 453 454 457 public boolean needsInsert() 458 { 459 return this.getModificationState().needsInsert(); 460 } 461 462 465 public boolean needsUpdate() 466 { 467 return this.getModificationState().needsUpdate(); 468 } 469 470 473 public boolean needsDelete() 474 { 475 return this.getModificationState().needsDelete(); 476 } 477 478 482 private void prepareInitialState(boolean isNewObject) 483 { 484 ModificationState initialState; 486 if(isNewObject) 487 { 488 initialState = StateNewDirty.getInstance(); 491 } 492 else if(isDeleted(oid)) 493 { 494 initialState = StateOldDirty.getInstance(); 498 } 499 else 500 { 501 initialState = StateOldClean.getInstance(); 504 } 505 modificationState = initialState; 507 } 508 509 516 public boolean isDeleted(Identity id) 517 { 518 ObjectEnvelope envelope = buffer.getByIdentity(id); 519 520 return (envelope != null && envelope.needsDelete()); 521 } 522 523 527 public void setModificationState(ModificationState newModificationState) 528 { 529 if(newModificationState != modificationState) 530 { 531 if(log.isDebugEnabled()) 532 { 533 log.debug("object state transition for object " + this.oid + " (" 534 + modificationState + " --> " + newModificationState + ")"); 535 } 540 modificationState = newModificationState; 541 } 542 } 543 544 548 public String toString() 549 { 550 ToStringBuilder buf = new ToStringBuilder(this); 551 buf.append("Identity", oid) 552 .append("ModificationState", modificationState.toString()); 553 return buf.toString(); 554 } 555 556 564 public boolean hasChanged(PersistenceBroker broker) 565 { 566 if(hasChanged == null) 567 { 568 Map current = null; 569 try 570 { 571 current = getCurrentImage(); 572 } 573 catch(Exception e) 574 { 575 log.warn("Could not verify object changes, mark dirty: " + getIdentity(), e); 576 } 577 if(beforeImage != null && current != null) 578 { 579 Iterator it = beforeImage.entrySet().iterator(); 580 hasChanged = Boolean.FALSE; 581 while(it.hasNext()) 582 { 583 Map.Entry entry = (Map.Entry ) it.next(); 584 Image imageBefore = (Image) entry.getValue(); 585 Image imageCurrent = (Image) current.get(entry.getKey()); 586 if(imageBefore.modified(imageCurrent)) 587 { 588 hasChanged = Boolean.TRUE; 589 break; 590 } 591 } 592 } 593 else 594 { 595 hasChanged = Boolean.TRUE; 596 } 597 if(log.isDebugEnabled()) 598 { 599 log.debug("State detection for " + getIdentity() + " --> object " 600 + (hasChanged.booleanValue() ? "has changed" : "unchanged")); 601 } 602 } 603 return hasChanged.booleanValue(); 604 } 605 606 610 void markReferenceElements(PersistenceBroker broker) 611 { 612 615 Map oldImage = getBeforeImage(); 616 Map newImage = getCurrentImage(); 617 618 Iterator iter = newImage.entrySet().iterator(); 619 while (iter.hasNext()) 620 { 621 Map.Entry entry = (Map.Entry ) iter.next(); 622 Object key = entry.getKey(); 623 if(key instanceof ObjectReferenceDescriptor) 625 { 626 Image oldRefImage = (Image) oldImage.get(key); 627 Image newRefImage = (Image) entry.getValue(); 628 newRefImage.performReferenceDetection(oldRefImage); 629 } 630 } 631 } 632 633 public void doUpdate() 634 { 635 if(log.isDebugEnabled()) log.debug("Start UPDATE action for " + getIdentity()); 636 performLinkEntries(); 637 getBroker().store(getObject(), getIdentity(), getClassDescriptor(), false, true); 638 } 639 640 public void doInsert() 641 { 642 if(log.isDebugEnabled()) log.debug("Start INSERT action for " + getIdentity()); 643 performLinkEntries(); 644 getBroker().store(getObject(), getIdentity(), getClassDescriptor(), true, true); 645 Identity oldOid = refreshIdentity(); 646 buffer.replaceRegisteredIdentity(getIdentity(), oldOid); 647 } 648 649 public void doDelete() 650 { 651 if(log.isDebugEnabled()) log.debug("Start DELETE action for " + getIdentity()); 652 getBroker().delete(getObject(), true); 653 } 654 655 public void doEvictFromCache() 656 { 657 if(log.isDebugEnabled()) log.debug("Remove from cache " + getIdentity()); 658 getBroker().removeFromCache(getIdentity()); 659 } 660 661 public boolean isWriteLocked() 662 { 663 return writeLocked; 664 } 665 666 public void setWriteLocked(boolean writeLocked) 667 { 668 this.writeLocked = writeLocked; 669 } 670 671 ClassDescriptor getClassDescriptor() 672 { 673 return getBroker().getClassDescriptor(ProxyHelper.getRealClass(getObject())); 674 } 675 676 void addLinkOneToOne(ObjectReferenceDescriptor ord, boolean unlink) 677 { 678 LinkEntry entry = new LinkEntryOneToOne(ord, getObject(), unlink); 679 linkEntryList.add(entry); 680 } 681 682 void addLinkOneToN(CollectionDescriptor col, Object source, boolean unlink) 683 { 684 if(col.isMtoNRelation()) throw new OJBRuntimeException("Expected an 1:n relation, but specified a m:n"); 685 LinkEntry entry = new LinkEntryOneToN(source, col, getObject(), unlink); 686 linkEntryList.add(entry); 687 } 688 689 private void performLinkEntries() 690 { 691 PersistenceBroker broker = getBroker(); 692 for(int i = 0; i < linkEntryList.size(); i++) 693 { 694 LinkEntry linkEntry = (LinkEntry) linkEntryList.get(i); 695 linkEntry.execute(broker); 696 } 697 } 698 699 public void addedOneToOne(ObjectReferenceDescriptor ord, Object refObjOrProxy, Identity oid) 700 { 701 setModificationState(getModificationState().markDirty()); 704 ObjectEnvelope oe = buffer.getByIdentity(oid); 708 if(oe == null) 709 { 710 RuntimeObject rt = new RuntimeObject(refObjOrProxy, getTx()); 711 getTx().lockAndRegister(rt, TransactionExt.READ, false, getTx().getRegistrationList()); 714 } 715 addLinkOneToOne(ord, false); 717 } 718 719 public void deletedOneToOne(ObjectReferenceDescriptor ord, Object refObjOrProxy, Identity oid, boolean needsUnlink) 720 { 721 setModificationState(getModificationState().markDirty()); 724 ObjectEnvelope oldRefMod = buffer.getByIdentity(oid); 725 if(!buffer.isNewAssociatedObject(oid)) 727 { 728 if(buffer.getTransaction().cascadeDeleteFor(ord)) 731 { 732 oldRefMod.setModificationState(oldRefMod.getModificationState().markDelete()); 733 } 734 if(needsUnlink) addLinkOneToOne(ord, true); 736 } 737 } 738 739 public void addedXToN(CollectionDescriptor cod, Object refObjOrProxy, Identity oid) 740 { 741 ObjectEnvelope mod = buffer.getByIdentity(oid); 742 if(mod == null) 744 { 745 boolean isNew = getTx().isTransient(null, refObjOrProxy, oid); 746 mod = buffer.get(oid, refObjOrProxy, isNew); 747 } 748 if(mod.needsDelete()) 752 { 753 mod.setModificationState(mod.getModificationState().markNew()); 754 } 755 else 756 { 757 761 if(!(cod.isMtoNRelation() && mod.getModificationState().equals(StateOldClean.getInstance()))) 762 { 763 mod.setModificationState(mod.getModificationState().markDirty()); 764 } 765 } 766 buffer.addNewAssociatedIdentity(oid); 769 if(cod.isMtoNRelation()) 771 { 772 buffer.addM2NLinkEntry(cod, getObject(), refObjOrProxy); 773 } 774 else 775 { 776 mod.addLinkOneToN(cod, getObject(), false); 778 } 779 if(mod.needsInsert()) 780 { 781 buffer.addForInsertDependent(mod); 782 } 783 } 784 785 public void deletedXToN(CollectionDescriptor cod, Object refObjOrProxy, Identity oid) 786 { 787 ObjectEnvelope mod = buffer.getByIdentity(oid); 788 if(!buffer.isNewAssociatedObject(oid)) 791 { 792 if(mod != null) 793 { 794 boolean cascade = buffer.getTransaction().cascadeDeleteFor(cod); 795 if(cascade) 796 { 797 mod.setModificationState(mod.getModificationState().markDelete()); 798 buffer.addForDeletionDependent(mod); 799 } 800 if(cod.isMtoNRelation()) 801 { 802 buffer.addM2NUnlinkEntry(cod, getObject(), refObjOrProxy); 803 } 804 else 805 { 806 if(!cascade) 810 { 811 mod.setModificationState(mod.getModificationState().markDirty()); 812 mod.addLinkOneToN(cod, getObject(), true); 813 } 814 } 815 } 816 else 817 { 818 throw new Image.ImageException("Unexpected behaviour, unregistered object to delete: " 819 + oid + ", main object is " + getIdentity()+ ", envelope object is " + this.toString()); 820 } 821 } 822 } 823 } | Popular Tags |