Project

General

Profile

Download (33.3 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.remoting.cache.ProxyUtils;
65
import eu.etaxonomy.taxeditor.service.ICachedCommonService;
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
	    if ( initialized ) {
596
	        return;
597
	    }
598

    
599
	    // In remoting we are sure that session is null
600
	    // both when using property paths and switching off conversations
601
	    if(session == null && remoting) {
602
	        remoteInitialize();
603
	    } else {
604
	        withTemporarySessionIfNeeded(
605
	                new LazyInitializationWork<Object>() {
606
	                    @Override
607
	                    public Object doWork() {
608
	                        session.initializeCollection( AbstractPersistentCollection.this, writing );
609
	                        return null;
610
	                    }
611
	                }
612
	                );
613
	    }
614
	}
615

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

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

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

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

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

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

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

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

    
683

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

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

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

    
750

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
882
	}
883

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

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

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

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

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

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

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

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

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

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

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

    
941
	}
942

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1024
	}
1025

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1164
	}
1165

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

    
1172
		public Object getAddedInstance();
1173

    
1174
		public Object getOrphan();
1175
	}
1176

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

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

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

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

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

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

    
1233
		return res;
1234
	}
1235

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

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

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

    
1256
		}
1257
	}
1258

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

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

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

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

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

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

    
1290

    
1291
	private void remoteInitialize() {
1292

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

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

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

    
1309
				Object obj = ProxyUtils.deproxy(cachedCommonService.initializeCollection(this));
1310
				afterInitialize();
1311

    
1312
				Class<?> clazz = getClass();
1313
				if (clazz != null) {
1314
					//CollectionField cf = cachedCommonService.getCollectionField(col);
1315
					//cachedCommonService.updatePersistentCollection(cf);
1316
				    Object collectionType = ProxyUtils.getCollectionType(obj);
1317
					Field field = clazz.getDeclaredField(collectionType.toString());
1318
					field.setAccessible(true);
1319
					field.set(this, obj);
1320
					ProxyUtils.setRoleValueInOwner(owner, role, obj);
1321

    
1322
				}
1323
			} catch (Exception ex) {
1324
				throw new CdmEagerLoadingException(ex);
1325
			}
1326
		}
1327
	}
1328

    
1329

    
1330

    
1331
}
1332

    
    (1-1/1)