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