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