Project

General

Profile

Download (33 KB) Statistics
| Branch: | Tag: | Revision:
1

    
2

    
3
/*
4
 * Hibernate, Relational Persistence for Idiomatic Java
5
 *
6
 * Copyright (c) 2008-2011, Red Hat Inc. or third-party contributors as
7
 * indicated by the @author tags or express copyright attribution
8
 * statements applied by the authors.  All third-party contributions are
9
 * distributed under license by Red Hat Inc.
10
 *
11
 * This copyrighted material is made available to anyone wishing to use, modify,
12
 * copy, or redistribute it subject to the terms and conditions of the GNU
13
 * Lesser General Public License, as published by the Free Software Foundation.
14
 *
15
 * This program is distributed in the hope that it will be useful,
16
 * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
17
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
18
 * for more details.
19
 *
20
 * You should have received a copy of the GNU Lesser General Public License
21
 * along with this distribution; if not, write to:
22
 * Free Software Foundation, Inc.
23
 * 51 Franklin Street, Fifth Floor
24
 * Boston, MA  02110-1301  USA
25
 */
26
package org.hibernate.collection.internal;
27

    
28
import java.io.Serializable;
29
import java.lang.reflect.Field;
30
import java.util.ArrayList;
31
import java.util.Collection;
32
import java.util.Collections;
33
import java.util.HashSet;
34
import java.util.Iterator;
35
import java.util.List;
36
import java.util.ListIterator;
37

    
38
import javax.naming.NamingException;
39

    
40
import org.hibernate.AssertionFailure;
41
import org.hibernate.HibernateException;
42
import org.hibernate.LazyInitializationException;
43
import org.hibernate.Session;
44
import org.hibernate.collection.spi.PersistentCollection;
45
import org.hibernate.engine.internal.ForeignKeys;
46
import org.hibernate.engine.spi.CollectionEntry;
47
import org.hibernate.engine.spi.EntityEntry;
48
import org.hibernate.engine.spi.SessionFactoryImplementor;
49
import org.hibernate.engine.spi.SessionImplementor;
50
import org.hibernate.engine.spi.Status;
51
import org.hibernate.engine.spi.TypedValue;
52
import org.hibernate.internal.SessionFactoryRegistry;
53
import org.hibernate.internal.util.MarkerObject;
54
import org.hibernate.internal.util.collections.EmptyIterator;
55
import org.hibernate.internal.util.collections.IdentitySet;
56
import org.hibernate.persister.collection.CollectionPersister;
57
import org.hibernate.persister.entity.EntityPersister;
58
import org.hibernate.pretty.MessageHelper;
59
import org.hibernate.type.Type;
60
import org.jboss.logging.Logger;
61

    
62
import eu.etaxonomy.cdm.api.application.CdmApplicationRemoteConfiguration;
63
import eu.etaxonomy.taxeditor.remoting.CdmEagerLoadingException;
64
import eu.etaxonomy.taxeditor.service.ICachedCommonService;
65
import eu.etaxonomy.taxeditor.service.CachedCommonServiceImpl.CollectionField;
66

    
67
/**
68
 * Base class implementing {@link org.hibernate.collection.spi.PersistentCollection}
69
 *
70
 * @author Gavin King
71
 */
72
public abstract class AbstractPersistentCollection implements Serializable, PersistentCollection {
73
	private static final Logger log = Logger.getLogger( AbstractPersistentCollection.class );
74

    
75
	private static final long serialVersionUID = -7238232378593030571L;
76

    
77
	private transient SessionImplementor session;
78
	private boolean initialized;
79
	private transient List<DelayedOperation> operationQueue;
80
	private transient boolean directlyAccessible;
81
	private transient boolean initializing;
82
	private Object owner;
83
	private int cachedSize = -1;
84

    
85
	private String role;
86
	private Serializable key;
87
	// collections detect changes made via their public interface and mark
88
	// themselves as dirty as a performance optimization
89
	private boolean dirty;
90
	private Serializable storedSnapshot;
91

    
92
	private String sessionFactoryUuid;
93
	private boolean specjLazyLoad = false;
94

    
95
	@Override
96
    public final String getRole() {
97
		return role;
98
	}
99

    
100
	@Override
101
    public final Serializable getKey() {
102
		return key;
103
	}
104

    
105
	@Override
106
    public final boolean isUnreferenced() {
107
		return role == null;
108
	}
109

    
110
	@Override
111
    public final boolean isDirty() {
112
		return dirty;
113
	}
114

    
115
	@Override
116
    public final void clearDirty() {
117
		dirty = false;
118
	}
119

    
120
	@Override
121
    public final void dirty() {
122
		dirty = true;
123
	}
124

    
125
	@Override
126
    public final Serializable getStoredSnapshot() {
127
		return storedSnapshot;
128
	}
129

    
130
	//Careful: these methods do not initialize the collection.
131

    
132
	/**
133
	 * Is the initialized collection empty?
134
	 */
135
	@Override
136
    public abstract boolean empty();
137

    
138
	/**
139
	 * Called by any read-only method of the collection interface
140
	 */
141
	protected final void read() {
142
		initialize( false );
143
	}
144

    
145
	/**
146
	 * Called by the {@link Collection#size} method
147
	 */
148
	@SuppressWarnings({"JavaDoc"})
149
	protected boolean readSize() {
150
		if ( !initialized ) {
151
			if ( cachedSize != -1 && !hasQueuedOperations() ) {
152
				return true;
153
			}
154
			else {
155
				// In remoting we are sure that session is null
156
				// both when using property paths and switching off conversations
157
				if(session == null && remoting) {
158
					log.info("--> readSize, of " + getRole() + " with key " + getKey());
159
					read();
160
				} else {
161
					boolean isExtraLazy = withTemporarySessionIfNeeded(
162
							new LazyInitializationWork<Boolean>() {
163
								@Override
164
								public Boolean doWork() {
165
									CollectionEntry entry = session.getPersistenceContext().getCollectionEntry( AbstractPersistentCollection.this );
166

    
167
									if ( entry != null ) {
168
										CollectionPersister persister = entry.getLoadedPersister();
169
										if ( persister.isExtraLazy() ) {
170
											if ( hasQueuedOperations() ) {
171
												session.flush();
172
											}
173
											cachedSize = persister.getSize( entry.getLoadedKey(), session );
174
											return true;
175
										}
176
										else {
177
											read();
178
										}
179
									}
180
									else{
181
										throwLazyInitializationExceptionIfNotConnected();
182
									}
183
									return false;
184
								}
185
							}
186
							);
187
					if ( isExtraLazy ) {
188
						return true;
189
					}
190
				}
191
			}
192
		}
193
		return false;
194
	}
195

    
196
	public static interface LazyInitializationWork<T> {
197
		public T doWork();
198
	}
199

    
200
	private <T> T withTemporarySessionIfNeeded(LazyInitializationWork<T> lazyInitializationWork) {
201
		SessionImplementor originalSession = null;
202
		boolean isTempSession = false;
203
		boolean isJTA = false;
204

    
205
		if ( session == null ) {
206
			if ( specjLazyLoad ) {
207
				session = openTemporarySessionForLoading();
208
				isTempSession = true;
209
			}
210
			else {
211
				throwLazyInitializationException( "could not initialize proxy - no Session" );
212
			}
213
		}
214
		else if ( !session.isOpen() ) {
215
			if ( specjLazyLoad ) {
216
				originalSession = session;
217
				session = openTemporarySessionForLoading();
218
				isTempSession = true;
219
			}
220
			else {
221
				throwLazyInitializationException( "could not initialize proxy - the owning Session was closed" );
222
			}
223
		}
224
		else if ( !session.isConnected() ) {
225
			if ( specjLazyLoad ) {
226
				originalSession = session;
227
				session = openTemporarySessionForLoading();
228
				isTempSession = true;
229
			}
230
			else {
231
				throwLazyInitializationException( "could not initialize proxy - the owning Session is disconnected" );
232
			}
233
		}
234

    
235
		if ( isTempSession ) {
236
			// TODO: On the next major release, add an
237
			// 'isJTA' or 'getTransactionFactory' method to Session.
238
			isJTA = session.getTransactionCoordinator()
239
					.getTransactionContext().getTransactionEnvironment()
240
					.getTransactionFactory()
241
					.compatibleWithJtaSynchronization();
242

    
243
			if ( !isJTA ) {
244
				// Explicitly handle the transactions only if we're not in
245
				// a JTA environment.  A lazy loading temporary session can
246
				// be created even if a current session and transaction are
247
				// open (ex: session.clear() was used).  We must prevent
248
				// multiple transactions.
249
				( ( Session) session ).beginTransaction();
250
			}
251

    
252
			session.getPersistenceContext().addUninitializedDetachedCollection(
253
					session.getFactory().getCollectionPersister( getRole() ),
254
					this
255
			);
256
		}
257

    
258
		try {
259
			return lazyInitializationWork.doWork();
260
		}
261
		finally {
262
			if ( isTempSession ) {
263
				// make sure the just opened temp session gets closed!
264
				try {
265
					if ( !isJTA ) {
266
						( ( Session) session ).getTransaction().commit();
267
					}
268
					( (Session) session ).close();
269
				}
270
				catch (Exception e) {
271
					log.warn( "Unable to close temporary session used to load lazy collection associated to no session" );
272
				}
273
				session = originalSession;
274
			}
275
		}
276
	}
277

    
278
	private SessionImplementor openTemporarySessionForLoading() {
279
		if ( sessionFactoryUuid == null ) {
280
			throwLazyInitializationException( "SessionFactory UUID not known to create temporary Session for loading" );
281
		}
282

    
283
		SessionFactoryImplementor sf = (SessionFactoryImplementor)
284
				SessionFactoryRegistry.INSTANCE.getSessionFactory( sessionFactoryUuid );
285
		return (SessionImplementor) sf.openSession();
286
	}
287

    
288
	protected Boolean readIndexExistence(final Object index) {
289
		if ( !initialized ) {
290
			// In remoting we are sure that session is null
291
			// both when using property paths and switching off conversations
292
			if(session == null && remoting) {
293
				log.info("--> readIndexExistence, of " + getRole() + " with key " + getKey());
294
				read();
295
			} else {
296
				Boolean extraLazyExistenceCheck = withTemporarySessionIfNeeded(
297
						new LazyInitializationWork<Boolean>() {
298
							@Override
299
							public Boolean doWork() {
300
								CollectionEntry entry = session.getPersistenceContext().getCollectionEntry( AbstractPersistentCollection.this );
301
								CollectionPersister persister = entry.getLoadedPersister();
302
								if ( persister.isExtraLazy() ) {
303
									if ( hasQueuedOperations() ) {
304
										session.flush();
305
									}
306
									return persister.indexExists( entry.getLoadedKey(), index, session );
307
								}
308
								else {
309
									read();
310
								}
311
								return null;
312
							}
313
						}
314
						);
315
				if ( extraLazyExistenceCheck != null ) {
316
					return extraLazyExistenceCheck;
317
				}
318
			}
319
		}
320
		return null;
321
	}
322

    
323
	protected Boolean readElementExistence(final Object element) {
324
		if ( !initialized ) {
325
			// In remoting we are sure that session is null
326
			// both when using property paths and switching off conversations
327
			if(session == null && remoting) {
328
				log.info("--> readElementExistence, of " + getRole() + " with key " + getKey());
329
				read();
330

    
331
			} else {
332
				Boolean extraLazyExistenceCheck = withTemporarySessionIfNeeded(
333
						new LazyInitializationWork<Boolean>() {
334
							@Override
335
							public Boolean doWork() {
336
								CollectionEntry entry = session.getPersistenceContext().getCollectionEntry( AbstractPersistentCollection.this );
337
								CollectionPersister persister = entry.getLoadedPersister();
338
								if ( persister.isExtraLazy() ) {
339
									if ( hasQueuedOperations() ) {
340
										session.flush();
341
									}
342
									return persister.elementExists( entry.getLoadedKey(), element, session );
343
								}
344
								else {
345
									read();
346
								}
347
								return null;
348
							}
349
						}
350
						);
351
				if ( extraLazyExistenceCheck != null ) {
352
					return extraLazyExistenceCheck;
353
				}
354
			}
355
		}
356
		return null;
357
	}
358

    
359
	protected static final Object UNKNOWN = new MarkerObject( "UNKNOWN" );
360

    
361
	protected Object readElementByIndex(final Object index) {
362
		if ( !initialized ) {
363
			// In remoting we are sure that session is null
364
			// both when using property paths and switching off conversations
365
			if(session == null && remoting) {
366
				log.info("--> readElementByIndex, of " + getRole() + " with key " + getKey());
367
				read();
368

    
369
			} else {
370
				class ExtraLazyElementByIndexReader implements LazyInitializationWork {
371
					private boolean isExtraLazy;
372
					private Object element;
373

    
374
					@Override
375
					public Object doWork() {
376
						CollectionEntry entry = session.getPersistenceContext().getCollectionEntry( AbstractPersistentCollection.this );
377
						CollectionPersister persister = entry.getLoadedPersister();
378
						isExtraLazy = persister.isExtraLazy();
379
						if ( isExtraLazy ) {
380
							if ( hasQueuedOperations() ) {
381
								session.flush();
382
							}
383
							element = persister.getElementByIndex( entry.getLoadedKey(), index, session, owner );
384
						}
385
						else {
386
							read();
387
						}
388
						return null;
389
					}
390
				}
391

    
392
				ExtraLazyElementByIndexReader reader = new ExtraLazyElementByIndexReader();
393
				//noinspection unchecked
394
				withTemporarySessionIfNeeded( reader );
395
				if ( reader.isExtraLazy ) {
396
					return reader.element;
397
				}
398
			}
399
		}
400
		return UNKNOWN;
401

    
402
	}
403

    
404
	protected int getCachedSize() {
405
		return cachedSize;
406
	}
407

    
408
	private boolean isConnectedToSession() {
409
		return session != null &&
410
				session.isOpen() &&
411
				session.getPersistenceContext().containsCollection( this );
412
	}
413

    
414
	/**
415
	 * Called by any writer method of the collection interface
416
	 */
417
	protected final void write() {
418
		initialize( true );
419
		dirty();
420
	}
421

    
422
	/**
423
	 * Is this collection in a state that would allow us to
424
	 * "queue" operations?
425
	 */
426
	@SuppressWarnings({"JavaDoc"})
427
	protected boolean isOperationQueueEnabled() {
428
		return !initialized &&
429
				isConnectedToSession() &&
430
				isInverseCollection();
431
	}
432

    
433
	/**
434
	 * Is this collection in a state that would allow us to
435
	 * "queue" puts? This is a special case, because of orphan
436
	 * delete.
437
	 */
438
	@SuppressWarnings({"JavaDoc"})
439
	protected boolean isPutQueueEnabled() {
440
		return !initialized &&
441
				isConnectedToSession() &&
442
				isInverseOneToManyOrNoOrphanDelete();
443
	}
444

    
445
	/**
446
	 * Is this collection in a state that would allow us to
447
	 * "queue" clear? This is a special case, because of orphan
448
	 * delete.
449
	 */
450
	@SuppressWarnings({"JavaDoc"})
451
	protected boolean isClearQueueEnabled() {
452
		return !initialized &&
453
				isConnectedToSession() &&
454
				isInverseCollectionNoOrphanDelete();
455
	}
456

    
457
	/**
458
	 * Is this the "inverse" end of a bidirectional association?
459
	 */
460
	@SuppressWarnings({"JavaDoc"})
461
	private boolean isInverseCollection() {
462
		CollectionEntry ce = session.getPersistenceContext().getCollectionEntry( this );
463
		return ce != null && ce.getLoadedPersister().isInverse();
464
	}
465

    
466
	/**
467
	 * Is this the "inverse" end of a bidirectional association with
468
	 * no orphan delete enabled?
469
	 */
470
	@SuppressWarnings({"JavaDoc"})
471
	private boolean isInverseCollectionNoOrphanDelete() {
472
		CollectionEntry ce = session.getPersistenceContext().getCollectionEntry( this );
473
		return ce != null &&
474
				ce.getLoadedPersister().isInverse() &&
475
				!ce.getLoadedPersister().hasOrphanDelete();
476
	}
477

    
478
	/**
479
	 * Is this the "inverse" end of a bidirectional one-to-many, or
480
	 * of a collection with no orphan delete?
481
	 */
482
	@SuppressWarnings({"JavaDoc"})
483
	private boolean isInverseOneToManyOrNoOrphanDelete() {
484
		CollectionEntry ce = session.getPersistenceContext().getCollectionEntry( this );
485
		return ce != null && ce.getLoadedPersister().isInverse() && (
486
				ce.getLoadedPersister().isOneToMany() ||
487
						!ce.getLoadedPersister().hasOrphanDelete()
488
		);
489
	}
490

    
491
	/**
492
	 * Queue an addition
493
	 */
494
	@SuppressWarnings({"JavaDoc"})
495
	protected final void queueOperation(DelayedOperation operation) {
496
		if ( operationQueue == null ) {
497
			operationQueue = new ArrayList<DelayedOperation>( 10 );
498
		}
499
		operationQueue.add( operation );
500
		dirty = true; //needed so that we remove this collection from the second-level cache
501
	}
502

    
503
	/**
504
	 * After reading all existing elements from the database,
505
	 * add the queued elements to the underlying collection.
506
	 */
507
	protected final void performQueuedOperations() {
508
		for ( DelayedOperation operation : operationQueue ) {
509
			operation.operate();
510
		}
511
	}
512

    
513
	/**
514
	 * After flushing, re-init snapshot state.
515
	 */
516
	@Override
517
    public void setSnapshot(Serializable key, String role, Serializable snapshot) {
518
		this.key = key;
519
		this.role = role;
520
		this.storedSnapshot = snapshot;
521
	}
522

    
523
	/**
524
	 * After flushing, clear any "queued" additions, since the
525
	 * database state is now synchronized with the memory state.
526
	 */
527
	@Override
528
    public void postAction() {
529
		operationQueue = null;
530
		cachedSize = -1;
531
		clearDirty();
532
	}
533

    
534
	/**
535
	 * Not called by Hibernate, but used by non-JDK serialization,
536
	 * eg. SOAP libraries.
537
	 */
538
	public AbstractPersistentCollection() {
539
	}
540

    
541
	protected AbstractPersistentCollection(SessionImplementor session) {
542
		this.session = session;
543
	}
544

    
545
	/**
546
	 * return the user-visible collection (or array) instance
547
	 */
548
	@Override
549
    public Object getValue() {
550
		return this;
551
	}
552

    
553
	/**
554
	 * Called just before reading any rows from the JDBC result set
555
	 */
556
	@Override
557
    public void beginRead() {
558
		// override on some subclasses
559
		initializing = true;
560
	}
561

    
562
	/**
563
	 * Called after reading all rows from the JDBC result set
564
	 */
565
	@Override
566
    public boolean endRead() {
567
		//override on some subclasses
568
		return afterInitialize();
569
	}
570

    
571
	@Override
572
    public boolean afterInitialize() {
573
		setInitialized();
574
		//do this bit after setting initialized to true or it will recurse
575
		if ( operationQueue != null ) {
576
			performQueuedOperations();
577
			operationQueue = null;
578
			cachedSize = -1;
579
			return false;
580
		}
581
		else {
582
			return true;
583
		}
584
	}
585

    
586
	/**
587
	 * Initialize the collection, if possible, wrapping any exceptions
588
	 * in a runtime exception
589
	 *
590
	 * @param writing currently obsolete
591
	 *
592
	 * @throws LazyInitializationException if we cannot initialize
593
	 */
594
	protected final void initialize(final boolean writing) {
595
		// In remoting we are sure that session is null
596
		// both when using property paths and switching off conversations
597
		if(session == null && remoting) {
598
			remoteInitialize();
599
		}
600

    
601
		if ( initialized ) {
602
			return;
603
		}
604

    
605

    
606
		withTemporarySessionIfNeeded(
607
				new LazyInitializationWork<Object>() {
608
					@Override
609
					public Object doWork() {
610
						session.initializeCollection( AbstractPersistentCollection.this, writing );
611
						return null;
612
					}
613
				}
614
		);
615
	}
616

    
617
	private void throwLazyInitializationExceptionIfNotConnected() {
618
		if ( !isConnectedToSession() ) {
619
			throwLazyInitializationException( "no session or session was closed" );
620
		}
621
		if ( !session.isConnected() ) {
622
			throwLazyInitializationException( "session is disconnected" );
623
		}
624
	}
625

    
626
	private void throwLazyInitializationException(String message) {
627
		throw new LazyInitializationException(
628
				"failed to lazily initialize a collection" +
629
						(role == null ? "" : " of role: " + role) +
630
						", " + message
631
		);
632
	}
633

    
634
	protected final void setInitialized() {
635
		this.initializing = false;
636
		this.initialized = true;
637
	}
638

    
639
	protected final void setDirectlyAccessible(boolean directlyAccessible) {
640
		this.directlyAccessible = directlyAccessible;
641
	}
642

    
643
	/**
644
	 * Could the application possibly have a direct reference to
645
	 * the underlying collection implementation?
646
	 */
647
	@Override
648
    public boolean isDirectlyAccessible() {
649
		return directlyAccessible;
650
	}
651

    
652
	/**
653
	 * Disassociate this collection from the given session.
654
	 *
655
	 * @return true if this was currently associated with the given session
656
	 */
657
	@Override
658
    public final boolean unsetSession(SessionImplementor currentSession) {
659
		prepareForPossibleSpecialSpecjInitialization();
660
		if ( currentSession == this.session ) {
661
			this.session = null;
662
			return true;
663
		}
664
		else {
665
			return false;
666
		}
667
	}
668

    
669
	protected void prepareForPossibleSpecialSpecjInitialization() {
670
		if ( session != null ) {
671
			specjLazyLoad = session.getFactory().getSettings().isInitializeLazyStateOutsideTransactionsEnabled();
672

    
673
			if ( specjLazyLoad && sessionFactoryUuid == null ) {
674
				try {
675
					sessionFactoryUuid = (String) session.getFactory().getReference().get( "uuid" ).getContent();
676
				}
677
				catch (NamingException e) {
678
					//not much we can do if this fails...
679
				}
680
			}
681
		}
682
	}
683

    
684

    
685
	/**
686
	 * Associate the collection with the given session.
687
	 *
688
	 * @return false if the collection was already associated with the session
689
	 *
690
	 * @throws HibernateException if the collection was already associated
691
	 * with another open session
692
	 */
693
	@Override
694
    public final boolean setCurrentSession(SessionImplementor session) throws HibernateException {
695
		if ( session == this.session ) {
696
			return false;
697
		}
698
		else {
699
			if ( isConnectedToSession() ) {
700
				CollectionEntry ce = session.getPersistenceContext().getCollectionEntry( this );
701
				if ( ce == null ) {
702
					throw new HibernateException(
703
							"Illegal attempt to associate a collection with two open sessions"
704
					);
705
				}
706
				else {
707
					throw new HibernateException(
708
							"Illegal attempt to associate a collection with two open sessions: " +
709
									MessageHelper.collectionInfoString(
710
											ce.getLoadedPersister(), this,
711
											ce.getLoadedKey(), session
712
									)
713
					);
714
				}
715
			}
716
			else {
717
				this.session = session;
718
				return true;
719
			}
720
		}
721
	}
722

    
723
	/**
724
	 * Do we need to completely recreate this collection when it changes?
725
	 */
726
	@Override
727
    public boolean needsRecreate(CollectionPersister persister) {
728
		return false;
729
	}
730

    
731
	/**
732
	 * To be called internally by the session, forcing
733
	 * immediate initialization.
734
	 */
735
	@Override
736
    public final void forceInitialization() throws HibernateException {
737
		if ( !initialized ) {
738
			if ( initializing ) {
739
				throw new AssertionFailure( "force initialize loading collection" );
740
			}
741
			if ( session == null ) {
742
				throw new HibernateException( "collection is not associated with any session" );
743
			}
744
			if ( !session.isConnected() ) {
745
				throw new HibernateException( "disconnected session" );
746
			}
747
			session.initializeCollection( this, false );
748
		}
749
	}
750

    
751

    
752
	/**
753
	 * Get the current snapshot from the session
754
	 */
755
	@SuppressWarnings({"JavaDoc"})
756
	protected final Serializable getSnapshot() {
757
		return session.getPersistenceContext().getSnapshot( this );
758
	}
759

    
760
	/**
761
	 * Is this instance initialized?
762
	 */
763
	@Override
764
    public final boolean wasInitialized() {
765
		return initialized;
766
	}
767

    
768
	@Override
769
    public boolean isRowUpdatePossible() {
770
		return true;
771
	}
772

    
773
	/**
774
	 * Does this instance have any "queued" additions?
775
	 */
776
	@Override
777
    public final boolean hasQueuedOperations() {
778
		return operationQueue != null;
779
	}
780

    
781
	/**
782
	 * Iterate the "queued" additions
783
	 */
784
	@Override
785
    public final Iterator queuedAdditionIterator() {
786
		if ( hasQueuedOperations() ) {
787
			return new Iterator() {
788
				int i = 0;
789

    
790
				@Override
791
                public Object next() {
792
					return operationQueue.get( i++ ).getAddedInstance();
793
				}
794

    
795
				@Override
796
                public boolean hasNext() {
797
					return i < operationQueue.size();
798
				}
799

    
800
				@Override
801
                public void remove() {
802
					throw new UnsupportedOperationException();
803
				}
804
			};
805
		}
806
		else {
807
			return EmptyIterator.INSTANCE;
808
		}
809
	}
810

    
811
	/**
812
	 * Iterate the "queued" additions
813
	 */
814
	@Override
815
    @SuppressWarnings({"unchecked"})
816
	public final Collection getQueuedOrphans(String entityName) {
817
		if ( hasQueuedOperations() ) {
818
			Collection additions = new ArrayList( operationQueue.size() );
819
			Collection removals = new ArrayList( operationQueue.size() );
820
			for ( DelayedOperation operation : operationQueue ) {
821
				additions.add( operation.getAddedInstance() );
822
				removals.add( operation.getOrphan() );
823
			}
824
			return getOrphans( removals, additions, entityName, session );
825
		}
826
		else {
827
			return Collections.EMPTY_LIST;
828
		}
829
	}
830

    
831
	/**
832
	 * Called before inserting rows, to ensure that any surrogate keys
833
	 * are fully generated
834
	 */
835
	@Override
836
    public void preInsert(CollectionPersister persister) throws HibernateException {
837
	}
838

    
839
	/**
840
	 * Called after inserting a row, to fetch the natively generated id
841
	 */
842
	@Override
843
    public void afterRowInsert(CollectionPersister persister, Object entry, int i) throws HibernateException {
844
	}
845

    
846
	/**
847
	 * get all "orphaned" elements
848
	 */
849
	@Override
850
    public abstract Collection getOrphans(Serializable snapshot, String entityName) throws HibernateException;
851

    
852
	/**
853
	 * Get the current session
854
	 */
855
	@SuppressWarnings({"JavaDoc"})
856
	public final SessionImplementor getSession() {
857
		return session;
858
	}
859

    
860
	protected final class IteratorProxy implements Iterator {
861
		protected final Iterator itr;
862

    
863
		public IteratorProxy(Iterator itr) {
864
			this.itr = itr;
865
		}
866

    
867
		@Override
868
        public boolean hasNext() {
869
			return itr.hasNext();
870
		}
871

    
872
		@Override
873
        public Object next() {
874
			return itr.next();
875
		}
876

    
877
		@Override
878
        public void remove() {
879
			write();
880
			itr.remove();
881
		}
882

    
883
	}
884

    
885
	protected final class ListIteratorProxy implements ListIterator {
886
		protected final ListIterator itr;
887

    
888
		public ListIteratorProxy(ListIterator itr) {
889
			this.itr = itr;
890
		}
891

    
892
		@Override
893
        @SuppressWarnings({"unchecked"})
894
		public void add(Object o) {
895
			write();
896
			itr.add( o );
897
		}
898

    
899
		@Override
900
        public boolean hasNext() {
901
			return itr.hasNext();
902
		}
903

    
904
		@Override
905
        public boolean hasPrevious() {
906
			return itr.hasPrevious();
907
		}
908

    
909
		@Override
910
        public Object next() {
911
			return itr.next();
912
		}
913

    
914
		@Override
915
        public int nextIndex() {
916
			return itr.nextIndex();
917
		}
918

    
919
		@Override
920
        public Object previous() {
921
			return itr.previous();
922
		}
923

    
924
		@Override
925
        public int previousIndex() {
926
			return itr.previousIndex();
927
		}
928

    
929
		@Override
930
        public void remove() {
931
			write();
932
			itr.remove();
933
		}
934

    
935
		@Override
936
        @SuppressWarnings({"unchecked"})
937
		public void set(Object o) {
938
			write();
939
			itr.set( o );
940
		}
941

    
942
	}
943

    
944
	protected class SetProxy implements java.util.Set {
945
		protected final Collection set;
946

    
947
		public SetProxy(Collection set) {
948
			this.set = set;
949
		}
950

    
951
		@Override
952
        @SuppressWarnings({"unchecked"})
953
		public boolean add(Object o) {
954
			write();
955
			return set.add( o );
956
		}
957

    
958
		@Override
959
        @SuppressWarnings({"unchecked"})
960
		public boolean addAll(Collection c) {
961
			write();
962
			return set.addAll( c );
963
		}
964

    
965
		@Override
966
        public void clear() {
967
			write();
968
			set.clear();
969
		}
970

    
971
		@Override
972
        public boolean contains(Object o) {
973
			return set.contains( o );
974
		}
975

    
976
		@Override
977
        public boolean containsAll(Collection c) {
978
			return set.containsAll( c );
979
		}
980

    
981
		@Override
982
        public boolean isEmpty() {
983
			return set.isEmpty();
984
		}
985

    
986
		@Override
987
        public Iterator iterator() {
988
			return new IteratorProxy( set.iterator() );
989
		}
990

    
991
		@Override
992
        public boolean remove(Object o) {
993
			write();
994
			return set.remove( o );
995
		}
996

    
997
		@Override
998
        public boolean removeAll(Collection c) {
999
			write();
1000
			return set.removeAll( c );
1001
		}
1002

    
1003
		@Override
1004
        public boolean retainAll(Collection c) {
1005
			write();
1006
			return set.retainAll( c );
1007
		}
1008

    
1009
		@Override
1010
        public int size() {
1011
			return set.size();
1012
		}
1013

    
1014
		@Override
1015
        public Object[] toArray() {
1016
			return set.toArray();
1017
		}
1018

    
1019
		@Override
1020
        @SuppressWarnings({"unchecked"})
1021
		public Object[] toArray(Object[] array) {
1022
			return set.toArray( array );
1023
		}
1024

    
1025
	}
1026

    
1027
	protected final class ListProxy implements java.util.List {
1028
		protected final List list;
1029

    
1030
		public ListProxy(List list) {
1031
			this.list = list;
1032
		}
1033

    
1034
		@Override
1035
		@SuppressWarnings({"unchecked"})
1036
		public void add(int index, Object value) {
1037
			write();
1038
			list.add( index, value );
1039
		}
1040

    
1041
		@Override
1042
		@SuppressWarnings({"unchecked"})
1043
		public boolean add(Object o) {
1044
			write();
1045
			return list.add( o );
1046
		}
1047

    
1048
		@Override
1049
		@SuppressWarnings({"unchecked"})
1050
		public boolean addAll(Collection c) {
1051
			write();
1052
			return list.addAll( c );
1053
		}
1054

    
1055
		@Override
1056
		@SuppressWarnings({"unchecked"})
1057
		public boolean addAll(int i, Collection c) {
1058
			write();
1059
			return list.addAll( i, c );
1060
		}
1061

    
1062
		@Override
1063
		public void clear() {
1064
			write();
1065
			list.clear();
1066
		}
1067

    
1068
		@Override
1069
		public boolean contains(Object o) {
1070
			return list.contains( o );
1071
		}
1072

    
1073
		@Override
1074
		public boolean containsAll(Collection c) {
1075
			return list.containsAll( c );
1076
		}
1077

    
1078
		@Override
1079
		public Object get(int i) {
1080
			return list.get( i );
1081
		}
1082

    
1083
		@Override
1084
		public int indexOf(Object o) {
1085
			return list.indexOf( o );
1086
		}
1087

    
1088
		@Override
1089
		public boolean isEmpty() {
1090
			return list.isEmpty();
1091
		}
1092

    
1093
		@Override
1094
		public Iterator iterator() {
1095
			return new IteratorProxy( list.iterator() );
1096
		}
1097

    
1098
		@Override
1099
		public int lastIndexOf(Object o) {
1100
			return list.lastIndexOf( o );
1101
		}
1102

    
1103
		@Override
1104
		public ListIterator listIterator() {
1105
			return new ListIteratorProxy( list.listIterator() );
1106
		}
1107

    
1108
		@Override
1109
		public ListIterator listIterator(int i) {
1110
			return new ListIteratorProxy( list.listIterator( i ) );
1111
		}
1112

    
1113
		@Override
1114
		public Object remove(int i) {
1115
			write();
1116
			return list.remove( i );
1117
		}
1118

    
1119
		@Override
1120
		public boolean remove(Object o) {
1121
			write();
1122
			return list.remove( o );
1123
		}
1124

    
1125
		@Override
1126
		public boolean removeAll(Collection c) {
1127
			write();
1128
			return list.removeAll( c );
1129
		}
1130

    
1131
		@Override
1132
		public boolean retainAll(Collection c) {
1133
			write();
1134
			return list.retainAll( c );
1135
		}
1136

    
1137
		@Override
1138
		@SuppressWarnings({"unchecked"})
1139
		public Object set(int i, Object o) {
1140
			write();
1141
			return list.set( i, o );
1142
		}
1143

    
1144
		@Override
1145
		public int size() {
1146
			return list.size();
1147
		}
1148

    
1149
		@Override
1150
		public List subList(int i, int j) {
1151
			return list.subList( i, j );
1152
		}
1153

    
1154
		@Override
1155
		public Object[] toArray() {
1156
			return list.toArray();
1157
		}
1158

    
1159
		@Override
1160
		@SuppressWarnings({"unchecked"})
1161
		public Object[] toArray(Object[] array) {
1162
			return list.toArray( array );
1163
		}
1164

    
1165
	}
1166

    
1167
	/**
1168
	 * Contract for operations which are part of a collection's operation queue.
1169
	 */
1170
	protected interface DelayedOperation {
1171
		public void operate();
1172

    
1173
		public Object getAddedInstance();
1174

    
1175
		public Object getOrphan();
1176
	}
1177

    
1178
	/**
1179
	 * Given a collection of entity instances that used to
1180
	 * belong to the collection, and a collection of instances
1181
	 * that currently belong, return a collection of orphans
1182
	 */
1183
	@SuppressWarnings({"JavaDoc", "unchecked"})
1184
	protected static Collection getOrphans(
1185
			Collection oldElements,
1186
			Collection currentElements,
1187
			String entityName,
1188
			SessionImplementor session) throws HibernateException {
1189

    
1190
		// short-circuit(s)
1191
		if ( currentElements.size() == 0 ) {
1192
			return oldElements; // no new elements, the old list contains only Orphans
1193
		}
1194
		if ( oldElements.size() == 0 ) {
1195
			return oldElements; // no old elements, so no Orphans neither
1196
		}
1197

    
1198
		final EntityPersister entityPersister = session.getFactory().getEntityPersister( entityName );
1199
		final Type idType = entityPersister.getIdentifierType();
1200

    
1201
		// create the collection holding the Orphans
1202
		Collection res = new ArrayList();
1203

    
1204
		// collect EntityIdentifier(s) of the *current* elements - add them into a HashSet for fast access
1205
		java.util.Set currentIds = new HashSet();
1206
		java.util.Set currentSaving = new IdentitySet();
1207
		for ( Object current : currentElements ) {
1208
			if ( current != null && ForeignKeys.isNotTransient( entityName, current, null, session ) ) {
1209
				EntityEntry ee = session.getPersistenceContext().getEntry( current );
1210
				if ( ee != null && ee.getStatus() == Status.SAVING ) {
1211
					currentSaving.add( current );
1212
				}
1213
				else {
1214
					Serializable currentId = ForeignKeys.getEntityIdentifierIfNotUnsaved(
1215
							entityName,
1216
							current,
1217
							session
1218
					);
1219
					currentIds.add( new TypedValue( idType, currentId, entityPersister.getEntityMode() ) );
1220
				}
1221
			}
1222
		}
1223

    
1224
		// iterate over the *old* list
1225
		for ( Object old : oldElements ) {
1226
			if ( !currentSaving.contains( old ) ) {
1227
				Serializable oldId = ForeignKeys.getEntityIdentifierIfNotUnsaved( entityName, old, session );
1228
				if ( !currentIds.contains( new TypedValue( idType, oldId, entityPersister.getEntityMode() ) ) ) {
1229
					res.add( old );
1230
				}
1231
			}
1232
		}
1233

    
1234
		return res;
1235
	}
1236

    
1237
	public static void identityRemove(
1238
			Collection list,
1239
			Object object,
1240
			String entityName,
1241
			SessionImplementor session) throws HibernateException {
1242

    
1243
		if ( object != null && ForeignKeys.isNotTransient( entityName, object, null, session ) ) {
1244
			final EntityPersister entityPersister = session.getFactory().getEntityPersister( entityName );
1245
			Type idType = entityPersister.getIdentifierType();
1246

    
1247
			Serializable idOfCurrent = ForeignKeys.getEntityIdentifierIfNotUnsaved( entityName, object, session );
1248
			Iterator itr = list.iterator();
1249
			while ( itr.hasNext() ) {
1250
				Serializable idOfOld = ForeignKeys.getEntityIdentifierIfNotUnsaved( entityName, itr.next(), session );
1251
				if ( idType.isEqual( idOfCurrent, idOfOld, session.getFactory() ) ) {
1252
					itr.remove();
1253
					break;
1254
				}
1255
			}
1256

    
1257
		}
1258
	}
1259

    
1260
	@Override
1261
    public Object getIdentifier(Object entry, int i) {
1262
		throw new UnsupportedOperationException();
1263
	}
1264

    
1265
	@Override
1266
    public Object getOwner() {
1267
		return owner;
1268
	}
1269

    
1270
	@Override
1271
    public void setOwner(Object owner) {
1272
		this.owner = owner;
1273
	}
1274

    
1275
	/** ------ Below is section of code which makes remote service calls ----- */
1276
	// The affected methods are :
1277
	// initialize(final boolean writing)
1278
	// readSize()
1279
	// readIndexExistence(final Object index)
1280
	// readElementExistence(final Object element)
1281
	// readElementByIndex(final Object index)
1282

    
1283
	private static CdmApplicationRemoteConfiguration configuration;
1284
	private static boolean remoting = false;
1285

    
1286
	public static void setConfiguration(CdmApplicationRemoteConfiguration conf) {
1287
	    remoting = true;
1288
		configuration = conf;
1289
	}
1290

    
1291

    
1292
	private void remoteInitialize() {
1293

    
1294
		if (getOwner() != null && !initialized) {
1295

    
1296
			try {
1297
				String role = getRole();
1298
				String fieldName = role.substring(role.lastIndexOf(".") + 1);
1299
				log.info("--> Remote Lazy Initializing Collection " + getRole() + " , key : " + getKey() + " , field : " + fieldName);
1300
				Object owner = getOwner();
1301

    
1302
				if(configuration == null) {
1303
					throw new HibernateException("CdmApplicationRemoteConfiguration not initialized (null)");
1304
				}
1305
				ICachedCommonService cachedCommonService = configuration.getCachedCommonService();
1306
				if(cachedCommonService == null) {
1307
					throw new HibernateException("commonService not initialized (null)");
1308
				}
1309

    
1310
				PersistentCollection col = cachedCommonService.initializeCollection(this);
1311
				afterInitialize();
1312

    
1313
				Class<?> clazz = getClass();
1314
				if (clazz != null) {
1315
					CollectionField cf = cachedCommonService.getCollectionField(col);
1316
					cachedCommonService.updatePersistentCollection(cf);
1317
					Field field = clazz.getDeclaredField(cf.getType().toString());
1318
					field.setAccessible(true);
1319
					field.set(this, cf.getCollection());
1320
				}
1321
			} catch (Exception ex) {
1322
				throw new CdmEagerLoadingException(ex);
1323
			}
1324
		}
1325
	}
1326

    
1327

    
1328

    
1329
}
1330

    
    (1-1/1)