Project

General

Profile

Download (39.1 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.FlushMode;
43
import org.hibernate.HibernateException;
44
import org.hibernate.LazyInitializationException;
45
import org.hibernate.Session;
46
import org.hibernate.collection.internal.AbstractPersistentCollection.DelayedOperation;
47
import org.hibernate.collection.internal.AbstractPersistentCollection.IteratorProxy;
48
import org.hibernate.collection.internal.AbstractPersistentCollection.LazyInitializationWork;
49
import org.hibernate.collection.internal.AbstractPersistentCollection.ListIteratorProxy;
50
import org.hibernate.collection.internal.AbstractPersistentCollection.ValueDelayedOperation;
51
import org.hibernate.collection.spi.PersistentCollection;
52
import org.hibernate.engine.internal.ForeignKeys;
53
import org.hibernate.engine.spi.CollectionEntry;
54
import org.hibernate.engine.spi.EntityEntry;
55
import org.hibernate.engine.spi.SessionFactoryImplementor;
56
import org.hibernate.engine.spi.SessionImplementor;
57
import org.hibernate.engine.spi.Status;
58
import org.hibernate.engine.spi.TypedValue;
59
import org.hibernate.internal.CoreLogging;
60
import org.hibernate.internal.CoreMessageLogger;
61
import org.hibernate.internal.SessionFactoryRegistry;
62
import org.hibernate.internal.util.MarkerObject;
63
import org.hibernate.internal.util.collections.EmptyIterator;
64
import org.hibernate.internal.util.collections.IdentitySet;
65
import org.hibernate.persister.collection.CollectionPersister;
66
import org.hibernate.persister.entity.EntityPersister;
67
import org.hibernate.pretty.MessageHelper;
68
import org.hibernate.type.CompositeType;
69
import org.hibernate.type.IntegerType;
70
import org.hibernate.type.LongType;
71
import org.hibernate.type.PostgresUUIDType;
72
import org.hibernate.type.StringType;
73
import org.hibernate.type.Type;
74
import org.hibernate.type.UUIDBinaryType;
75
import org.hibernate.type.UUIDCharType;
76
import org.jboss.logging.Logger;
77

    
78
import eu.etaxonomy.cdm.api.application.CdmApplicationRemoteConfiguration;
79
import eu.etaxonomy.cdm.cache.ProxyUtils;
80
import eu.etaxonomy.cdm.model.common.CdmBase;
81
import eu.etaxonomy.taxeditor.remoting.CdmEagerLoadingException;
82
import eu.etaxonomy.taxeditor.service.ICachedCommonService;
83

    
84
/**
85
 * Base class implementing {@link org.hibernate.collection.spi.PersistentCollection}
86
 *
87
 * This is an extended copy of the original class from hibernate. It has been extended to
88
 * allow making remote service calls to spring httpinvoker services (see section at the bottom
89
 * of this class).
90
 *
91
 * @author Gavin King
92
 * @author Cherian Mathew
93
 */
94
public abstract class AbstractPersistentCollection implements Serializable, PersistentCollection {
95
	private static final CoreMessageLogger LOG = CoreLogging.messageLogger( AbstractPersistentCollection.class );
96

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

    
117
	private transient SessionImplementor session;
118
	private boolean isTempSession = false;
119
	private boolean initialized;
120
	private transient List<DelayedOperation> operationQueue;
121
	private transient boolean directlyAccessible;
122
	private transient boolean initializing;
123
	private Object owner;
124
	private int cachedSize = -1;
125

    
126
	private String role;
127
	private Serializable key;
128
	// collections detect changes made via their public interface and mark
129
	// themselves as dirty as a performance optimization
130
	private boolean dirty;
131
	private Serializable storedSnapshot;
132

    
133
	private String sessionFactoryUuid;
134
	private boolean allowLoadOutsideTransaction;
135

    
136
	/**
137
	 * Not called by Hibernate, but used by non-JDK serialization,
138
	 * eg. SOAP libraries.
139
	 */
140
	public AbstractPersistentCollection() {
141
	}
142

    
143
	protected AbstractPersistentCollection(SessionImplementor session) {
144
		this.session = session;
145
	}
146

    
147
	@Override
148
	public final String getRole() {
149
		return role;
150
	}
151

    
152
	@Override
153
	public final Serializable getKey() {
154
		return key;
155
	}
156

    
157
	@Override
158
	public final boolean isUnreferenced() {
159
		return role == null;
160
	}
161

    
162
	@Override
163
	public final boolean isDirty() {
164
		return dirty;
165
	}
166

    
167
	@Override
168
	public final void clearDirty() {
169
		dirty = false;
170
	}
171

    
172
	@Override
173
	public final void dirty() {
174
		dirty = true;
175
	}
176

    
177
	@Override
178
	public final Serializable getStoredSnapshot() {
179
		return storedSnapshot;
180
	}
181

    
182
	//Careful: these methods do not initialize the collection.
183

    
184
	@Override
185
	public abstract boolean empty();
186

    
187
	/**
188
	 * Called by any read-only method of the collection interface
189
	 */
190
	protected final void read() {
191
		initialize( false );
192
	}
193

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

    
216
								if ( entry != null ) {
217
									final CollectionPersister persister = entry.getLoadedPersister();
218
									if ( persister.isExtraLazy() ) {
219
										if ( hasQueuedOperations() ) {
220
											session.flush();
221
										}
222
										cachedSize = persister.getSize( entry.getLoadedKey(), session );
223
										return true;
224
									}
225
									else {
226
										read();
227
									}
228
								}
229
								else{
230
									throwLazyInitializationExceptionIfNotConnected();
231
								}
232
								return false;
233
							}
234
						}
235
				);
236
				if ( isExtraLazy ) {
237
					return true;
238
				}
239
				}
240
			}
241
		}
242
		return false;
243
	}
244

    
245
	/**
246
	 * TBH not sure why this is public
247
	 *
248
	 * @param <T> The java type of the return for this LazyInitializationWork
249
	 */
250
	public static interface LazyInitializationWork<T> {
251
		/**
252
		 * Do the represented work and return the result.
253
		 *
254
		 * @return The result
255
		 */
256
		public T doWork();
257
	}
258

    
259
	private <T> T withTemporarySessionIfNeeded(LazyInitializationWork<T> lazyInitializationWork) {
260
		SessionImplementor tempSession = null;
261

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

    
287

    
288
		SessionImplementor originalSession = null;
289
		boolean isJTA = false;
290

    
291
		if ( tempSession != null ) {
292
			isTempSession = true;
293
			originalSession = session;
294
			session = tempSession;
295

    
296

    
297
			isJTA = session.getTransactionCoordinator().getTransactionCoordinatorBuilder().isJta();
298
			
299
			if ( !isJTA ) {
300
				// Explicitly handle the transactions only if we're not in
301
				// a JTA environment.  A lazy loading temporary session can
302
				// be created even if a current session and transaction are
303
				// open (ex: session.clear() was used).  We must prevent
304
				// multiple transactions.
305
				( (Session) session ).beginTransaction();
306
			}
307

    
308
			session.getPersistenceContext().addUninitializedDetachedCollection(
309
					session.getFactory().getCollectionPersister( getRole() ),
310
					this
311
			);
312
		}
313

    
314
		try {
315
			return lazyInitializationWork.doWork();
316
		}
317
		finally {
318
			if ( tempSession != null ) {
319
				// make sure the just opened temp session gets closed!
320
				isTempSession = false;
321
				session = originalSession;
322

    
323
				try {
324
					if ( !isJTA ) {
325
						( (Session) tempSession ).getTransaction().commit();
326
					}
327
					( (Session) tempSession ).close();
328
				}
329
				catch (Exception e) {
330
					LOG.warn( "Unable to close temporary session used to load lazy collection associated to no session" );
331
				}
332
			}
333
		}
334
	}
335

    
336
	private SessionImplementor openTemporarySessionForLoading() {
337
		if ( sessionFactoryUuid == null ) {
338
			throwLazyInitializationException( "SessionFactory UUID not known to create temporary Session for loading" );
339
		}
340

    
341
		final SessionFactoryImplementor sf = (SessionFactoryImplementor)
342
				SessionFactoryRegistry.INSTANCE.getSessionFactory( sessionFactoryUuid );
343
		return (SessionImplementor) sf.openSession();
344
	}
345

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

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

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

    
417
	protected static final Object UNKNOWN = new MarkerObject( "UNKNOWN" );
418

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

    
427
			} else {
428
			class ExtraLazyElementByIndexReader implements LazyInitializationWork {
429
				private boolean isExtraLazy;
430
				private Object element;
431

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

    
450
			final ExtraLazyElementByIndexReader reader = new ExtraLazyElementByIndexReader();
451
			//noinspection unchecked
452
			withTemporarySessionIfNeeded( reader );
453
			if ( reader.isExtraLazy ) {
454
				return reader.element;
455
			}
456
			}
457
		}
458
		return UNKNOWN;
459

    
460
	}
461

    
462
	protected int getCachedSize() {
463
		return cachedSize;
464
	}
465

    
466
	protected boolean isConnectedToSession() {
467
		return session != null
468
				&& session.isOpen()
469
				&& session.getPersistenceContext().containsCollection( this );
470
	}
471

    
472
	protected boolean isInitialized() {
473
		return initialized;
474
	}
475

    
476
	/**
477
	 * Called by any writer method of the collection interface
478
	 */
479
	protected final void write() {
480
		initialize( true );
481
		dirty();
482
	}
483

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

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

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

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

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

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

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

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

    
580
	/**
581
	 * After reading all existing elements from the database,
582
	 * add the queued elements to the underlying collection.
583
	 */
584
	protected final void performQueuedOperations() {
585
		for ( DelayedOperation operation : operationQueue ) {
586
			operation.operate();
587
		}
588
	}
589

    
590
	@Override
591
	public void setSnapshot(Serializable key, String role, Serializable snapshot) {
592
		this.key = key;
593
		this.role = role;
594
		this.storedSnapshot = snapshot;
595
	}
596

    
597
	@Override
598
	public void postAction() {
599
		operationQueue = null;
600
		cachedSize = -1;
601
		clearDirty();
602
	}
603

    
604
	@Override
605
	public Object getValue() {
606
		return this;
607
	}
608

    
609
	@Override
610
	public void beginRead() {
611
		// override on some subclasses
612
		initializing = true;
613
	}
614

    
615
	@Override
616
	public boolean endRead() {
617
		//override on some subclasses
618
		return afterInitialize();
619
	}
620

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

    
636
	/**
637
	 * Initialize the collection, if possible, wrapping any exceptions
638
	 * in a runtime exception
639
	 *
640
	 * @param writing currently obsolete
641
	 *
642
	 * @throws LazyInitializationException if we cannot initialize
643
	 */
644
	protected final void initialize(final boolean writing) {
645
		if ( initialized ) {
646
			return;
647
		}
648
	    // In remoting we are sure that session is null
649
	    // both when using property paths and switching off conversations
650
	    if(session == null && remoting) {
651
	        remoteInitialize();
652
	    } else { //keep formatting to ease update to newer hibernate version
653
		withTemporarySessionIfNeeded(
654
				new LazyInitializationWork<Object>() {
655
					@Override
656
					public Object doWork() {
657
						session.initializeCollection( AbstractPersistentCollection.this, writing );
658
						return null;
659
					}
660
				}
661
		);
662
	    }
663
	}
664

    
665
	private void throwLazyInitializationExceptionIfNotConnected() {
666
		if ( !isConnectedToSession() ) {
667
			throwLazyInitializationException( "no session or session was closed" );
668
		}
669
		if ( !session.isConnected() ) {
670
			throwLazyInitializationException( "session is disconnected" );
671
		}
672
	}
673

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

    
682
	protected final void setInitialized() {
683
		this.initializing = false;
684
		this.initialized = true;
685
	}
686

    
687
	protected final void setDirectlyAccessible(boolean directlyAccessible) {
688
		this.directlyAccessible = directlyAccessible;
689
	}
690

    
691
	@Override
692
	public boolean isDirectlyAccessible() {
693
		return directlyAccessible;
694
	}
695

    
696
	@Override
697
	public final boolean unsetSession(SessionImplementor currentSession) {
698
		prepareForPossibleLoadingOutsideTransaction();
699
		if ( currentSession == this.session ) {
700
			if ( !isTempSession ) {
701
				this.session = null;
702
			}
703
			return true;
704
		}
705
		else {
706
			if ( this.session != null ) {
707
				LOG.logCannotUnsetUnexpectedSessionInCollection( generateUnexpectedSessionStateMessage( currentSession ) );
708
			}
709
			return false;
710
		}
711
	}
712

    
713
	protected void prepareForPossibleLoadingOutsideTransaction() {
714
		if ( session != null ) {
715
			allowLoadOutsideTransaction = session.getFactory().getSessionFactoryOptions().isInitializeLazyStateOutsideTransactionsEnabled();
716

    
717
			if ( allowLoadOutsideTransaction && sessionFactoryUuid == null ) {
718
				sessionFactoryUuid = session.getFactory().getUuid();
719
			}
720
		}
721
	}
722

    
723
	@Override
724
	public final boolean setCurrentSession(SessionImplementor session) throws HibernateException {
725
		if ( session == this.session ) {
726
			return false;
727
		}
728
		else {
729
			if ( this.session != null ) {
730
				final String msg = generateUnexpectedSessionStateMessage( session );
731
				if ( isConnectedToSession() ) {
732
					throw new HibernateException(
733
							"Illegal attempt to associate a collection with two open sessions. " + msg
734
					);
735
				}
736
				else {
737
					LOG.logUnexpectedSessionInCollectionNotConnected( msg );
738
					this.session = session;
739
					return true;
740
				}
741
			}
742
			else {
743
				this.session = session;
744
				return true;
745
			}
746
		}
747
	}
748

    
749
	private String generateUnexpectedSessionStateMessage(SessionImplementor session) {
750
		// NOTE: If this.session != null, this.session may be operating on this collection
751
		// (e.g., by changing this.role, this.key, or even this.session) in a different thread.
752

    
753
		// Grab the current role and key (it can still get changed by this.session...)
754
		// If this collection is connected to this.session, then this.role and this.key should
755
		// be consistent with the CollectionEntry in this.session (as long as this.session doesn't
756
		// change it). Don't access the CollectionEntry in this.session because that could result
757
		// in multi-threaded access to this.session.
758
		final String roleCurrent = role;
759
		final Serializable keyCurrent = key;
760

    
761
		final StringBuilder sb = new StringBuilder( "Collection : " );
762
		if ( roleCurrent != null ) {
763
			sb.append( MessageHelper.collectionInfoString( roleCurrent, keyCurrent ) );
764
		}
765
		else {
766
			final CollectionEntry ce = session.getPersistenceContext().getCollectionEntry( this );
767
			if ( ce != null ) {
768
				sb.append(
769
						MessageHelper.collectionInfoString(
770
								ce.getLoadedPersister(),
771
								this,
772
								ce.getLoadedKey(),
773
								session
774
						)
775
				);
776
			}
777
			else {
778
				sb.append( "<unknown>" );
779
			}
780
		}
781
		// only include the collection contents if debug logging
782
		if ( LOG.isDebugEnabled() ) {
783
			final String collectionContents = wasInitialized() ? toString() : "<uninitialized>";
784
			sb.append( "\nCollection contents: [" ).append( collectionContents ).append( "]" );
785
		}
786
		return sb.toString();
787
	}
788

    
789
	@Override
790
	public boolean needsRecreate(CollectionPersister persister) {
791
		// Workaround for situations like HHH-7072.  If the collection element is a component that consists entirely
792
		// of nullable properties, we currently have to forcefully recreate the entire collection.  See the use
793
		// of hasNotNullableColumns in the AbstractCollectionPersister constructor for more info.  In order to delete
794
		// row-by-row, that would require SQL like "WHERE ( COL = ? OR ( COL is null AND ? is null ) )", rather than
795
		// the current "WHERE COL = ?" (fails for null for most DBs).  Note that
796
		// the param would have to be bound twice.  Until we eventually add "parameter bind points" concepts to the
797
		// AST in ORM 5+, handling this type of condition is either extremely difficult or impossible.  Forcing
798
		// recreation isn't ideal, but not really any other option in ORM 4.
799
		// Selecting a type used in where part of update statement
800
		// (must match condidion in org.hibernate.persister.collection.BasicCollectionPersister.doUpdateRows).
801
		// See HHH-9474
802
		Type whereType;
803
		if ( persister.hasIndex() ) {
804
			whereType = persister.getIndexType();
805
		}
806
		else {
807
			whereType = persister.getElementType();
808
		}
809
		if ( whereType instanceof CompositeType ) {
810
			CompositeType componentIndexType = (CompositeType) whereType;
811
			return !componentIndexType.hasNotNullProperty();
812
		}
813
		return false;
814
	}
815

    
816
	@Override
817
	public final void forceInitialization() throws HibernateException {
818
		if ( !initialized ) {
819
			if ( initializing ) {
820
				throw new AssertionFailure( "force initialize loading collection" );
821
			}
822
			initialize( false );
823
		}
824
	}
825

    
826

    
827
	/**
828
	 * Get the current snapshot from the session
829
	 */
830
	@SuppressWarnings({"JavaDoc"})
831
	protected final Serializable getSnapshot() {
832
		return session.getPersistenceContext().getSnapshot( this );
833
	}
834

    
835
	@Override
836
	public final boolean wasInitialized() {
837
		return initialized;
838
	}
839

    
840
	@Override
841
	public boolean isRowUpdatePossible() {
842
		return true;
843
	}
844

    
845
	@Override
846
	public final boolean hasQueuedOperations() {
847
		return operationQueue != null;
848
	}
849

    
850
	@Override
851
	public final Iterator queuedAdditionIterator() {
852
		if ( hasQueuedOperations() ) {
853
			return new Iterator() {
854
				private int index;
855

    
856
				@Override
857
				public Object next() {
858
					return operationQueue.get( index++ ).getAddedInstance();
859
				}
860

    
861
				@Override
862
				public boolean hasNext() {
863
					return index < operationQueue.size();
864
				}
865

    
866
				@Override
867
				public void remove() {
868
					throw new UnsupportedOperationException();
869
				}
870
			};
871
		}
872
		else {
873
			return EmptyIterator.INSTANCE;
874
		}
875
	}
876

    
877
	@Override
878
	@SuppressWarnings({"unchecked"})
879
	public final Collection getQueuedOrphans(String entityName) {
880
		if ( hasQueuedOperations() ) {
881
			final Collection additions = new ArrayList( operationQueue.size() );
882
			final Collection removals = new ArrayList( operationQueue.size() );
883
			for ( DelayedOperation operation : operationQueue ) {
884
				additions.add( operation.getAddedInstance() );
885
				removals.add( operation.getOrphan() );
886
			}
887
			return getOrphans( removals, additions, entityName, session );
888
		}
889
		else {
890
			return Collections.EMPTY_LIST;
891
		}
892
	}
893

    
894
	@Override
895
	public void preInsert(CollectionPersister persister) throws HibernateException {
896
	}
897

    
898
	@Override
899
	public void afterRowInsert(CollectionPersister persister, Object entry, int i) throws HibernateException {
900
	}
901

    
902
	@Override
903
	public abstract Collection getOrphans(Serializable snapshot, String entityName) throws HibernateException;
904

    
905
	/**
906
	 * Get the session currently associated with this collection.
907
	 *
908
	 * @return The session
909
	 */
910
	public final SessionImplementor getSession() {
911
		return session;
912
	}
913

    
914
	protected final class IteratorProxy implements Iterator {
915
		protected final Iterator itr;
916

    
917
		public IteratorProxy(Iterator itr) {
918
			this.itr = itr;
919
		}
920

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

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

    
931
		@Override
932
		public void remove() {
933
			write();
934
			itr.remove();
935
		}
936
	}
937

    
938
	protected final class ListIteratorProxy implements ListIterator {
939
		protected final ListIterator itr;
940

    
941
		public ListIteratorProxy(ListIterator itr) {
942
			this.itr = itr;
943
		}
944

    
945
		@Override
946
		@SuppressWarnings({"unchecked"})
947
		public void add(Object o) {
948
			write();
949
			itr.add( o );
950
		}
951

    
952
		@Override
953
		public boolean hasNext() {
954
			return itr.hasNext();
955
		}
956

    
957
		@Override
958
		public boolean hasPrevious() {
959
			return itr.hasPrevious();
960
		}
961

    
962
		@Override
963
		public Object next() {
964
			return itr.next();
965
		}
966

    
967
		@Override
968
		public int nextIndex() {
969
			return itr.nextIndex();
970
		}
971

    
972
		@Override
973
		public Object previous() {
974
			return itr.previous();
975
		}
976

    
977
		@Override
978
		public int previousIndex() {
979
			return itr.previousIndex();
980
		}
981

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

    
988
		@Override
989
		@SuppressWarnings({"unchecked"})
990
		public void set(Object o) {
991
			write();
992
			itr.set( o );
993
		}
994
	}
995

    
996
	protected class SetProxy implements java.util.Set {
997
		protected final Collection set;
998

    
999
		public SetProxy(Collection set) {
1000
			this.set = set;
1001
		}
1002

    
1003
		@Override
1004
		@SuppressWarnings({"unchecked"})
1005
		public boolean add(Object o) {
1006
			write();
1007
			return set.add( o );
1008
		}
1009

    
1010
		@Override
1011
		@SuppressWarnings({"unchecked"})
1012
		public boolean addAll(Collection c) {
1013
			write();
1014
			return set.addAll( c );
1015
		}
1016

    
1017
		@Override
1018
		public void clear() {
1019
			write();
1020
			set.clear();
1021
		}
1022

    
1023
		@Override
1024
		public boolean contains(Object o) {
1025
			return set.contains( o );
1026
		}
1027

    
1028
		@Override
1029
		@SuppressWarnings("unchecked")
1030
		public boolean containsAll(Collection c) {
1031
			return set.containsAll( c );
1032
		}
1033

    
1034
		@Override
1035
		public boolean isEmpty() {
1036
			return set.isEmpty();
1037
		}
1038

    
1039
		@Override
1040
		public Iterator iterator() {
1041
			return new IteratorProxy( set.iterator() );
1042
		}
1043

    
1044
		@Override
1045
		public boolean remove(Object o) {
1046
			write();
1047
			return set.remove( o );
1048
		}
1049

    
1050
		@Override
1051
		@SuppressWarnings("unchecked")
1052
		public boolean removeAll(Collection c) {
1053
			write();
1054
			return set.removeAll( c );
1055
		}
1056

    
1057
		@Override
1058
		@SuppressWarnings("unchecked")
1059
		public boolean retainAll(Collection c) {
1060
			write();
1061
			return set.retainAll( c );
1062
		}
1063

    
1064
		@Override
1065
		public int size() {
1066
			return set.size();
1067
		}
1068

    
1069
		@Override
1070
		public Object[] toArray() {
1071
			return set.toArray();
1072
		}
1073

    
1074
		@Override
1075
		@SuppressWarnings({"unchecked"})
1076
		public Object[] toArray(Object[] array) {
1077
			return set.toArray( array );
1078
		}
1079
	}
1080

    
1081
	protected final class ListProxy implements java.util.List {
1082
		protected final List list;
1083

    
1084
		public ListProxy(List list) {
1085
			this.list = list;
1086
		}
1087

    
1088
		@Override
1089
		@SuppressWarnings({"unchecked"})
1090
		public void add(int index, Object value) {
1091
			write();
1092
			list.add( index, value );
1093
		}
1094

    
1095
		@Override
1096
		@SuppressWarnings({"unchecked"})
1097
		public boolean add(Object o) {
1098
			write();
1099
			return list.add( o );
1100
		}
1101

    
1102
		@Override
1103
		@SuppressWarnings({"unchecked"})
1104
		public boolean addAll(Collection c) {
1105
			write();
1106
			return list.addAll( c );
1107
		}
1108

    
1109
		@Override
1110
		@SuppressWarnings({"unchecked"})
1111
		public boolean addAll(int i, Collection c) {
1112
			write();
1113
			return list.addAll( i, c );
1114
		}
1115

    
1116
		@Override
1117
		public void clear() {
1118
			write();
1119
			list.clear();
1120
		}
1121

    
1122
		@Override
1123
		public boolean contains(Object o) {
1124
			return list.contains( o );
1125
		}
1126

    
1127
		@Override
1128
		@SuppressWarnings("unchecked")
1129
		public boolean containsAll(Collection c) {
1130
			return list.containsAll( c );
1131
		}
1132

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

    
1138
		@Override
1139
		public int indexOf(Object o) {
1140
			return list.indexOf( o );
1141
		}
1142

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

    
1148
		@Override
1149
		public Iterator iterator() {
1150
			return new IteratorProxy( list.iterator() );
1151
		}
1152

    
1153
		@Override
1154
		public int lastIndexOf(Object o) {
1155
			return list.lastIndexOf( o );
1156
		}
1157

    
1158
		@Override
1159
		public ListIterator listIterator() {
1160
			return new ListIteratorProxy( list.listIterator() );
1161
		}
1162

    
1163
		@Override
1164
		public ListIterator listIterator(int i) {
1165
			return new ListIteratorProxy( list.listIterator( i ) );
1166
		}
1167

    
1168
		@Override
1169
		public Object remove(int i) {
1170
			write();
1171
			return list.remove( i );
1172
		}
1173

    
1174
		@Override
1175
		public boolean remove(Object o) {
1176
			write();
1177
			return list.remove( o );
1178
		}
1179

    
1180
		@Override
1181
		@SuppressWarnings("unchecked")
1182
		public boolean removeAll(Collection c) {
1183
			write();
1184
			return list.removeAll( c );
1185
		}
1186

    
1187
		@Override
1188
		@SuppressWarnings("unchecked")
1189
		public boolean retainAll(Collection c) {
1190
			write();
1191
			return list.retainAll( c );
1192
		}
1193

    
1194
		@Override
1195
		@SuppressWarnings({"unchecked"})
1196
		public Object set(int i, Object o) {
1197
			write();
1198
			return list.set( i, o );
1199
		}
1200

    
1201
		@Override
1202
		public int size() {
1203
			return list.size();
1204
		}
1205

    
1206
		@Override
1207
		public List subList(int i, int j) {
1208
			return list.subList( i, j );
1209
		}
1210

    
1211
		@Override
1212
		public Object[] toArray() {
1213
			return list.toArray();
1214
		}
1215

    
1216
		@Override
1217
		@SuppressWarnings({"unchecked"})
1218
		public Object[] toArray(Object[] array) {
1219
			return list.toArray( array );
1220
		}
1221

    
1222
	}
1223

    
1224
	/**
1225
	 * Contract for operations which are part of a collection's operation queue.
1226
	 */
1227
	protected interface DelayedOperation {
1228
		public void operate();
1229

    
1230
		public Object getAddedInstance();
1231

    
1232
		public Object getOrphan();
1233
	}
1234

    
1235
	protected interface ValueDelayedOperation extends DelayedOperation {
1236
		void replace(CollectionPersister collectionPersister, Map copyCache);
1237
	}
1238

    
1239
	protected abstract class AbstractValueDelayedOperation implements ValueDelayedOperation {
1240
		private Object addedValue;
1241
		private Object orphan;
1242

    
1243
		protected AbstractValueDelayedOperation(Object addedValue, Object orphan) {
1244
			this.addedValue = addedValue;
1245
			this.orphan = orphan;
1246
		}
1247

    
1248
		public void replace(CollectionPersister persister, Map copyCache) {
1249
			if ( addedValue != null ) {
1250
				addedValue = getReplacement( persister.getElementType(), addedValue, copyCache );
1251
			}
1252
		}
1253

    
1254
		protected final Object getReplacement(Type type, Object current, Map copyCache) {
1255
			return type.replace( current, null, session, owner, copyCache );
1256
		}
1257

    
1258
		@Override
1259
		public final Object getAddedInstance() {
1260
			return addedValue;
1261
		}
1262

    
1263
		@Override
1264
		public final Object getOrphan() {
1265
			return orphan;
1266
		}
1267
	}
1268

    
1269
	/**
1270
	 * Given a collection of entity instances that used to
1271
	 * belong to the collection, and a collection of instances
1272
	 * that currently belong, return a collection of orphans
1273
	 */
1274
	@SuppressWarnings({"JavaDoc", "unchecked"})
1275
	protected static Collection getOrphans(
1276
			Collection oldElements,
1277
			Collection currentElements,
1278
			String entityName,
1279
			SessionImplementor session) throws HibernateException {
1280

    
1281
		// short-circuit(s)
1282
		if ( currentElements.size() == 0 ) {
1283
			// no new elements, the old list contains only Orphans
1284
			return oldElements;
1285
		}
1286
		if ( oldElements.size() == 0 ) {
1287
			// no old elements, so no Orphans neither
1288
			return oldElements;
1289
		}
1290

    
1291
		final EntityPersister entityPersister = session.getFactory().getEntityPersister( entityName );
1292
		final Type idType = entityPersister.getIdentifierType();
1293
		final boolean useIdDirect = mayUseIdDirect( idType );
1294

    
1295
		// create the collection holding the Orphans
1296
		final Collection res = new ArrayList();
1297

    
1298
		// collect EntityIdentifier(s) of the *current* elements - add them into a HashSet for fast access
1299
		final java.util.Set currentIds = new HashSet();
1300
		final java.util.Set currentSaving = new IdentitySet();
1301
		for ( Object current : currentElements ) {
1302
			if ( current != null && ForeignKeys.isNotTransient( entityName, current, null, session ) ) {
1303
				final EntityEntry ee = session.getPersistenceContext().getEntry( current );
1304
				if ( ee != null && ee.getStatus() == Status.SAVING ) {
1305
					currentSaving.add( current );
1306
				}
1307
				else {
1308
					final Serializable currentId = ForeignKeys.getEntityIdentifierIfNotUnsaved(
1309
							entityName,
1310
							current,
1311
							session
1312
					);
1313
					currentIds.add( useIdDirect ? currentId : new TypedValue( idType, currentId ) );
1314
				}
1315
			}
1316
		}
1317

    
1318
		// iterate over the *old* list
1319
		for ( Object old : oldElements ) {
1320
			if ( !currentSaving.contains( old ) ) {
1321
				final Serializable oldId = ForeignKeys.getEntityIdentifierIfNotUnsaved( entityName, old, session );
1322
				if ( !currentIds.contains( useIdDirect ? oldId : new TypedValue( idType, oldId ) ) ) {
1323
					res.add( old );
1324
				}
1325
			}
1326
		}
1327

    
1328
		return res;
1329
	}
1330

    
1331
	private static boolean mayUseIdDirect(Type idType) {
1332
		return idType == StringType.INSTANCE
1333
			|| idType == IntegerType.INSTANCE
1334
			|| idType == LongType.INSTANCE
1335
			|| idType == UUIDBinaryType.INSTANCE
1336
			|| idType == UUIDCharType.INSTANCE
1337
			|| idType == PostgresUUIDType.INSTANCE;
1338
	}
1339

    
1340
	/**
1341
	 * Removes entity entries that have an equal identifier with the incoming entity instance
1342
	 *
1343
	 * @param list The list containing the entity instances
1344
	 * @param entityInstance The entity instance to match elements.
1345
	 * @param entityName The entity name
1346
	 * @param session The session
1347
	 */
1348
	public static void identityRemove(
1349
			Collection list,
1350
			Object entityInstance,
1351
			String entityName,
1352
			SessionImplementor session) {
1353

    
1354
		if ( entityInstance != null && ForeignKeys.isNotTransient( entityName, entityInstance, null, session ) ) {
1355
			final EntityPersister entityPersister = session.getFactory().getEntityPersister( entityName );
1356
			final Type idType = entityPersister.getIdentifierType();
1357

    
1358
			final Serializable idOfCurrent = ForeignKeys.getEntityIdentifierIfNotUnsaved( entityName, entityInstance, session );
1359
			final Iterator itr = list.iterator();
1360
			while ( itr.hasNext() ) {
1361
				final Serializable idOfOld = ForeignKeys.getEntityIdentifierIfNotUnsaved( entityName, itr.next(), session );
1362
				if ( idType.isEqual( idOfCurrent, idOfOld, session.getFactory() ) ) {
1363
					itr.remove();
1364
					break;
1365
				}
1366
			}
1367

    
1368
		}
1369
	}
1370

    
1371
	@Override
1372
	public Object getIdentifier(Object entry, int i) {
1373
		throw new UnsupportedOperationException();
1374
	}
1375

    
1376
	@Override
1377
	public Object getOwner() {
1378
		return owner;
1379
	}
1380

    
1381
	@Override
1382
	public void setOwner(Object owner) {
1383
		this.owner = owner;
1384
	}
1385

    
1386
/** #################################################################### 
1387
    
1388
    ADDED PART: Below is section of code which makes remote service calls 
1389
    
1390
    #################################################################### */
1391
    
1392
	
1393
	// The affected methods are :
1394
	// initialize(final boolean writing)
1395
	// readSize()
1396
	// readIndexExistence(final Object index)
1397
	// readElementExistence(final Object element)
1398
	// readElementByIndex(final Object index)
1399

    
1400
	private static CdmApplicationRemoteConfiguration configuration;
1401
	private static boolean remoting = false;
1402

    
1403
	public static void setConfiguration(CdmApplicationRemoteConfiguration conf) {
1404
	    remoting = true;
1405
		configuration = conf;
1406
	}
1407

    
1408
	private void remoteInitialize() {
1409

    
1410
		if (getOwner() != null && !initialized) {
1411

    
1412
			try {
1413
				String role = getRole();
1414
				String fieldName = role.substring(role.lastIndexOf(".") + 1);
1415
				LOG.info("--> Remote Lazy Initializing Collection " + getRole() + " , owner : " + getOwner().getClass() + "/" + getKey() + " , field : " + fieldName);
1416
				Object owner = getOwner();
1417
				CdmBase cdmBase;
1418
				if(owner instanceof CdmBase) {
1419
				    cdmBase = (CdmBase)owner;
1420
				} else {
1421
				    throw new HibernateException("Owner of persistent collection is not a cdm entity");
1422
				}
1423
				if(configuration == null) {
1424
					throw new HibernateException("CdmApplicationRemoteConfiguration not initialized (null)");
1425
				}
1426
				ICachedCommonService cachedCommonService = configuration.getCachedCommonService();
1427
				if(cachedCommonService == null) {
1428
					throw new HibernateException("commonService not initialized (null)");
1429
				}
1430

    
1431
				//Object obj = ProxyUtils.deproxyIfInitialized(cachedCommonService.initializeCollection(this));
1432
				Object obj = ProxyUtils.deproxyIfInitialized(cachedCommonService.initializeCollection(cdmBase.getUuid(), fieldName));
1433
				if(ProxyUtils.isUninitializedProxy(obj)) {
1434
				    throw new HibernateException("Persistent Collection initialized but is still a proxy");
1435
				}
1436
				afterInitialize();
1437

    
1438
				Class<?> clazz = getClass();
1439
				if (clazz != null) {
1440
					//CollectionField cf = cachedCommonService.getCollectionField(col);
1441
					//cachedCommonService.updatePersistentCollection(cf);
1442
				    Object collectionType = ProxyUtils.getCollectionType(obj, clazz);
1443
					Field field = clazz.getDeclaredField(collectionType.toString());
1444
					field.setAccessible(true);
1445
					field.set(this, obj);
1446
					ProxyUtils.setRoleValueInOwner(owner, role, obj);
1447

    
1448
				}
1449
			} catch (Exception ex) {
1450
				throw new CdmEagerLoadingException(ex);
1451
			}
1452
		}
1453
	}
1454
}
    (1-1/1)