1 package org.apache.ojb.odmg;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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
55
56
57
58
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
70
71
72 private ModificationState modificationState = null;
73 private Identity oid;
74 private Boolean hasChanged;
75 private boolean writeLocked;
76
77
78
79
80 private Object myObj;
81
82
83
84
85
86
87
88 private Map beforeImage;
89 private Map currentImage;
90 private ObjectEnvelopeTable buffer;
91
92 private List linkEntryList;
93
94
95
96
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
103 myObj = ProxyHelper.getRealObject(obj);
104 prepareInitialState(isNewObject);
105
106
107
108
109
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
147
148
149
150
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
160 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
187
188
189
190 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
210
211
212
213 refreshPKFields();
214 }
215
216
217 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
253
254
255
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
273
274
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
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311 public void beforeCommit()
312 {
313 if(myObj instanceof TransactionAware)
314 {
315 TransactionAware ta = (TransactionAware) myObj;
316 ta.beforeCommit();
317 }
318 }
319
320
321
322
323 public void afterCommit()
324 {
325 if(myObj instanceof TransactionAware)
326 {
327 TransactionAware ta = (TransactionAware) myObj;
328 ta.afterCommit();
329 }
330 }
331
332
333
334
335 public void beforeAbort()
336 {
337 if(myObj instanceof TransactionAware)
338 {
339 TransactionAware ta = (TransactionAware) myObj;
340 ta.beforeAbort();
341 }
342 }
343
344
345
346
347 public void afterAbort()
348 {
349 if(myObj instanceof TransactionAware)
350 {
351 TransactionAware ta = (TransactionAware) myObj;
352 ta.afterAbort();
353 }
354 }
355
356
357
358
359 private Map buildObjectImage(PersistenceBroker broker) throws PersistenceBrokerException
360 {
361 Map imageMap = new HashMap();
362 ClassDescriptor cld = broker.getClassDescriptor(getObject().getClass());
363
364
365 buildImageForSingleReferences(imageMap, cld);
366
367 buildImageForFields(imageMap, cld);
368
369 buildImageForCollectionReferences(imageMap, cld);
370 return imageMap;
371 }
372
373 private void buildImageForSingleReferences(Map imageMap, ClassDescriptor cld)
374 {
375
376 Iterator iter = cld.getObjectReferenceDescriptors(true).iterator();
377 ObjectReferenceDescriptor rds;
378 while(iter.hasNext())
379 {
380 rds = (ObjectReferenceDescriptor) iter.next();
381
382
383
384
385
386 if(!rds.isSuperReferenceDescriptor())
387 {
388 Object referenceObject = rds.getPersistentField().get(myObj);
389
390 IndirectionHandler handler = ProxyHelper.getIndirectionHandler(referenceObject);
391
392
393
394
395
396
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
411 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
421 Object value = fld.getPersistentField().get(myObj);
422
423 value = fld.getFieldConversion().javaToSql(value);
424
425 value = fld.getJdbcType().getFieldType().copy(value);
426
427
428 imageMap.put(fld.getPersistentField().getName(), new Image.Field(fld.getJdbcType().getFieldType(), value));
429 }
430
431 private void buildImageForCollectionReferences(Map imageMap, ClassDescriptor cld)
432 {
433
434 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
446
447
448
449 public ModificationState getModificationState()
450 {
451 return modificationState;
452 }
453
454
455
456
457 public boolean needsInsert()
458 {
459 return this.getModificationState().needsInsert();
460 }
461
462
463
464
465 public boolean needsUpdate()
466 {
467 return this.getModificationState().needsUpdate();
468 }
469
470
471
472
473 public boolean needsDelete()
474 {
475 return this.getModificationState().needsDelete();
476 }
477
478
479
480
481
482 private void prepareInitialState(boolean isNewObject)
483 {
484
485 ModificationState initialState;
486 if(isNewObject)
487 {
488
489
490 initialState = StateNewDirty.getInstance();
491 }
492 else if(isDeleted(oid))
493 {
494
495
496
497 initialState = StateOldDirty.getInstance();
498 }
499 else
500 {
501
502
503 initialState = StateOldClean.getInstance();
504 }
505
506 modificationState = initialState;
507 }
508
509
510
511
512
513
514
515
516 public boolean isDeleted(Identity id)
517 {
518 ObjectEnvelope envelope = buffer.getByIdentity(id);
519
520 return (envelope != null && envelope.needsDelete());
521 }
522
523
524
525
526
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
536
537
538
539 }
540 modificationState = newModificationState;
541 }
542 }
543
544
545
546
547
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
557
558
559
560
561
562
563
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
607
608
609
610 void markReferenceElements(PersistenceBroker broker)
611 {
612
613
614
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
624 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
702
703 setModificationState(getModificationState().markDirty());
704
705
706
707 ObjectEnvelope oe = buffer.getByIdentity(oid);
708 if(oe == null)
709 {
710 RuntimeObject rt = new RuntimeObject(refObjOrProxy, getTx());
711
712
713 getTx().lockAndRegister(rt, TransactionExt.READ, false, getTx().getRegistrationList());
714 }
715
716 addLinkOneToOne(ord, false);
717 }
718
719 public void deletedOneToOne(ObjectReferenceDescriptor ord, Object refObjOrProxy, Identity oid, boolean needsUnlink)
720 {
721
722
723 setModificationState(getModificationState().markDirty());
724 ObjectEnvelope oldRefMod = buffer.getByIdentity(oid);
725
726 if(!buffer.isNewAssociatedObject(oid))
727 {
728
729
730 if(buffer.getTransaction().cascadeDeleteFor(ord))
731 {
732 oldRefMod.setModificationState(oldRefMod.getModificationState().markDelete());
733 }
734
735 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
743 if(mod == null)
744 {
745 boolean isNew = getTx().isTransient(null, refObjOrProxy, oid);
746 mod = buffer.get(oid, refObjOrProxy, isNew);
747 }
748
749
750
751 if(mod.needsDelete())
752 {
753 mod.setModificationState(mod.getModificationState().markNew());
754 }
755 else
756 {
757
758
759
760
761 if(!(cod.isMtoNRelation() && mod.getModificationState().equals(StateOldClean.getInstance())))
762 {
763 mod.setModificationState(mod.getModificationState().markDirty());
764 }
765 }
766
767
768 buffer.addNewAssociatedIdentity(oid);
769
770 if(cod.isMtoNRelation())
771 {
772 buffer.addM2NLinkEntry(cod, getObject(), refObjOrProxy);
773 }
774 else
775 {
776
777 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
789
790 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
807
808
809 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 }