Project

General

Profile

Download (34.8 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.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

    
    (1-1/1)