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