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