Project

General

Profile

Download (36.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.HashSet;
34
import java.util.Iterator;
35
import java.util.List;
36
import java.util.ListIterator;
37
import java.util.Map;
38

    
39
import javax.naming.NamingException;
40

    
41
import org.hibernate.AssertionFailure;
42
import org.hibernate.HibernateException;
43
import org.hibernate.LazyInitializationException;
44
import org.hibernate.Session;
45
import org.hibernate.collection.internal.AbstractPersistentCollection.DelayedOperation;
46
import org.hibernate.collection.internal.AbstractPersistentCollection.IteratorProxy;
47
import org.hibernate.collection.internal.AbstractPersistentCollection.ListIteratorProxy;
48
import org.hibernate.collection.internal.AbstractPersistentCollection.ValueDelayedOperation;
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.IntegerType;
65
import org.hibernate.type.LongType;
66
import org.hibernate.type.PostgresUUIDType;
67
import org.hibernate.type.StringType;
68
import org.hibernate.type.Type;
69
import org.hibernate.type.UUIDBinaryType;
70
import org.hibernate.type.UUIDCharType;
71
import org.jboss.logging.Logger;
72

    
73
import eu.etaxonomy.cdm.api.application.CdmApplicationRemoteConfiguration;
74
import eu.etaxonomy.cdm.cache.ProxyUtils;
75
import eu.etaxonomy.cdm.model.common.CdmBase;
76
import eu.etaxonomy.taxeditor.remoting.CdmEagerLoadingException;
77
import eu.etaxonomy.taxeditor.service.ICachedCommonService;
78

    
79
/**
80
 * Base class implementing {@link org.hibernate.collection.spi.PersistentCollection}
81
 *
82
 * This is an extended copy of the original class from hibernate. It has been extended to
83
 * allow making remote service calls to spring httpinvoker services (see section at the bottom
84
 * of this class).
85
 *
86
 * @author Gavin King
87
 * @author Cherian Mathew
88
 */
89
public abstract class AbstractPersistentCollection implements Serializable, PersistentCollection {
90
	private static final Logger log = Logger.getLogger( AbstractPersistentCollection.class );
91

    
92
	/**
93
	 * <b>IMPORTANT:</b><br>
94
	 * This serialVersionUID must be kept in sync with the serialVersionUID which is generated
95
	 * on the fly for serialized AbstractPersistentCollection objects coming from the httpInvoker
96
	 * service.
97
	 * This is most probably necessary after updating hibernate to a newer version. In any case
98
	 * it the need for updating this <code>serialVersionUID</code> becomes obvious when the attempt
99
	 * to connect to the server side fails with an  <code>InvalidClassException</code>:
100
	 *
101
	 * <pre>
102
	 * java.io.InvalidClassException: org.hibernate.collection.internal.AbstractPersistentCollection;
103
	 * local class incompatible:
104
	 * stream classdesc serialVersionUID = 2742261122392386159,
105
	 * local class serialVersionUID = -7238232378593030571
106
	 * </pre>
107
	 * The correct <code>serialVersionUID</code> is the <code>stream classdesc serialVersionUID</code>
108
	 * from the error message.
109
	 */
110
	private static final long serialVersionUID = 7094296207968006972L;
111

    
112
	private transient SessionImplementor session;
113
	private boolean initialized;
114
	private transient List<DelayedOperation> operationQueue;
115
	private transient boolean directlyAccessible;
116
	private transient boolean initializing;
117
	private Object owner;
118
	private int cachedSize = -1;
119

    
120
	private String role;
121
	private Serializable key;
122
	// collections detect changes made via their public interface and mark
123
	// themselves as dirty as a performance optimization
124
	private boolean dirty;
125
	private Serializable storedSnapshot;
126

    
127
	private String sessionFactoryUuid;
128
	private boolean specjLazyLoad = false;
129

    
130
	/**
131
	 * Not called by Hibernate, but used by non-JDK serialization,
132
	 * eg. SOAP libraries.
133
	 */
134
	public AbstractPersistentCollection() {
135
	}
136

    
137
	protected AbstractPersistentCollection(SessionImplementor session) {
138
		this.session = session;
139
	}
140

    
141
	@Override
142
	public final String getRole() {
143
		return role;
144
	}
145

    
146
	@Override
147
	public final Serializable getKey() {
148
		return key;
149
	}
150

    
151
	@Override
152
	public final boolean isUnreferenced() {
153
		return role == null;
154
	}
155

    
156
	@Override
157
	public final boolean isDirty() {
158
		return dirty;
159
	}
160

    
161
	@Override
162
	public final void clearDirty() {
163
		dirty = false;
164
	}
165

    
166
	@Override
167
	public final void dirty() {
168
		dirty = true;
169
	}
170

    
171
	@Override
172
	public final Serializable getStoredSnapshot() {
173
		return storedSnapshot;
174
	}
175

    
176
	//Careful: these methods do not initialize the collection.
177

    
178
	@Override
179
	public abstract boolean empty();
180

    
181
	/**
182
	 * Called by any read-only method of the collection interface
183
	 */
184
	protected final void read() {
185
		initialize( false );
186
	}
187

    
188
	/**
189
	 * Called by the {@link Collection#size} method
190
	 */
191
	@SuppressWarnings({"JavaDoc"})
192
	protected boolean readSize() {
193
		if ( !initialized ) {
194
			if ( cachedSize != -1 && !hasQueuedOperations() ) {
195
				return true;
196
			}
197
			else {
198
				// In remoting we are sure that session is null
199
				// both when using property paths and switching off conversations
200
				if(session == null && remoting) {
201
					log.info("--> readSize, of " + getRole() + " with key " + getKey());
202
					read();
203
				} else {
204
					boolean isExtraLazy = withTemporarySessionIfNeeded(
205
							new LazyInitializationWork<Boolean>() {
206
								@Override
207
								public Boolean doWork() {
208
									CollectionEntry entry = session.getPersistenceContext().getCollectionEntry( AbstractPersistentCollection.this );
209

    
210
									if ( entry != null ) {
211
										CollectionPersister persister = entry.getLoadedPersister();
212
										if ( persister.isExtraLazy() ) {
213
											if ( hasQueuedOperations() ) {
214
												session.flush();
215
											}
216
											cachedSize = persister.getSize( entry.getLoadedKey(), session );
217
											return true;
218
										}
219
										else {
220
											read();
221
										}
222
									}
223
									else{
224
										throwLazyInitializationExceptionIfNotConnected();
225
									}
226
									return false;
227
								}
228
							}
229
							);
230
					if ( isExtraLazy ) {
231
						return true;
232
					}
233
				}
234
			}
235
		}
236
		return false;
237
	}
238

    
239
	/**
240
	 * TBH not sure why this is public
241
	 *
242
	 * @param <T> The java type of the return for this LazyInitializationWork
243
	 */
244
	public static interface LazyInitializationWork<T> {
245
		/**
246
		 * Do the represented work and return the result.
247
		 *
248
		 * @return The result
249
		 */
250
		public T doWork();
251
	}
252

    
253
	private <T> T withTemporarySessionIfNeeded(LazyInitializationWork<T> lazyInitializationWork) {
254
		SessionImplementor originalSession = null;
255
		boolean isTempSession = false;
256
		boolean isJTA = false;
257

    
258
		if ( session == null ) {
259
			if ( specjLazyLoad ) {
260
				session = openTemporarySessionForLoading();
261
				isTempSession = true;
262
			}
263
			else {
264
				throwLazyInitializationException( "could not initialize proxy - no Session" );
265
			}
266
		}
267
		else if ( !session.isOpen() ) {
268
			if ( specjLazyLoad ) {
269
				originalSession = session;
270
				session = openTemporarySessionForLoading();
271
				isTempSession = true;
272
			}
273
			else {
274
				throwLazyInitializationException( "could not initialize proxy - the owning Session was closed" );
275
			}
276
		}
277
		else if ( !session.isConnected() ) {
278
			if ( specjLazyLoad ) {
279
				originalSession = session;
280
				session = openTemporarySessionForLoading();
281
				isTempSession = true;
282
			}
283
			else {
284
				throwLazyInitializationException( "could not initialize proxy - the owning Session is disconnected" );
285
			}
286
		}
287

    
288
		if ( isTempSession ) {
289
			// TODO: On the next major release, add an
290
			// 'isJTA' or 'getTransactionFactory' method to Session.
291
			/*isJTA = session.getTransactionCoordinator()
292
					.getTransactionContext().getTransactionEnvironment()
293
					.getTransactionFactory()
294
					.compatibleWithJtaSynchronization();*/
295
			isJTA = session.getTransactionCoordinator().getTransactionCoordinatorBuilder().isJta();
296
			if ( !isJTA ) {
297
				// Explicitly handle the transactions only if we're not in
298
				// a JTA environment.  A lazy loading temporary session can
299
				// be created even if a current session and transaction are
300
				// open (ex: session.clear() was used).  We must prevent
301
				// multiple transactions.
302
				( ( Session) session ).beginTransaction();
303
			}
304

    
305
			session.getPersistenceContext().addUninitializedDetachedCollection(
306
					session.getFactory().getCollectionPersister( getRole() ),
307
					this
308
			);
309
		}
310

    
311
		try {
312
			return lazyInitializationWork.doWork();
313
		}
314
		finally {
315
			if ( isTempSession ) {
316
				// make sure the just opened temp session gets closed!
317
				try {
318
					if ( !isJTA ) {
319
						( ( Session) session ).getTransaction().commit();
320
					}
321
					( (Session) session ).close();
322
				}
323
				catch (Exception e) {
324
					log.warn( "Unable to close temporary session used to load lazy collection associated to no session" );
325
				}
326
				session = originalSession;
327
			}
328
		}
329
	}
330

    
331
	private SessionImplementor openTemporarySessionForLoading() {
332
		if ( sessionFactoryUuid == null ) {
333
			throwLazyInitializationException( "SessionFactory UUID not known to create temporary Session for loading" );
334
		}
335

    
336
		SessionFactoryImplementor sf = (SessionFactoryImplementor)
337
				SessionFactoryRegistry.INSTANCE.getSessionFactory( sessionFactoryUuid );
338
		return (SessionImplementor) sf.openSession();
339
	}
340

    
341
	protected Boolean readIndexExistence(final Object index) {
342
		if ( !initialized ) {
343
			// In remoting we are sure that session is null
344
			// both when using property paths and switching off conversations
345
			if(session == null && remoting) {
346
				log.info("--> readIndexExistence, of " + getRole() + " with key " + getKey());
347
				read();
348
			} else {
349
				Boolean extraLazyExistenceCheck = withTemporarySessionIfNeeded(
350
						new LazyInitializationWork<Boolean>() {
351
							@Override
352
							public Boolean doWork() {
353
								CollectionEntry entry = session.getPersistenceContext().getCollectionEntry( AbstractPersistentCollection.this );
354
								CollectionPersister persister = entry.getLoadedPersister();
355
								if ( persister.isExtraLazy() ) {
356
									if ( hasQueuedOperations() ) {
357
										session.flush();
358
									}
359
									return persister.indexExists( entry.getLoadedKey(), index, session );
360
								}
361
								else {
362
									read();
363
								}
364
								return null;
365
							}
366
						}
367
						);
368
				if ( extraLazyExistenceCheck != null ) {
369
					return extraLazyExistenceCheck;
370
				}
371
			}
372
		}
373
		return null;
374
	}
375

    
376
	protected Boolean readElementExistence(final Object element) {
377
		if ( !initialized ) {
378
			// In remoting we are sure that session is null
379
			// both when using property paths and switching off conversations
380
			if(session == null && remoting) {
381
				log.info("--> readElementExistence, of " + getRole() + " with key " + getKey());
382
				read();
383

    
384
			} else {
385
				Boolean extraLazyExistenceCheck = withTemporarySessionIfNeeded(
386
						new LazyInitializationWork<Boolean>() {
387
							@Override
388
							public Boolean doWork() {
389
								CollectionEntry entry = session.getPersistenceContext().getCollectionEntry( AbstractPersistentCollection.this );
390
								CollectionPersister persister = entry.getLoadedPersister();
391
								if ( persister.isExtraLazy() ) {
392
									if ( hasQueuedOperations() ) {
393
										session.flush();
394
									}
395
									return persister.elementExists( entry.getLoadedKey(), element, session );
396
								}
397
								else {
398
									read();
399
								}
400
								return null;
401
							}
402
						}
403
						);
404
				if ( extraLazyExistenceCheck != null ) {
405
					return extraLazyExistenceCheck;
406
				}
407
			}
408
		}
409
		return null;
410
	}
411

    
412
	protected static final Object UNKNOWN = new MarkerObject( "UNKNOWN" );
413

    
414
	protected Object readElementByIndex(final Object index) {
415
		if ( !initialized ) {
416
			// In remoting we are sure that session is null
417
			// both when using property paths and switching off conversations
418
			if(session == null && remoting) {
419
				log.info("--> readElementByIndex, of " + getRole() + " with key " + getKey());
420
				read();
421

    
422
			} else {
423
				class ExtraLazyElementByIndexReader implements LazyInitializationWork {
424
					private boolean isExtraLazy;
425
					private Object element;
426

    
427
					@Override
428
					public Object doWork() {
429
						CollectionEntry entry = session.getPersistenceContext().getCollectionEntry( AbstractPersistentCollection.this );
430
						CollectionPersister persister = entry.getLoadedPersister();
431
						isExtraLazy = persister.isExtraLazy();
432
						if ( isExtraLazy ) {
433
							if ( hasQueuedOperations() ) {
434
								session.flush();
435
							}
436
							element = persister.getElementByIndex( entry.getLoadedKey(), index, session, owner );
437
						}
438
						else {
439
							read();
440
						}
441
						return null;
442
					}
443
				}
444

    
445
				ExtraLazyElementByIndexReader reader = new ExtraLazyElementByIndexReader();
446
				//noinspection unchecked
447
				withTemporarySessionIfNeeded( reader );
448
				if ( reader.isExtraLazy ) {
449
					return reader.element;
450
				}
451
			}
452
		}
453
		return UNKNOWN;
454

    
455
	}
456

    
457
	protected int getCachedSize() {
458
		return cachedSize;
459
	}
460

    
461
	protected boolean isConnectedToSession() {
462
		return session != null
463
				&& session.isOpen()
464
				&& session.getPersistenceContext().containsCollection( this );
465
	}
466

    
467
	protected boolean isInitialized() {
468
		return initialized;
469
	}
470

    
471
	/**
472
	 * Called by any writer method of the collection interface
473
	 */
474
	protected final void write() {
475
		initialize( true );
476
		dirty();
477
	}
478

    
479
	/**
480
	 * Is this collection in a state that would allow us to
481
	 * "queue" operations?
482
	 */
483
	@SuppressWarnings({"JavaDoc"})
484
	protected boolean isOperationQueueEnabled() {
485
		return !initialized
486
				&& isConnectedToSession()
487
				&& isInverseCollection();
488
	}
489

    
490
	/**
491
	 * Is this collection in a state that would allow us to
492
	 * "queue" puts? This is a special case, because of orphan
493
	 * delete.
494
	 */
495
	@SuppressWarnings({"JavaDoc"})
496
	protected boolean isPutQueueEnabled() {
497
		return !initialized
498
				&& isConnectedToSession()
499
				&& isInverseOneToManyOrNoOrphanDelete();
500
	}
501

    
502
	/**
503
	 * Is this collection in a state that would allow us to
504
	 * "queue" clear? This is a special case, because of orphan
505
	 * delete.
506
	 */
507
	@SuppressWarnings({"JavaDoc"})
508
	protected boolean isClearQueueEnabled() {
509
		return !initialized
510
				&& isConnectedToSession()
511
				&& isInverseCollectionNoOrphanDelete();
512
	}
513

    
514
	/**
515
	 * Is this the "inverse" end of a bidirectional association?
516
	 */
517
	@SuppressWarnings({"JavaDoc"})
518
	protected boolean isInverseCollection() {
519
		final CollectionEntry ce = session.getPersistenceContext().getCollectionEntry( this );
520
		return ce != null && ce.getLoadedPersister().isInverse();
521
	}
522

    
523
	/**
524
	 * Is this the "inverse" end of a bidirectional association with
525
	 * no orphan delete enabled?
526
	 */
527
	@SuppressWarnings({"JavaDoc"})
528
	protected boolean isInverseCollectionNoOrphanDelete() {
529
		final CollectionEntry ce = session.getPersistenceContext().getCollectionEntry( this );
530
		return ce != null
531
				&&
532
				ce.getLoadedPersister().isInverse() &&
533
				!ce.getLoadedPersister().hasOrphanDelete();
534
	}
535

    
536
	/**
537
	 * Is this the "inverse" end of a bidirectional one-to-many, or
538
	 * of a collection with no orphan delete?
539
	 */
540
	@SuppressWarnings({"JavaDoc"})
541
	protected boolean isInverseOneToManyOrNoOrphanDelete() {
542
		final CollectionEntry ce = session.getPersistenceContext().getCollectionEntry( this );
543
		return ce != null
544
				&& ce.getLoadedPersister().isInverse()
545
				&& ( ce.getLoadedPersister().isOneToMany() || !ce.getLoadedPersister().hasOrphanDelete() );
546
	}
547

    
548
	/**
549
	 * Queue an addition
550
	 */
551
	@SuppressWarnings({"JavaDoc"})
552
	protected final void queueOperation(DelayedOperation operation) {
553
		if ( operationQueue == null ) {
554
			operationQueue = new ArrayList<DelayedOperation>( 10 );
555
		}
556
		operationQueue.add( operation );
557
		//needed so that we remove this collection from the second-level cache
558
		dirty = true;
559
	}
560

    
561
	/**
562
	 * Replace entity instances with copy in {@code copyCache}/.
563
	 *
564
	 * @param copyCache - mapping from entity in the process of being
565
	 *                    merged to managed copy.
566
	 */
567
	public final void replaceQueuedOperationValues(CollectionPersister persister, Map copyCache) {
568
		for ( DelayedOperation operation : operationQueue ) {
569
			if ( ValueDelayedOperation.class.isInstance( operation ) ) {
570
				( (ValueDelayedOperation) operation ).replace( persister, copyCache );
571
			}
572
		}
573
	}
574

    
575
	/**
576
	 * After reading all existing elements from the database,
577
	 * add the queued elements to the underlying collection.
578
	 */
579
	protected final void performQueuedOperations() {
580
		for ( DelayedOperation operation : operationQueue ) {
581
			operation.operate();
582
		}
583
	}
584

    
585
	@Override
586
	public void setSnapshot(Serializable key, String role, Serializable snapshot) {
587
		this.key = key;
588
		this.role = role;
589
		this.storedSnapshot = snapshot;
590
	}
591

    
592
	@Override
593
	public void postAction() {
594
		operationQueue = null;
595
		cachedSize = -1;
596
		clearDirty();
597
	}
598

    
599
	@Override
600
	public Object getValue() {
601
		return this;
602
	}
603

    
604
	@Override
605
	public void beginRead() {
606
		// override on some subclasses
607
		initializing = true;
608
	}
609

    
610
	@Override
611
	public boolean endRead() {
612
		//override on some subclasses
613
		return afterInitialize();
614
	}
615

    
616
	@Override
617
	public boolean afterInitialize() {
618
		setInitialized();
619
		//do this bit after setting initialized to true or it will recurse
620
		if ( operationQueue != null ) {
621
			performQueuedOperations();
622
			operationQueue = null;
623
			cachedSize = -1;
624
			return false;
625
		}
626
		else {
627
			return true;
628
		}
629
	}
630

    
631
	/**
632
	 * Initialize the collection, if possible, wrapping any exceptions
633
	 * in a runtime exception
634
	 *
635
	 * @param writing currently obsolete
636
	 *
637
	 * @throws LazyInitializationException if we cannot initialize
638
	 */
639
	protected final void initialize(final boolean writing) {
640
	    if ( initialized ) {
641
	        return;
642
	    }
643

    
644
	    // In remoting we are sure that session is null
645
	    // both when using property paths and switching off conversations
646
	    if(session == null && remoting) {
647
	        remoteInitialize();
648
	    } else {
649
	        withTemporarySessionIfNeeded(
650
	                new LazyInitializationWork<Object>() {
651
	                    @Override
652
	                    public Object doWork() {
653
	                        session.initializeCollection( AbstractPersistentCollection.this, writing );
654
	                        return null;
655
	                    }
656
	                }
657
	                );
658
	    }
659
	}
660

    
661
	private void throwLazyInitializationExceptionIfNotConnected() {
662
		if ( !isConnectedToSession() ) {
663
			throwLazyInitializationException( "no session or session was closed" );
664
		}
665
		if ( !session.isConnected() ) {
666
			throwLazyInitializationException( "session is disconnected" );
667
		}
668
	}
669

    
670
	private void throwLazyInitializationException(String message) {
671
		throw new LazyInitializationException(
672
				"failed to lazily initialize a collection" +
673
						(role == null ? "" : " of role: " + role) +
674
						", " + message
675
		);
676
	}
677

    
678
	protected final void setInitialized() {
679
		this.initializing = false;
680
		this.initialized = true;
681
	}
682

    
683
	protected final void setDirectlyAccessible(boolean directlyAccessible) {
684
		this.directlyAccessible = directlyAccessible;
685
	}
686

    
687
	@Override
688
	public boolean isDirectlyAccessible() {
689
		return directlyAccessible;
690
	}
691

    
692
	/**
693
	 * Disassociate this collection from the given session.
694
	 *
695
	 * @return true if this was currently associated with the given session
696
	 */
697
	@Override
698
    public final boolean unsetSession(SessionImplementor currentSession) {
699
		prepareForPossibleSpecialSpecjInitialization();
700
		if ( currentSession == this.session ) {
701
			this.session = null;
702
			return true;
703
		}
704
		else {
705
			return false;
706
		}
707
	}
708

    
709
	protected void prepareForPossibleSpecialSpecjInitialization() {
710
		if ( session != null ) {
711
			specjLazyLoad = session.getFactory().getSettings().isInitializeLazyStateOutsideTransactionsEnabled();
712

    
713
			if ( specjLazyLoad && sessionFactoryUuid == null ) {
714
				try {
715
					sessionFactoryUuid = (String) session.getFactory().getReference().get( "uuid" ).getContent();
716
				}
717
				catch (NamingException e) {
718
					//not much we can do if this fails...
719
				}
720
			}
721
		}
722
	}
723

    
724

    
725
	/**
726
	 * Associate the collection with the given session.
727
	 *
728
	 * @return false if the collection was already associated with the session
729
	 *
730
	 * @throws HibernateException if the collection was already associated
731
	 * with another open session
732
	 */
733
	@Override
734
    public final boolean setCurrentSession(SessionImplementor session) throws HibernateException {
735
		if ( session == this.session ) {
736
			return false;
737
		}
738
		else {
739
			if ( isConnectedToSession() ) {
740
				CollectionEntry ce = session.getPersistenceContext().getCollectionEntry( this );
741
				if ( ce == null ) {
742
					throw new HibernateException(
743
							"Illegal attempt to associate a collection with two open sessions"
744
					);
745
				}
746
				else {
747
					throw new HibernateException(
748
							"Illegal attempt to associate a collection with two open sessions: " +
749
									MessageHelper.collectionInfoString(
750
											ce.getLoadedPersister(), this,
751
											ce.getLoadedKey(), session
752
									)
753
					);
754
				}
755
			}
756
			else {
757
				this.session = session;
758
				return true;
759
			}
760
		}
761
	}
762

    
763
	/**
764
	 * Do we need to completely recreate this collection when it changes?
765
	 */
766
	@Override
767
    public boolean needsRecreate(CollectionPersister persister) {
768
		return false;
769
	}
770

    
771
	/**
772
	 * To be called internally by the session, forcing
773
	 * immediate initialization.
774
	 */
775
	@Override
776
    public final void forceInitialization() throws HibernateException {
777
		if ( !initialized ) {
778
			if ( initializing ) {
779
				throw new AssertionFailure( "force initialize loading collection" );
780
			}
781
			if ( session == null ) {
782
				throw new HibernateException( "collection is not associated with any session" );
783
			}
784
			if ( !session.isConnected() ) {
785
				throw new HibernateException( "disconnected session" );
786
			}
787
			session.initializeCollection( this, false );
788
		}
789
	}
790

    
791

    
792
	/**
793
	 * Get the current snapshot from the session
794
	 */
795
	@SuppressWarnings({"JavaDoc"})
796
	protected final Serializable getSnapshot() {
797
		return session.getPersistenceContext().getSnapshot( this );
798
	}
799

    
800
	@Override
801
	public final boolean wasInitialized() {
802
		return initialized;
803
	}
804

    
805
	@Override
806
	public boolean isRowUpdatePossible() {
807
		return true;
808
	}
809

    
810
	@Override
811
	public final boolean hasQueuedOperations() {
812
		return operationQueue != null;
813
	}
814

    
815
	@Override
816
	public final Iterator queuedAdditionIterator() {
817
		if ( hasQueuedOperations() ) {
818
			return new Iterator() {
819
				private int index;
820

    
821
				@Override
822
				public Object next() {
823
					return operationQueue.get( index++ ).getAddedInstance();
824
				}
825

    
826
				@Override
827
				public boolean hasNext() {
828
					return index < operationQueue.size();
829
				}
830

    
831
				@Override
832
				public void remove() {
833
					throw new UnsupportedOperationException();
834
				}
835
			};
836
		}
837
		else {
838
			return EmptyIterator.INSTANCE;
839
		}
840
	}
841

    
842
	@Override
843
	@SuppressWarnings({"unchecked"})
844
	public final Collection getQueuedOrphans(String entityName) {
845
		if ( hasQueuedOperations() ) {
846
			final Collection additions = new ArrayList( operationQueue.size() );
847
			final Collection removals = new ArrayList( operationQueue.size() );
848
			for ( DelayedOperation operation : operationQueue ) {
849
				additions.add( operation.getAddedInstance() );
850
				removals.add( operation.getOrphan() );
851
			}
852
			return getOrphans( removals, additions, entityName, session );
853
		}
854
		else {
855
			return Collections.EMPTY_LIST;
856
		}
857
	}
858

    
859
	@Override
860
	public void preInsert(CollectionPersister persister) throws HibernateException {
861
	}
862

    
863
	@Override
864
	public void afterRowInsert(CollectionPersister persister, Object entry, int i) throws HibernateException {
865
	}
866

    
867
	@Override
868
	public abstract Collection getOrphans(Serializable snapshot, String entityName) throws HibernateException;
869

    
870
	/**
871
	 * Get the session currently associated with this collection.
872
	 *
873
	 * @return The session
874
	 */
875
	public final SessionImplementor getSession() {
876
		return session;
877
	}
878

    
879
	protected final class IteratorProxy implements Iterator {
880
		protected final Iterator itr;
881

    
882
		public IteratorProxy(Iterator itr) {
883
			this.itr = itr;
884
		}
885

    
886
		@Override
887
		public boolean hasNext() {
888
			return itr.hasNext();
889
		}
890

    
891
		@Override
892
		public Object next() {
893
			return itr.next();
894
		}
895

    
896
		@Override
897
		public void remove() {
898
			write();
899
			itr.remove();
900
		}
901
	}
902
	
903
	protected final class ListIteratorProxy implements ListIterator {
904
		protected final ListIterator itr;
905

    
906
		public ListIteratorProxy(ListIterator itr) {
907
			this.itr = itr;
908
		}
909

    
910
		@Override
911
		@SuppressWarnings({"unchecked"})
912
		public void add(Object o) {
913
			write();
914
			itr.add( o );
915
		}
916

    
917
		@Override
918
		public boolean hasNext() {
919
			return itr.hasNext();
920
		}
921

    
922
		@Override
923
		public boolean hasPrevious() {
924
			return itr.hasPrevious();
925
		}
926

    
927
		@Override
928
		public Object next() {
929
			return itr.next();
930
		}
931

    
932
		@Override
933
		public int nextIndex() {
934
			return itr.nextIndex();
935
		}
936

    
937
		@Override
938
		public Object previous() {
939
			return itr.previous();
940
		}
941

    
942
		@Override
943
		public int previousIndex() {
944
			return itr.previousIndex();
945
		}
946

    
947
		@Override
948
		public void remove() {
949
			write();
950
			itr.remove();
951
		}
952

    
953
		@Override
954
		@SuppressWarnings({"unchecked"})
955
		public void set(Object o) {
956
			write();
957
			itr.set( o );
958
		}
959
	}
960

    
961
	protected class SetProxy implements java.util.Set {
962
		protected final Collection set;
963

    
964
		public SetProxy(Collection set) {
965
			this.set = set;
966
		}
967

    
968
		@Override
969
		@SuppressWarnings({"unchecked"})
970
		public boolean add(Object o) {
971
			write();
972
			return set.add( o );
973
		}
974

    
975
		@Override
976
		@SuppressWarnings({"unchecked"})
977
		public boolean addAll(Collection c) {
978
			write();
979
			return set.addAll( c );
980
		}
981

    
982
		@Override
983
		public void clear() {
984
			write();
985
			set.clear();
986
		}
987

    
988
		@Override
989
		public boolean contains(Object o) {
990
			return set.contains( o );
991
		}
992

    
993
		@Override
994
		@SuppressWarnings("unchecked")
995
		public boolean containsAll(Collection c) {
996
			return set.containsAll( c );
997
		}
998

    
999
		@Override
1000
		public boolean isEmpty() {
1001
			return set.isEmpty();
1002
		}
1003

    
1004
		@Override
1005
		public Iterator iterator() {
1006
			return new IteratorProxy( set.iterator() );
1007
		}
1008

    
1009
		@Override
1010
		public boolean remove(Object o) {
1011
			write();
1012
			return set.remove( o );
1013
		}
1014

    
1015
		@Override
1016
		@SuppressWarnings("unchecked")
1017
		public boolean removeAll(Collection c) {
1018
			write();
1019
			return set.removeAll( c );
1020
		}
1021

    
1022
		@Override
1023
		@SuppressWarnings("unchecked")
1024
		public boolean retainAll(Collection c) {
1025
			write();
1026
			return set.retainAll( c );
1027
		}
1028

    
1029
		@Override
1030
		public int size() {
1031
			return set.size();
1032
		}
1033

    
1034
		@Override
1035
		public Object[] toArray() {
1036
			return set.toArray();
1037
		}
1038

    
1039
		@Override
1040
		@SuppressWarnings({"unchecked"})
1041
		public Object[] toArray(Object[] array) {
1042
			return set.toArray( array );
1043
		}
1044
	}
1045

    
1046
	protected final class ListProxy implements java.util.List {
1047
		protected final List list;
1048

    
1049
		public ListProxy(List list) {
1050
			this.list = list;
1051
		}
1052

    
1053
		@Override
1054
		@SuppressWarnings({"unchecked"})
1055
		public void add(int index, Object value) {
1056
			write();
1057
			list.add( index, value );
1058
		}
1059

    
1060
		@Override
1061
		@SuppressWarnings({"unchecked"})
1062
		public boolean add(Object o) {
1063
			write();
1064
			return list.add( o );
1065
		}
1066

    
1067
		@Override
1068
		@SuppressWarnings({"unchecked"})
1069
		public boolean addAll(Collection c) {
1070
			write();
1071
			return list.addAll( c );
1072
		}
1073

    
1074
		@Override
1075
		@SuppressWarnings({"unchecked"})
1076
		public boolean addAll(int i, Collection c) {
1077
			write();
1078
			return list.addAll( i, c );
1079
		}
1080

    
1081
		@Override
1082
		public void clear() {
1083
			write();
1084
			list.clear();
1085
		}
1086

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

    
1092
		@Override
1093
		@SuppressWarnings("unchecked")
1094
		public boolean containsAll(Collection c) {
1095
			return list.containsAll( c );
1096
		}
1097

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

    
1103
		@Override
1104
		public int indexOf(Object o) {
1105
			return list.indexOf( o );
1106
		}
1107

    
1108
		@Override
1109
		public boolean isEmpty() {
1110
			return list.isEmpty();
1111
		}
1112

    
1113
		@Override
1114
		public Iterator iterator() {
1115
			return new IteratorProxy( list.iterator() );
1116
		}
1117

    
1118
		@Override
1119
		public int lastIndexOf(Object o) {
1120
			return list.lastIndexOf( o );
1121
		}
1122

    
1123
		@Override
1124
		public ListIterator listIterator() {
1125
			return new ListIteratorProxy( list.listIterator() );
1126
		}
1127

    
1128
		@Override
1129
		public ListIterator listIterator(int i) {
1130
			return new ListIteratorProxy( list.listIterator( i ) );
1131
		}
1132

    
1133
		@Override
1134
		public Object remove(int i) {
1135
			write();
1136
			return list.remove( i );
1137
		}
1138

    
1139
		@Override
1140
		public boolean remove(Object o) {
1141
			write();
1142
			return list.remove( o );
1143
		}
1144

    
1145
		@Override
1146
		@SuppressWarnings("unchecked")
1147
		public boolean removeAll(Collection c) {
1148
			write();
1149
			return list.removeAll( c );
1150
		}
1151

    
1152
		@Override
1153
		@SuppressWarnings("unchecked")
1154
		public boolean retainAll(Collection c) {
1155
			write();
1156
			return list.retainAll( c );
1157
		}
1158

    
1159
		@Override
1160
		@SuppressWarnings({"unchecked"})
1161
		public Object set(int i, Object o) {
1162
			write();
1163
			return list.set( i, o );
1164
		}
1165

    
1166
		@Override
1167
		public int size() {
1168
			return list.size();
1169
		}
1170

    
1171
		@Override
1172
		public List subList(int i, int j) {
1173
			return list.subList( i, j );
1174
		}
1175

    
1176
		@Override
1177
		public Object[] toArray() {
1178
			return list.toArray();
1179
		}
1180

    
1181
		@Override
1182
		@SuppressWarnings({"unchecked"})
1183
		public Object[] toArray(Object[] array) {
1184
			return list.toArray( array );
1185
		}
1186

    
1187
	}
1188

    
1189
	/**
1190
	 * Contract for operations which are part of a collection's operation queue.
1191
	 */
1192
	protected interface DelayedOperation {
1193
		public void operate();
1194

    
1195
		public Object getAddedInstance();
1196

    
1197
		public Object getOrphan();
1198
	}
1199
	
1200
	protected interface ValueDelayedOperation extends DelayedOperation {
1201
		void replace(CollectionPersister collectionPersister, Map copyCache);
1202
	}
1203
	
1204
	protected abstract class AbstractValueDelayedOperation implements ValueDelayedOperation {
1205
		private Object addedValue;
1206
		private Object orphan;
1207

    
1208
		protected AbstractValueDelayedOperation(Object addedValue, Object orphan) {
1209
			this.addedValue = addedValue;
1210
			this.orphan = orphan;
1211
		}
1212

    
1213
		public void replace(CollectionPersister persister, Map copyCache) {
1214
			if ( addedValue != null ) {
1215
				addedValue = getReplacement( persister.getElementType(), addedValue, copyCache );
1216
			}
1217
		}
1218

    
1219
		protected final Object getReplacement(Type type, Object current, Map copyCache) {
1220
			return type.replace( current, null, session, owner, copyCache );
1221
		}
1222

    
1223
		@Override
1224
		public final Object getAddedInstance() {
1225
			return addedValue;
1226
		}
1227

    
1228
		@Override
1229
		public final Object getOrphan() {
1230
			return orphan;
1231
		}
1232
	}
1233

    
1234
	/**
1235
	 * Given a collection of entity instances that used to
1236
	 * belong to the collection, and a collection of instances
1237
	 * that currently belong, return a collection of orphans
1238
	 */
1239
	@SuppressWarnings({"JavaDoc", "unchecked"})
1240
	protected static Collection getOrphans(
1241
			Collection oldElements,
1242
			Collection currentElements,
1243
			String entityName,
1244
			SessionImplementor session) throws HibernateException {
1245

    
1246
		// short-circuit(s)
1247
		if ( currentElements.size() == 0 ) {
1248
			return oldElements; // no new elements, the old list contains only Orphans
1249
		}
1250
		if ( oldElements.size() == 0 ) {
1251
			return oldElements; // no old elements, so no Orphans neither
1252
		}
1253

    
1254
		final EntityPersister entityPersister = session.getFactory().getEntityPersister( entityName );
1255
		final Type idType = entityPersister.getIdentifierType();
1256

    
1257
		// create the collection holding the Orphans
1258
		Collection res = new ArrayList();
1259

    
1260
		// collect EntityIdentifier(s) of the *current* elements - add them into a HashSet for fast access
1261
		java.util.Set currentIds = new HashSet();
1262
		java.util.Set currentSaving = new IdentitySet();
1263
		for ( Object current : currentElements ) {
1264
			if ( current != null && ForeignKeys.isNotTransient( entityName, current, null, session ) ) {
1265
				EntityEntry ee = session.getPersistenceContext().getEntry( current );
1266
				if ( ee != null && ee.getStatus() == Status.SAVING ) {
1267
					currentSaving.add( current );
1268
				}
1269
				else {
1270
					Serializable currentId = ForeignKeys.getEntityIdentifierIfNotUnsaved(
1271
							entityName,
1272
							current,
1273
							session
1274
					);
1275
					currentIds.add( new TypedValue( idType, currentId, entityPersister.getEntityMode() ) );
1276
				}
1277
			}
1278
		}
1279

    
1280
		// iterate over the *old* list
1281
		for ( Object old : oldElements ) {
1282
			if ( !currentSaving.contains( old ) ) {
1283
				Serializable oldId = ForeignKeys.getEntityIdentifierIfNotUnsaved( entityName, old, session );
1284
				if ( !currentIds.contains( new TypedValue( idType, oldId, entityPersister.getEntityMode() ) ) ) {
1285
					res.add( old );
1286
				}
1287
			}
1288
		}
1289

    
1290
		return res;
1291
	}
1292

    
1293
	private static boolean mayUseIdDirect(Type idType) {
1294
		return idType == StringType.INSTANCE
1295
			|| idType == IntegerType.INSTANCE
1296
			|| idType == LongType.INSTANCE
1297
			|| idType == UUIDBinaryType.INSTANCE
1298
			|| idType == UUIDCharType.INSTANCE
1299
			|| idType == PostgresUUIDType.INSTANCE;
1300
	}
1301

    
1302
	public static void identityRemove(
1303
			Collection list,
1304
			Object object,
1305
			String entityName,
1306
			SessionImplementor session) throws HibernateException {
1307

    
1308
		if ( object != null && ForeignKeys.isNotTransient( entityName, object, null, session ) ) {
1309
			final EntityPersister entityPersister = session.getFactory().getEntityPersister( entityName );
1310
			Type idType = entityPersister.getIdentifierType();
1311

    
1312
			Serializable idOfCurrent = ForeignKeys.getEntityIdentifierIfNotUnsaved( entityName, object, session );
1313
			Iterator itr = list.iterator();
1314
			while ( itr.hasNext() ) {
1315
				Serializable idOfOld = ForeignKeys.getEntityIdentifierIfNotUnsaved( entityName, itr.next(), session );
1316
				if ( idType.isEqual( idOfCurrent, idOfOld, session.getFactory() ) ) {
1317
					itr.remove();
1318
					break;
1319
				}
1320
			}
1321

    
1322
		}
1323
	}
1324

    
1325
	@Override
1326
	public Object getIdentifier(Object entry, int i) {
1327
		throw new UnsupportedOperationException();
1328
	}
1329

    
1330
	@Override
1331
	public Object getOwner() {
1332
		return owner;
1333
	}
1334

    
1335
	@Override
1336
	public void setOwner(Object owner) {
1337
		this.owner = owner;
1338
	}
1339

    
1340
/** #################################################################### 
1341
    
1342
    ADDED PART: Below is section of code which makes remote service calls 
1343
    
1344
    #################################################################### */
1345
    
1346
	
1347
	// The affected methods are :
1348
	// initialize(final boolean writing)
1349
	// readSize()
1350
	// readIndexExistence(final Object index)
1351
	// readElementExistence(final Object element)
1352
	// readElementByIndex(final Object index)
1353

    
1354
	private static CdmApplicationRemoteConfiguration configuration;
1355
	private static boolean remoting = false;
1356

    
1357
	public static void setConfiguration(CdmApplicationRemoteConfiguration conf) {
1358
	    remoting = true;
1359
		configuration = conf;
1360
	}
1361

    
1362
	private void remoteInitialize() {
1363

    
1364
		if (getOwner() != null && !initialized) {
1365

    
1366
			try {
1367
				String role = getRole();
1368
				String fieldName = role.substring(role.lastIndexOf(".") + 1);
1369
				log.info("--> Remote Lazy Initializing Collection " + getRole() + " , owner : " + getOwner().getClass() + "/" + getKey() + " , field : " + fieldName);
1370
				Object owner = getOwner();
1371
				CdmBase cdmBase;
1372
				if(owner instanceof CdmBase) {
1373
				    cdmBase = (CdmBase)owner;
1374
				} else {
1375
				    throw new HibernateException("Owner of persistent collection is not a cdm entity");
1376
				}
1377
				if(configuration == null) {
1378
					throw new HibernateException("CdmApplicationRemoteConfiguration not initialized (null)");
1379
				}
1380
				ICachedCommonService cachedCommonService = configuration.getCachedCommonService();
1381
				if(cachedCommonService == null) {
1382
					throw new HibernateException("commonService not initialized (null)");
1383
				}
1384

    
1385
				//Object obj = ProxyUtils.deproxyIfInitialized(cachedCommonService.initializeCollection(this));
1386
				Object obj = ProxyUtils.deproxyIfInitialized(cachedCommonService.initializeCollection(cdmBase.getUuid(), fieldName));
1387
				if(ProxyUtils.isUninitializedProxy(obj)) {
1388
				    throw new HibernateException("Persistent Collection initialized but is still a proxy");
1389
				}
1390
				afterInitialize();
1391

    
1392
				Class<?> clazz = getClass();
1393
				if (clazz != null) {
1394
					//CollectionField cf = cachedCommonService.getCollectionField(col);
1395
					//cachedCommonService.updatePersistentCollection(cf);
1396
				    Object collectionType = ProxyUtils.getCollectionType(obj, clazz);
1397
					Field field = clazz.getDeclaredField(collectionType.toString());
1398
					field.setAccessible(true);
1399
					field.set(this, obj);
1400
					ProxyUtils.setRoleValueInOwner(owner, role, obj);
1401

    
1402
				}
1403
			} catch (Exception ex) {
1404
				throw new CdmEagerLoadingException(ex);
1405
			}
1406
		}
1407
	}
1408
}
    (1-1/1)