ed2f23a14292e89567c4e0111c287ea79fe2c088
[taxeditor.git] / eu.etaxonomy.taxeditor.cdmlib / src / main / java / org / hibernate / collection / internal / AbstractPersistentCollection.java
1
2
3 /*
4 * Hibernate, Relational Persistence for Idiomatic Java
5 *
6 * Copyright (c) 2008-2011, Red Hat Inc. or third-party contributors as
7 * indicated by the @author tags or express copyright attribution
8 * statements applied by the authors. All third-party contributions are
9 * distributed under license by Red Hat Inc.
10 *
11 * This copyrighted material is made available to anyone wishing to use, modify,
12 * copy, or redistribute it subject to the terms and conditions of the GNU
13 * Lesser General Public License, as published by the Free Software Foundation.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
17 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
18 * for more details.
19 *
20 * You should have received a copy of the GNU Lesser General Public License
21 * along with this distribution; if not, write to:
22 * Free Software Foundation, Inc.
23 * 51 Franklin Street, Fifth Floor
24 * Boston, MA 02110-1301 USA
25 */
26 package org.hibernate.collection.internal;
27
28 import java.io.Serializable;
29 import java.lang.reflect.Field;
30 import java.util.ArrayList;
31 import java.util.Collection;
32 import java.util.Collections;
33 import java.util.HashMap;
34 import java.util.HashSet;
35 import java.util.Iterator;
36 import java.util.List;
37 import java.util.ListIterator;
38 import java.util.Map;
39 import java.util.Set;
40 import java.util.TreeMap;
41 import java.util.TreeSet;
42
43 import javax.naming.NamingException;
44
45 import org.hibernate.AssertionFailure;
46 import org.hibernate.HibernateException;
47 import org.hibernate.LazyInitializationException;
48 import org.hibernate.Session;
49 import org.hibernate.collection.spi.PersistentCollection;
50 import org.hibernate.engine.internal.ForeignKeys;
51 import org.hibernate.engine.spi.CollectionEntry;
52 import org.hibernate.engine.spi.EntityEntry;
53 import org.hibernate.engine.spi.SessionFactoryImplementor;
54 import org.hibernate.engine.spi.SessionImplementor;
55 import org.hibernate.engine.spi.Status;
56 import org.hibernate.engine.spi.TypedValue;
57 import org.hibernate.internal.SessionFactoryRegistry;
58 import org.hibernate.internal.util.MarkerObject;
59 import org.hibernate.internal.util.collections.EmptyIterator;
60 import org.hibernate.internal.util.collections.IdentitySet;
61 import org.hibernate.persister.collection.CollectionPersister;
62 import org.hibernate.persister.entity.EntityPersister;
63 import org.hibernate.pretty.MessageHelper;
64 import org.hibernate.type.Type;
65 import org.jboss.logging.Logger;
66
67 import eu.etaxonomy.cdm.api.application.ICdmApplicationConfiguration;
68 import eu.etaxonomy.cdm.api.service.ICommonService;
69 import eu.etaxonomy.cdm.model.common.PersistentMultiLanguageText;
70
71 /**
72 * Base class implementing {@link org.hibernate.collection.spi.PersistentCollection}
73 *
74 * @author Gavin King
75 */
76 public abstract class AbstractPersistentCollection implements Serializable, PersistentCollection {
77 private static final Logger log = Logger.getLogger( AbstractPersistentCollection.class );
78
79 private static final long serialVersionUID = -7238232378593030571L;
80
81 private transient SessionImplementor session;
82 private boolean initialized;
83 private transient List<DelayedOperation> operationQueue;
84 private transient boolean directlyAccessible;
85 private transient boolean initializing;
86 private Object owner;
87 private int cachedSize = -1;
88
89 private String role;
90 private Serializable key;
91 // collections detect changes made via their public interface and mark
92 // themselves as dirty as a performance optimization
93 private boolean dirty;
94 private Serializable storedSnapshot;
95
96 private String sessionFactoryUuid;
97 private boolean specjLazyLoad = false;
98
99 public final String getRole() {
100 return role;
101 }
102
103 public final Serializable getKey() {
104 return key;
105 }
106
107 public final boolean isUnreferenced() {
108 return role == null;
109 }
110
111 public final boolean isDirty() {
112 return dirty;
113 }
114
115 public final void clearDirty() {
116 dirty = false;
117 }
118
119 public final void dirty() {
120 dirty = true;
121 }
122
123 public final Serializable getStoredSnapshot() {
124 return storedSnapshot;
125 }
126
127 //Careful: these methods do not initialize the collection.
128
129 /**
130 * Is the initialized collection empty?
131 */
132 public abstract boolean empty();
133
134 /**
135 * Called by any read-only method of the collection interface
136 */
137 protected final void read() {
138 initialize( false );
139 }
140
141 /**
142 * Called by the {@link Collection#size} method
143 */
144 @SuppressWarnings({"JavaDoc"})
145 protected boolean readSize() {
146 if ( !initialized ) {
147 if ( cachedSize != -1 && !hasQueuedOperations() ) {
148 return true;
149 }
150 else {
151 boolean isExtraLazy = withTemporarySessionIfNeeded(
152 new LazyInitializationWork<Boolean>() {
153 @Override
154 public Boolean doWork() {
155 CollectionEntry entry = session.getPersistenceContext().getCollectionEntry( AbstractPersistentCollection.this );
156
157 if ( entry != null ) {
158 CollectionPersister persister = entry.getLoadedPersister();
159 if ( persister.isExtraLazy() ) {
160 if ( hasQueuedOperations() ) {
161 session.flush();
162 }
163 cachedSize = persister.getSize( entry.getLoadedKey(), session );
164 return true;
165 }
166 else {
167 read();
168 }
169 }
170 else{
171 throwLazyInitializationExceptionIfNotConnected();
172 }
173 return false;
174 }
175 }
176 );
177 if ( isExtraLazy ) {
178 return true;
179 }
180 }
181 }
182 return false;
183 }
184
185 public static interface LazyInitializationWork<T> {
186 public T doWork();
187 }
188
189 private <T> T withTemporarySessionIfNeeded(LazyInitializationWork<T> lazyInitializationWork) {
190 SessionImplementor originalSession = null;
191 boolean isTempSession = false;
192 boolean isJTA = false;
193
194 if ( session == null ) {
195 if ( specjLazyLoad ) {
196 session = openTemporarySessionForLoading();
197 isTempSession = true;
198 }
199 else {
200 throwLazyInitializationException( "could not initialize proxy - no Session" );
201 }
202 }
203 else if ( !session.isOpen() ) {
204 if ( specjLazyLoad ) {
205 originalSession = session;
206 session = openTemporarySessionForLoading();
207 isTempSession = true;
208 }
209 else {
210 throwLazyInitializationException( "could not initialize proxy - the owning Session was closed" );
211 }
212 }
213 else if ( !session.isConnected() ) {
214 if ( specjLazyLoad ) {
215 originalSession = session;
216 session = openTemporarySessionForLoading();
217 isTempSession = true;
218 }
219 else {
220 throwLazyInitializationException( "could not initialize proxy - the owning Session is disconnected" );
221 }
222 }
223
224 if ( isTempSession ) {
225 // TODO: On the next major release, add an
226 // 'isJTA' or 'getTransactionFactory' method to Session.
227 isJTA = session.getTransactionCoordinator()
228 .getTransactionContext().getTransactionEnvironment()
229 .getTransactionFactory()
230 .compatibleWithJtaSynchronization();
231
232 if ( !isJTA ) {
233 // Explicitly handle the transactions only if we're not in
234 // a JTA environment. A lazy loading temporary session can
235 // be created even if a current session and transaction are
236 // open (ex: session.clear() was used). We must prevent
237 // multiple transactions.
238 ( ( Session) session ).beginTransaction();
239 }
240
241 session.getPersistenceContext().addUninitializedDetachedCollection(
242 session.getFactory().getCollectionPersister( getRole() ),
243 this
244 );
245 }
246
247 try {
248 return lazyInitializationWork.doWork();
249 }
250 finally {
251 if ( isTempSession ) {
252 // make sure the just opened temp session gets closed!
253 try {
254 if ( !isJTA ) {
255 ( ( Session) session ).getTransaction().commit();
256 }
257 ( (Session) session ).close();
258 }
259 catch (Exception e) {
260 log.warn( "Unable to close temporary session used to load lazy collection associated to no session" );
261 }
262 session = originalSession;
263 }
264 }
265 }
266
267 private SessionImplementor openTemporarySessionForLoading() {
268 if ( sessionFactoryUuid == null ) {
269 throwLazyInitializationException( "SessionFactory UUID not known to create temporary Session for loading" );
270 }
271
272 SessionFactoryImplementor sf = (SessionFactoryImplementor)
273 SessionFactoryRegistry.INSTANCE.getSessionFactory( sessionFactoryUuid );
274 return (SessionImplementor) sf.openSession();
275 }
276
277 protected Boolean readIndexExistence(final Object index) {
278 if ( !initialized ) {
279 Boolean extraLazyExistenceCheck = withTemporarySessionIfNeeded(
280 new LazyInitializationWork<Boolean>() {
281 @Override
282 public Boolean doWork() {
283 CollectionEntry entry = session.getPersistenceContext().getCollectionEntry( AbstractPersistentCollection.this );
284 CollectionPersister persister = entry.getLoadedPersister();
285 if ( persister.isExtraLazy() ) {
286 if ( hasQueuedOperations() ) {
287 session.flush();
288 }
289 return persister.indexExists( entry.getLoadedKey(), index, session );
290 }
291 else {
292 read();
293 }
294 return null;
295 }
296 }
297 );
298 if ( extraLazyExistenceCheck != null ) {
299 return extraLazyExistenceCheck;
300 }
301 }
302 return null;
303 }
304
305 protected Boolean readElementExistence(final Object element) {
306 if ( !initialized ) {
307 Boolean extraLazyExistenceCheck = withTemporarySessionIfNeeded(
308 new LazyInitializationWork<Boolean>() {
309 @Override
310 public Boolean doWork() {
311 CollectionEntry entry = session.getPersistenceContext().getCollectionEntry( AbstractPersistentCollection.this );
312 CollectionPersister persister = entry.getLoadedPersister();
313 if ( persister.isExtraLazy() ) {
314 if ( hasQueuedOperations() ) {
315 session.flush();
316 }
317 return persister.elementExists( entry.getLoadedKey(), element, session );
318 }
319 else {
320 read();
321 }
322 return null;
323 }
324 }
325 );
326 if ( extraLazyExistenceCheck != null ) {
327 return extraLazyExistenceCheck;
328 }
329 }
330 return null;
331 }
332
333 protected static final Object UNKNOWN = new MarkerObject( "UNKNOWN" );
334
335 protected Object readElementByIndex(final Object index) {
336 if ( !initialized ) {
337 class ExtraLazyElementByIndexReader implements LazyInitializationWork {
338 private boolean isExtraLazy;
339 private Object element;
340
341 @Override
342 public Object doWork() {
343 CollectionEntry entry = session.getPersistenceContext().getCollectionEntry( AbstractPersistentCollection.this );
344 CollectionPersister persister = entry.getLoadedPersister();
345 isExtraLazy = persister.isExtraLazy();
346 if ( isExtraLazy ) {
347 if ( hasQueuedOperations() ) {
348 session.flush();
349 }
350 element = persister.getElementByIndex( entry.getLoadedKey(), index, session, owner );
351 }
352 else {
353 read();
354 }
355 return null;
356 }
357 }
358
359 ExtraLazyElementByIndexReader reader = new ExtraLazyElementByIndexReader();
360 //noinspection unchecked
361 withTemporarySessionIfNeeded( reader );
362 if ( reader.isExtraLazy ) {
363 return reader.element;
364 }
365 }
366 return UNKNOWN;
367
368 }
369
370 protected int getCachedSize() {
371 return cachedSize;
372 }
373
374 private boolean isConnectedToSession() {
375 return session != null &&
376 session.isOpen() &&
377 session.getPersistenceContext().containsCollection( this );
378 }
379
380 /**
381 * Called by any writer method of the collection interface
382 */
383 protected final void write() {
384 initialize( true );
385 dirty();
386 }
387
388 /**
389 * Is this collection in a state that would allow us to
390 * "queue" operations?
391 */
392 @SuppressWarnings({"JavaDoc"})
393 protected boolean isOperationQueueEnabled() {
394 return !initialized &&
395 isConnectedToSession() &&
396 isInverseCollection();
397 }
398
399 /**
400 * Is this collection in a state that would allow us to
401 * "queue" puts? This is a special case, because of orphan
402 * delete.
403 */
404 @SuppressWarnings({"JavaDoc"})
405 protected boolean isPutQueueEnabled() {
406 return !initialized &&
407 isConnectedToSession() &&
408 isInverseOneToManyOrNoOrphanDelete();
409 }
410
411 /**
412 * Is this collection in a state that would allow us to
413 * "queue" clear? This is a special case, because of orphan
414 * delete.
415 */
416 @SuppressWarnings({"JavaDoc"})
417 protected boolean isClearQueueEnabled() {
418 return !initialized &&
419 isConnectedToSession() &&
420 isInverseCollectionNoOrphanDelete();
421 }
422
423 /**
424 * Is this the "inverse" end of a bidirectional association?
425 */
426 @SuppressWarnings({"JavaDoc"})
427 private boolean isInverseCollection() {
428 CollectionEntry ce = session.getPersistenceContext().getCollectionEntry( this );
429 return ce != null && ce.getLoadedPersister().isInverse();
430 }
431
432 /**
433 * Is this the "inverse" end of a bidirectional association with
434 * no orphan delete enabled?
435 */
436 @SuppressWarnings({"JavaDoc"})
437 private boolean isInverseCollectionNoOrphanDelete() {
438 CollectionEntry ce = session.getPersistenceContext().getCollectionEntry( this );
439 return ce != null &&
440 ce.getLoadedPersister().isInverse() &&
441 !ce.getLoadedPersister().hasOrphanDelete();
442 }
443
444 /**
445 * Is this the "inverse" end of a bidirectional one-to-many, or
446 * of a collection with no orphan delete?
447 */
448 @SuppressWarnings({"JavaDoc"})
449 private boolean isInverseOneToManyOrNoOrphanDelete() {
450 CollectionEntry ce = session.getPersistenceContext().getCollectionEntry( this );
451 return ce != null && ce.getLoadedPersister().isInverse() && (
452 ce.getLoadedPersister().isOneToMany() ||
453 !ce.getLoadedPersister().hasOrphanDelete()
454 );
455 }
456
457 /**
458 * Queue an addition
459 */
460 @SuppressWarnings({"JavaDoc"})
461 protected final void queueOperation(DelayedOperation operation) {
462 if ( operationQueue == null ) {
463 operationQueue = new ArrayList<DelayedOperation>( 10 );
464 }
465 operationQueue.add( operation );
466 dirty = true; //needed so that we remove this collection from the second-level cache
467 }
468
469 /**
470 * After reading all existing elements from the database,
471 * add the queued elements to the underlying collection.
472 */
473 protected final void performQueuedOperations() {
474 for ( DelayedOperation operation : operationQueue ) {
475 operation.operate();
476 }
477 }
478
479 /**
480 * After flushing, re-init snapshot state.
481 */
482 public void setSnapshot(Serializable key, String role, Serializable snapshot) {
483 this.key = key;
484 this.role = role;
485 this.storedSnapshot = snapshot;
486 }
487
488 /**
489 * After flushing, clear any "queued" additions, since the
490 * database state is now synchronized with the memory state.
491 */
492 public void postAction() {
493 operationQueue = null;
494 cachedSize = -1;
495 clearDirty();
496 }
497
498 /**
499 * Not called by Hibernate, but used by non-JDK serialization,
500 * eg. SOAP libraries.
501 */
502 public AbstractPersistentCollection() {
503 }
504
505 protected AbstractPersistentCollection(SessionImplementor session) {
506 this.session = session;
507 }
508
509 /**
510 * return the user-visible collection (or array) instance
511 */
512 public Object getValue() {
513 return this;
514 }
515
516 /**
517 * Called just before reading any rows from the JDBC result set
518 */
519 public void beginRead() {
520 // override on some subclasses
521 initializing = true;
522 }
523
524 /**
525 * Called after reading all rows from the JDBC result set
526 */
527 public boolean endRead() {
528 //override on some subclasses
529 return afterInitialize();
530 }
531
532 public boolean afterInitialize() {
533 setInitialized();
534 //do this bit after setting initialized to true or it will recurse
535 if ( operationQueue != null ) {
536 performQueuedOperations();
537 operationQueue = null;
538 cachedSize = -1;
539 return false;
540 }
541 else {
542 return true;
543 }
544 }
545
546 /**
547 * Initialize the collection, if possible, wrapping any exceptions
548 * in a runtime exception
549 *
550 * @param writing currently obsolete
551 *
552 * @throws LazyInitializationException if we cannot initialize
553 */
554 protected final void initialize(final boolean writing) {
555 // FIXME:Remoting is it true that session is null => we are in remoting mode
556 // if(session == null) {
557 // remoteInitialize();
558 // }
559
560 if ( initialized ) {
561 return;
562 }
563
564
565 withTemporarySessionIfNeeded(
566 new LazyInitializationWork<Object>() {
567 @Override
568 public Object doWork() {
569 session.initializeCollection( AbstractPersistentCollection.this, writing );
570 return null;
571 }
572 }
573 );
574 }
575
576 private void throwLazyInitializationExceptionIfNotConnected() {
577 if ( !isConnectedToSession() ) {
578 throwLazyInitializationException( "no session or session was closed" );
579 }
580 if ( !session.isConnected() ) {
581 throwLazyInitializationException( "session is disconnected" );
582 }
583 }
584
585 private void throwLazyInitializationException(String message) {
586 throw new LazyInitializationException(
587 "failed to lazily initialize a collection" +
588 (role == null ? "" : " of role: " + role) +
589 ", " + message
590 );
591 }
592
593 protected final void setInitialized() {
594 this.initializing = false;
595 this.initialized = true;
596 }
597
598 protected final void setDirectlyAccessible(boolean directlyAccessible) {
599 this.directlyAccessible = directlyAccessible;
600 }
601
602 /**
603 * Could the application possibly have a direct reference to
604 * the underlying collection implementation?
605 */
606 public boolean isDirectlyAccessible() {
607 return directlyAccessible;
608 }
609
610 /**
611 * Disassociate this collection from the given session.
612 *
613 * @return true if this was currently associated with the given session
614 */
615 public final boolean unsetSession(SessionImplementor currentSession) {
616 prepareForPossibleSpecialSpecjInitialization();
617 if ( currentSession == this.session ) {
618 this.session = null;
619 return true;
620 }
621 else {
622 return false;
623 }
624 }
625
626 protected void prepareForPossibleSpecialSpecjInitialization() {
627 if ( session != null ) {
628 specjLazyLoad = session.getFactory().getSettings().isInitializeLazyStateOutsideTransactionsEnabled();
629
630 if ( specjLazyLoad && sessionFactoryUuid == null ) {
631 try {
632 sessionFactoryUuid = (String) session.getFactory().getReference().get( "uuid" ).getContent();
633 }
634 catch (NamingException e) {
635 //not much we can do if this fails...
636 }
637 }
638 }
639 }
640
641
642 /**
643 * Associate the collection with the given session.
644 *
645 * @return false if the collection was already associated with the session
646 *
647 * @throws HibernateException if the collection was already associated
648 * with another open session
649 */
650 public final boolean setCurrentSession(SessionImplementor session) throws HibernateException {
651 if ( session == this.session ) {
652 return false;
653 }
654 else {
655 if ( isConnectedToSession() ) {
656 CollectionEntry ce = session.getPersistenceContext().getCollectionEntry( this );
657 if ( ce == null ) {
658 throw new HibernateException(
659 "Illegal attempt to associate a collection with two open sessions"
660 );
661 }
662 else {
663 throw new HibernateException(
664 "Illegal attempt to associate a collection with two open sessions: " +
665 MessageHelper.collectionInfoString(
666 ce.getLoadedPersister(), this,
667 ce.getLoadedKey(), session
668 )
669 );
670 }
671 }
672 else {
673 this.session = session;
674 return true;
675 }
676 }
677 }
678
679 /**
680 * Do we need to completely recreate this collection when it changes?
681 */
682 public boolean needsRecreate(CollectionPersister persister) {
683 return false;
684 }
685
686 /**
687 * To be called internally by the session, forcing
688 * immediate initialization.
689 */
690 public final void forceInitialization() throws HibernateException {
691 if ( !initialized ) {
692 if ( initializing ) {
693 throw new AssertionFailure( "force initialize loading collection" );
694 }
695 if ( session == null ) {
696 throw new HibernateException( "collection is not associated with any session" );
697 }
698 if ( !session.isConnected() ) {
699 throw new HibernateException( "disconnected session" );
700 }
701 session.initializeCollection( this, false );
702 }
703 }
704
705
706 /**
707 * Get the current snapshot from the session
708 */
709 @SuppressWarnings({"JavaDoc"})
710 protected final Serializable getSnapshot() {
711 return session.getPersistenceContext().getSnapshot( this );
712 }
713
714 /**
715 * Is this instance initialized?
716 */
717 public final boolean wasInitialized() {
718 return initialized;
719 }
720
721 public boolean isRowUpdatePossible() {
722 return true;
723 }
724
725 /**
726 * Does this instance have any "queued" additions?
727 */
728 public final boolean hasQueuedOperations() {
729 return operationQueue != null;
730 }
731
732 /**
733 * Iterate the "queued" additions
734 */
735 public final Iterator queuedAdditionIterator() {
736 if ( hasQueuedOperations() ) {
737 return new Iterator() {
738 int i = 0;
739
740 public Object next() {
741 return operationQueue.get( i++ ).getAddedInstance();
742 }
743
744 public boolean hasNext() {
745 return i < operationQueue.size();
746 }
747
748 public void remove() {
749 throw new UnsupportedOperationException();
750 }
751 };
752 }
753 else {
754 return EmptyIterator.INSTANCE;
755 }
756 }
757
758 /**
759 * Iterate the "queued" additions
760 */
761 @SuppressWarnings({"unchecked"})
762 public final Collection getQueuedOrphans(String entityName) {
763 if ( hasQueuedOperations() ) {
764 Collection additions = new ArrayList( operationQueue.size() );
765 Collection removals = new ArrayList( operationQueue.size() );
766 for ( DelayedOperation operation : operationQueue ) {
767 additions.add( operation.getAddedInstance() );
768 removals.add( operation.getOrphan() );
769 }
770 return getOrphans( removals, additions, entityName, session );
771 }
772 else {
773 return Collections.EMPTY_LIST;
774 }
775 }
776
777 /**
778 * Called before inserting rows, to ensure that any surrogate keys
779 * are fully generated
780 */
781 public void preInsert(CollectionPersister persister) throws HibernateException {
782 }
783
784 /**
785 * Called after inserting a row, to fetch the natively generated id
786 */
787 public void afterRowInsert(CollectionPersister persister, Object entry, int i) throws HibernateException {
788 }
789
790 /**
791 * get all "orphaned" elements
792 */
793 public abstract Collection getOrphans(Serializable snapshot, String entityName) throws HibernateException;
794
795 /**
796 * Get the current session
797 */
798 @SuppressWarnings({"JavaDoc"})
799 public final SessionImplementor getSession() {
800 return session;
801 }
802
803 protected final class IteratorProxy implements Iterator {
804 protected final Iterator itr;
805
806 public IteratorProxy(Iterator itr) {
807 this.itr = itr;
808 }
809
810 public boolean hasNext() {
811 return itr.hasNext();
812 }
813
814 public Object next() {
815 return itr.next();
816 }
817
818 public void remove() {
819 write();
820 itr.remove();
821 }
822
823 }
824
825 protected final class ListIteratorProxy implements ListIterator {
826 protected final ListIterator itr;
827
828 public ListIteratorProxy(ListIterator itr) {
829 this.itr = itr;
830 }
831
832 @SuppressWarnings({"unchecked"})
833 public void add(Object o) {
834 write();
835 itr.add( o );
836 }
837
838 public boolean hasNext() {
839 return itr.hasNext();
840 }
841
842 public boolean hasPrevious() {
843 return itr.hasPrevious();
844 }
845
846 public Object next() {
847 return itr.next();
848 }
849
850 public int nextIndex() {
851 return itr.nextIndex();
852 }
853
854 public Object previous() {
855 return itr.previous();
856 }
857
858 public int previousIndex() {
859 return itr.previousIndex();
860 }
861
862 public void remove() {
863 write();
864 itr.remove();
865 }
866
867 @SuppressWarnings({"unchecked"})
868 public void set(Object o) {
869 write();
870 itr.set( o );
871 }
872
873 }
874
875 protected class SetProxy implements java.util.Set {
876 protected final Collection set;
877
878 public SetProxy(Collection set) {
879 this.set = set;
880 }
881
882 @SuppressWarnings({"unchecked"})
883 public boolean add(Object o) {
884 write();
885 return set.add( o );
886 }
887
888 @SuppressWarnings({"unchecked"})
889 public boolean addAll(Collection c) {
890 write();
891 return set.addAll( c );
892 }
893
894 public void clear() {
895 write();
896 set.clear();
897 }
898
899 public boolean contains(Object o) {
900 return set.contains( o );
901 }
902
903 public boolean containsAll(Collection c) {
904 return set.containsAll( c );
905 }
906
907 public boolean isEmpty() {
908 return set.isEmpty();
909 }
910
911 public Iterator iterator() {
912 return new IteratorProxy( set.iterator() );
913 }
914
915 public boolean remove(Object o) {
916 write();
917 return set.remove( o );
918 }
919
920 public boolean removeAll(Collection c) {
921 write();
922 return set.removeAll( c );
923 }
924
925 public boolean retainAll(Collection c) {
926 write();
927 return set.retainAll( c );
928 }
929
930 public int size() {
931 return set.size();
932 }
933
934 public Object[] toArray() {
935 return set.toArray();
936 }
937
938 @SuppressWarnings({"unchecked"})
939 public Object[] toArray(Object[] array) {
940 return set.toArray( array );
941 }
942
943 }
944
945 protected final class ListProxy implements java.util.List {
946 protected final List list;
947
948 public ListProxy(List list) {
949 this.list = list;
950 }
951
952 @Override
953 @SuppressWarnings({"unchecked"})
954 public void add(int index, Object value) {
955 write();
956 list.add( index, value );
957 }
958
959 @Override
960 @SuppressWarnings({"unchecked"})
961 public boolean add(Object o) {
962 write();
963 return list.add( o );
964 }
965
966 @Override
967 @SuppressWarnings({"unchecked"})
968 public boolean addAll(Collection c) {
969 write();
970 return list.addAll( c );
971 }
972
973 @Override
974 @SuppressWarnings({"unchecked"})
975 public boolean addAll(int i, Collection c) {
976 write();
977 return list.addAll( i, c );
978 }
979
980 @Override
981 public void clear() {
982 write();
983 list.clear();
984 }
985
986 @Override
987 public boolean contains(Object o) {
988 return list.contains( o );
989 }
990
991 @Override
992 public boolean containsAll(Collection c) {
993 return list.containsAll( c );
994 }
995
996 @Override
997 public Object get(int i) {
998 return list.get( i );
999 }
1000
1001 @Override
1002 public int indexOf(Object o) {
1003 return list.indexOf( o );
1004 }
1005
1006 @Override
1007 public boolean isEmpty() {
1008 return list.isEmpty();
1009 }
1010
1011 @Override
1012 public Iterator iterator() {
1013 return new IteratorProxy( list.iterator() );
1014 }
1015
1016 @Override
1017 public int lastIndexOf(Object o) {
1018 return list.lastIndexOf( o );
1019 }
1020
1021 @Override
1022 public ListIterator listIterator() {
1023 return new ListIteratorProxy( list.listIterator() );
1024 }
1025
1026 @Override
1027 public ListIterator listIterator(int i) {
1028 return new ListIteratorProxy( list.listIterator( i ) );
1029 }
1030
1031 @Override
1032 public Object remove(int i) {
1033 write();
1034 return list.remove( i );
1035 }
1036
1037 @Override
1038 public boolean remove(Object o) {
1039 write();
1040 return list.remove( o );
1041 }
1042
1043 @Override
1044 public boolean removeAll(Collection c) {
1045 write();
1046 return list.removeAll( c );
1047 }
1048
1049 @Override
1050 public boolean retainAll(Collection c) {
1051 write();
1052 return list.retainAll( c );
1053 }
1054
1055 @Override
1056 @SuppressWarnings({"unchecked"})
1057 public Object set(int i, Object o) {
1058 write();
1059 return list.set( i, o );
1060 }
1061
1062 @Override
1063 public int size() {
1064 return list.size();
1065 }
1066
1067 @Override
1068 public List subList(int i, int j) {
1069 return list.subList( i, j );
1070 }
1071
1072 @Override
1073 public Object[] toArray() {
1074 return list.toArray();
1075 }
1076
1077 @Override
1078 @SuppressWarnings({"unchecked"})
1079 public Object[] toArray(Object[] array) {
1080 return list.toArray( array );
1081 }
1082
1083 }
1084
1085 /**
1086 * Contract for operations which are part of a collection's operation queue.
1087 */
1088 protected interface DelayedOperation {
1089 public void operate();
1090
1091 public Object getAddedInstance();
1092
1093 public Object getOrphan();
1094 }
1095
1096 /**
1097 * Given a collection of entity instances that used to
1098 * belong to the collection, and a collection of instances
1099 * that currently belong, return a collection of orphans
1100 */
1101 @SuppressWarnings({"JavaDoc", "unchecked"})
1102 protected static Collection getOrphans(
1103 Collection oldElements,
1104 Collection currentElements,
1105 String entityName,
1106 SessionImplementor session) throws HibernateException {
1107
1108 // short-circuit(s)
1109 if ( currentElements.size() == 0 ) {
1110 return oldElements; // no new elements, the old list contains only Orphans
1111 }
1112 if ( oldElements.size() == 0 ) {
1113 return oldElements; // no old elements, so no Orphans neither
1114 }
1115
1116 final EntityPersister entityPersister = session.getFactory().getEntityPersister( entityName );
1117 final Type idType = entityPersister.getIdentifierType();
1118
1119 // create the collection holding the Orphans
1120 Collection res = new ArrayList();
1121
1122 // collect EntityIdentifier(s) of the *current* elements - add them into a HashSet for fast access
1123 java.util.Set currentIds = new HashSet();
1124 java.util.Set currentSaving = new IdentitySet();
1125 for ( Object current : currentElements ) {
1126 if ( current != null && ForeignKeys.isNotTransient( entityName, current, null, session ) ) {
1127 EntityEntry ee = session.getPersistenceContext().getEntry( current );
1128 if ( ee != null && ee.getStatus() == Status.SAVING ) {
1129 currentSaving.add( current );
1130 }
1131 else {
1132 Serializable currentId = ForeignKeys.getEntityIdentifierIfNotUnsaved(
1133 entityName,
1134 current,
1135 session
1136 );
1137 currentIds.add( new TypedValue( idType, currentId, entityPersister.getEntityMode() ) );
1138 }
1139 }
1140 }
1141
1142 // iterate over the *old* list
1143 for ( Object old : oldElements ) {
1144 if ( !currentSaving.contains( old ) ) {
1145 Serializable oldId = ForeignKeys.getEntityIdentifierIfNotUnsaved( entityName, old, session );
1146 if ( !currentIds.contains( new TypedValue( idType, oldId, entityPersister.getEntityMode() ) ) ) {
1147 res.add( old );
1148 }
1149 }
1150 }
1151
1152 return res;
1153 }
1154
1155 public static void identityRemove(
1156 Collection list,
1157 Object object,
1158 String entityName,
1159 SessionImplementor session) throws HibernateException {
1160
1161 if ( object != null && ForeignKeys.isNotTransient( entityName, object, null, session ) ) {
1162 final EntityPersister entityPersister = session.getFactory().getEntityPersister( entityName );
1163 Type idType = entityPersister.getIdentifierType();
1164
1165 Serializable idOfCurrent = ForeignKeys.getEntityIdentifierIfNotUnsaved( entityName, object, session );
1166 Iterator itr = list.iterator();
1167 while ( itr.hasNext() ) {
1168 Serializable idOfOld = ForeignKeys.getEntityIdentifierIfNotUnsaved( entityName, itr.next(), session );
1169 if ( idType.isEqual( idOfCurrent, idOfOld, session.getFactory() ) ) {
1170 itr.remove();
1171 break;
1172 }
1173 }
1174
1175 }
1176 }
1177
1178 public Object getIdentifier(Object entry, int i) {
1179 throw new UnsupportedOperationException();
1180 }
1181
1182 public Object getOwner() {
1183 return owner;
1184 }
1185
1186 public void setOwner(Object owner) {
1187 this.owner = owner;
1188 }
1189
1190 /** Below is section of code which makes remote service calls */
1191
1192 private static ICdmApplicationConfiguration configuration;
1193
1194
1195 public static void setConfiguration(ICdmApplicationConfiguration conf) {
1196 configuration = conf;
1197 }
1198
1199 private void remoteInitialize() {
1200
1201 if (getOwner() != null && !initialized) {
1202
1203 try {
1204 String role = getRole();
1205 String fieldName = role.substring(role.lastIndexOf(".") + 1);
1206 log.info("--> Remote Lazy Initializing " + getRole() + " , field : " + fieldName);
1207 Object owner = getOwner();
1208
1209 if(configuration == null) {
1210 throw new HibernateException("CdmApplicationRemoteConfiguration not initialized (null)");
1211 }
1212 ICommonService commonService = configuration.getCommonService();
1213 if(commonService == null) {
1214 throw new HibernateException("commonService not initialized (null)");
1215 }
1216
1217 PersistentCollection col = commonService.initializeCollection(this);
1218 afterInitialize();
1219
1220 Class<?> clazz = getClass();
1221 if (clazz != null) {
1222 CollectionField cf = getCollectionField(col);
1223 Field field = clazz.getDeclaredField(cf.getFieldName());
1224 field.setAccessible(true);
1225 field.set(this, cf.getCollection());
1226 }
1227 } catch (Exception ex) {
1228 log.warn(ex.getMessage());
1229 }
1230 }
1231 }
1232
1233 private CollectionField getCollectionField(PersistentCollection pc) {
1234 if(pc != null) {
1235 if(pc instanceof PersistentSet) {
1236 return new CollectionField(new HashSet((Set)pc), "set");
1237 }
1238 if(pc instanceof PersistentSortedSet) {
1239 return new CollectionField(new TreeSet((Set)pc), "set");
1240 }
1241 if(pc instanceof PersistentList) {
1242 return new CollectionField(new ArrayList((List)pc), "list");
1243 }
1244 if(pc instanceof PersistentMap || pc instanceof PersistentMultiLanguageText) {
1245 return new CollectionField(new HashMap((Map)pc), "map");
1246 }
1247 if(pc instanceof PersistentSortedMap) {
1248 return new CollectionField(new TreeMap((Map)pc), "map");
1249 }
1250 }
1251 return null;
1252 }
1253
1254 private String getCollectionFieldName(PersistentCollection pc) {
1255 if(pc != null) {
1256 if(pc instanceof PersistentSet || pc instanceof PersistentSortedSet) {
1257 return "set";
1258 }
1259 if(pc instanceof PersistentList) {
1260 return "list";
1261 }
1262 if(pc instanceof PersistentMap || pc instanceof PersistentMultiLanguageText) {
1263 return "map";
1264 }
1265 }
1266 return null;
1267 }
1268
1269 private class CollectionField {
1270 private Object col;
1271 private String fieldName;
1272 public CollectionField(Object col, String fieldName) {
1273 this.col = col;
1274 this.fieldName = fieldName;
1275 }
1276
1277 public Object getCollection() {
1278 return this.col;
1279 }
1280
1281 public String getFieldName() {
1282 return this.fieldName;
1283 }
1284 }
1285
1286 }
1287