Project

General

Profile

Download (33.9 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.cdm.model.common.CdmBase;
64
import eu.etaxonomy.taxeditor.remoting.CdmEagerLoadingException;
65
import eu.etaxonomy.taxeditor.remoting.cache.ProxyUtils;
66
import eu.etaxonomy.taxeditor.service.ICachedCommonService;
67

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
236
		if ( isTempSession ) {
237
			// TODO: On the next major release, add an
238
			// 'isJTA' or 'getTransactionFactory' method to Session.
239
			/*isJTA = session.getTransactionCoordinator()
240
					.getTransactionContext().getTransactionEnvironment()
241
					.getTransactionFactory()
242
					.compatibleWithJtaSynchronization();*/
243
			isJTA = session.getTransactionCoordinator().getTransactionCoordinatorBuilder().isJta();
244
			if ( !isJTA ) {
245
				// Explicitly handle the transactions only if we're not in
246
				// a JTA environment.  A lazy loading temporary session can
247
				// be created even if a current session and transaction are
248
				// open (ex: session.clear() was used).  We must prevent
249
				// multiple transactions.
250
				( ( Session) session ).beginTransaction();
251
			}
252

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

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

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

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

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

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

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

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

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

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

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

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

    
403
	}
404

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
587
	/**
588
	 * Initialize the collection, if possible, wrapping any exceptions
589
	 * in a runtime exception
590
	 *
591
	 * @param writing currently obsolete
592
	 *
593
	 * @throws LazyInitializationException if we cannot initialize
594
	 */
595
	protected final void initialize(final boolean writing) {
596
	    if ( initialized ) {
597
	        return;
598
	    }
599

    
600
	    // In remoting we are sure that session is null
601
	    // both when using property paths and switching off conversations
602
	    if(session == null && remoting) {
603
	        remoteInitialize();
604
	    } else {
605
	        withTemporarySessionIfNeeded(
606
	                new LazyInitializationWork<Object>() {
607
	                    @Override
608
	                    public Object doWork() {
609
	                        session.initializeCollection( AbstractPersistentCollection.this, writing );
610
	                        return null;
611
	                    }
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() + " , owner : " + getOwner().getClass() + "/" + getKey() + " , field : " + fieldName);
1300
				Object owner = getOwner();
1301
				CdmBase cdmBase;
1302
				if(owner instanceof CdmBase) {
1303
				    cdmBase = (CdmBase)owner;
1304
				} else {
1305
				    throw new HibernateException("Owner of persistent collection is not a cdm entity");
1306
				}
1307
				if(configuration == null) {
1308
					throw new HibernateException("CdmApplicationRemoteConfiguration not initialized (null)");
1309
				}
1310
				ICachedCommonService cachedCommonService = configuration.getCachedCommonService();
1311
				if(cachedCommonService == null) {
1312
					throw new HibernateException("commonService not initialized (null)");
1313
				}
1314

    
1315
				//Object obj = ProxyUtils.deproxy(cachedCommonService.initializeCollection(this));
1316
				Object obj = ProxyUtils.deproxy(cachedCommonService.initializeCollection(cdmBase.getUuid(), fieldName));
1317
				if(ProxyUtils.isProxy(obj)) {
1318
				    throw new HibernateException("Persistent Collection initialized but is still a proxy");
1319
				}
1320
				afterInitialize();
1321

    
1322
				Class<?> clazz = getClass();
1323
				if (clazz != null) {
1324
					//CollectionField cf = cachedCommonService.getCollectionField(col);
1325
					//cachedCommonService.updatePersistentCollection(cf);
1326
				    Object collectionType = ProxyUtils.getCollectionType(obj);
1327
					Field field = clazz.getDeclaredField(collectionType.toString());
1328
					field.setAccessible(true);
1329
					field.set(this, obj);
1330
					ProxyUtils.setRoleValueInOwner(owner, role, obj);
1331

    
1332
				}
1333
			} catch (Exception ex) {
1334
				throw new CdmEagerLoadingException(ex);
1335
			}
1336
		}
1337
	}
1338

    
1339

    
1340

    
1341
}
1342

    
    (1-1/1)