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