Project

General

Profile

Download (38.2 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 initialized;
119
	private transient List<DelayedOperation> operationQueue;
120
	private transient boolean directlyAccessible;
121
	private transient boolean initializing;
122
	private Object owner;
123
	private int cachedSize = -1;
124

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

    
132
	private String sessionFactoryUuid;
133
	private boolean specjLazyLoad = false;
134

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
258
	private <T> T withTemporarySessionIfNeeded(LazyInitializationWork<T> lazyInitializationWork) {
259
		SessionImplementor originalSession = null;
260
		boolean isTempSession = false;
261
		boolean isJTA = false;
262

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

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

    
311
			session.getPersistenceContext().addUninitializedDetachedCollection(
312
					session.getFactory().getCollectionPersister( getRole() ),
313
					this
314
			);
315
		}
316

    
317
		try {
318
			return lazyInitializationWork.doWork();
319
		}
320
		finally {
321
			if ( isTempSession ) {
322
				// make sure the just opened temp session gets closed!
323
				try {
324
					if ( !isJTA ) {
325
						( ( Session) session ).getTransaction().commit();
326
					}
327
					( (Session) session ).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
				session = originalSession;
333
			}
334
		}
335
	}
336

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

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

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

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

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

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

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

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

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

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

    
461
	}
462

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
697
	@Override
698
	public final boolean unsetSession(SessionImplementor currentSession) {
699
		prepareForPossibleLoadingOutsideTransaction();
700
		if ( currentSession == this.session ) {
701
			this.session = null;
702
			return true;
703
		}
704
		else {
705
			return false;
706
		}
707
	}
708

    
709
	protected void prepareForPossibleLoadingOutsideTransaction() {
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
	@Override
725
	public final boolean setCurrentSession(SessionImplementor session) throws HibernateException {
726
		if ( session == this.session ) {
727
			return false;
728
		}
729
		else {
730
			if ( isConnectedToSession() ) {
731
				CollectionEntry ce = session.getPersistenceContext().getCollectionEntry( this );
732
				if ( ce == null ) {
733
					throw new HibernateException(
734
							"Illegal attempt to associate a collection with two open sessions"
735
					);
736
				}
737
				else {
738
					throw new HibernateException(
739
							"Illegal attempt to associate a collection with two open sessions: " +
740
									MessageHelper.collectionInfoString(
741
											ce.getLoadedPersister(), this,
742
											ce.getLoadedKey(), session
743
									)
744
					);
745
				}
746
			}
747
			else {
748
				this.session = session;
749
				return true;
750
			}
751
		}
752
	}
753

    
754
	/**
755
	 * Do we need to completely recreate this collection when it changes?
756
	 */
757
	@Override
758
	public boolean needsRecreate(CollectionPersister persister) {
759
		// Workaround for situations like HHH-7072.  If the collection element is a component that consists entirely
760
		// of nullable properties, we currently have to forcefully recreate the entire collection.  See the use
761
		// of hasNotNullableColumns in the AbstractCollectionPersister constructor for more info.  In order to delete
762
		// row-by-row, that would require SQL like "WHERE ( COL = ? OR ( COL is null AND ? is null ) )", rather than
763
		// the current "WHERE COL = ?" (fails for null for most DBs).  Note that
764
		// the param would have to be bound twice.  Until we eventually add "parameter bind points" concepts to the
765
		// AST in ORM 5+, handling this type of condition is either extremely difficult or impossible.  Forcing
766
		// recreation isn't ideal, but not really any other option in ORM 4.
767
		// Selecting a type used in where part of update statement
768
		// (must match condidion in org.hibernate.persister.collection.BasicCollectionPersister.doUpdateRows).
769
		// See HHH-9474
770
		Type whereType;
771
		if ( persister.hasIndex() ) {
772
			whereType = persister.getIndexType();
773
		}
774
		else {
775
			whereType = persister.getElementType();
776
		}
777
		if ( whereType instanceof CompositeType ) {
778
			CompositeType componentIndexType = (CompositeType) whereType;
779
			return !componentIndexType.hasNotNullProperty();
780
		}
781
		return false;
782
	}
783

    
784
	@Override
785
	public final void forceInitialization() throws HibernateException {
786
		if ( !initialized ) {
787
			if ( initializing ) {
788
				throw new AssertionFailure( "force initialize loading collection" );
789
			}
790
			if ( session == null ) {
791
				throw new HibernateException( "collection is not associated with any session" );
792
			}
793
			if ( !session.isConnected() ) {
794
				throw new HibernateException( "disconnected session" );
795
			}
796
			session.initializeCollection( this, false );
797
		}
798
	}
799

    
800

    
801
	/**
802
	 * Get the current snapshot from the session
803
	 */
804
	@SuppressWarnings({"JavaDoc"})
805
	protected final Serializable getSnapshot() {
806
		return session.getPersistenceContext().getSnapshot( this );
807
	}
808

    
809
	@Override
810
	public final boolean wasInitialized() {
811
		return initialized;
812
	}
813

    
814
	@Override
815
	public boolean isRowUpdatePossible() {
816
		return true;
817
	}
818

    
819
	@Override
820
	public final boolean hasQueuedOperations() {
821
		return operationQueue != null;
822
	}
823

    
824
	@Override
825
	public final Iterator queuedAdditionIterator() {
826
		if ( hasQueuedOperations() ) {
827
			return new Iterator() {
828
				private int index;
829

    
830
				@Override
831
				public Object next() {
832
					return operationQueue.get( index++ ).getAddedInstance();
833
				}
834

    
835
				@Override
836
				public boolean hasNext() {
837
					return index < operationQueue.size();
838
				}
839

    
840
				@Override
841
				public void remove() {
842
					throw new UnsupportedOperationException();
843
				}
844
			};
845
		}
846
		else {
847
			return EmptyIterator.INSTANCE;
848
		}
849
	}
850

    
851
	@Override
852
	@SuppressWarnings({"unchecked"})
853
	public final Collection getQueuedOrphans(String entityName) {
854
		if ( hasQueuedOperations() ) {
855
			final Collection additions = new ArrayList( operationQueue.size() );
856
			final Collection removals = new ArrayList( operationQueue.size() );
857
			for ( DelayedOperation operation : operationQueue ) {
858
				additions.add( operation.getAddedInstance() );
859
				removals.add( operation.getOrphan() );
860
			}
861
			return getOrphans( removals, additions, entityName, session );
862
		}
863
		else {
864
			return Collections.EMPTY_LIST;
865
		}
866
	}
867

    
868
	@Override
869
	public void preInsert(CollectionPersister persister) throws HibernateException {
870
	}
871

    
872
	@Override
873
	public void afterRowInsert(CollectionPersister persister, Object entry, int i) throws HibernateException {
874
	}
875

    
876
	@Override
877
	public abstract Collection getOrphans(Serializable snapshot, String entityName) throws HibernateException;
878

    
879
	/**
880
	 * Get the session currently associated with this collection.
881
	 *
882
	 * @return The session
883
	 */
884
	public final SessionImplementor getSession() {
885
		return session;
886
	}
887

    
888
	protected final class IteratorProxy implements Iterator {
889
		protected final Iterator itr;
890

    
891
		public IteratorProxy(Iterator itr) {
892
			this.itr = itr;
893
		}
894

    
895
		@Override
896
		public boolean hasNext() {
897
			return itr.hasNext();
898
		}
899

    
900
		@Override
901
		public Object next() {
902
			return itr.next();
903
		}
904

    
905
		@Override
906
		public void remove() {
907
			write();
908
			itr.remove();
909
		}
910
	}
911

    
912
	protected final class ListIteratorProxy implements ListIterator {
913
		protected final ListIterator itr;
914

    
915
		public ListIteratorProxy(ListIterator itr) {
916
			this.itr = itr;
917
		}
918

    
919
		@Override
920
		@SuppressWarnings({"unchecked"})
921
		public void add(Object o) {
922
			write();
923
			itr.add( o );
924
		}
925

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

    
931
		@Override
932
		public boolean hasPrevious() {
933
			return itr.hasPrevious();
934
		}
935

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

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

    
946
		@Override
947
		public Object previous() {
948
			return itr.previous();
949
		}
950

    
951
		@Override
952
		public int previousIndex() {
953
			return itr.previousIndex();
954
		}
955

    
956
		@Override
957
		public void remove() {
958
			write();
959
			itr.remove();
960
		}
961

    
962
		@Override
963
		@SuppressWarnings({"unchecked"})
964
		public void set(Object o) {
965
			write();
966
			itr.set( o );
967
		}
968
	}
969

    
970
	protected class SetProxy implements java.util.Set {
971
		protected final Collection set;
972

    
973
		public SetProxy(Collection set) {
974
			this.set = set;
975
		}
976

    
977
		@Override
978
		@SuppressWarnings({"unchecked"})
979
		public boolean add(Object o) {
980
			write();
981
			return set.add( o );
982
		}
983

    
984
		@Override
985
		@SuppressWarnings({"unchecked"})
986
		public boolean addAll(Collection c) {
987
			write();
988
			return set.addAll( c );
989
		}
990

    
991
		@Override
992
		public void clear() {
993
			write();
994
			set.clear();
995
		}
996

    
997
		@Override
998
		public boolean contains(Object o) {
999
			return set.contains( o );
1000
		}
1001

    
1002
		@Override
1003
		@SuppressWarnings("unchecked")
1004
		public boolean containsAll(Collection c) {
1005
			return set.containsAll( c );
1006
		}
1007

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

    
1013
		@Override
1014
		public Iterator iterator() {
1015
			return new IteratorProxy( set.iterator() );
1016
		}
1017

    
1018
		@Override
1019
		public boolean remove(Object o) {
1020
			write();
1021
			return set.remove( o );
1022
		}
1023

    
1024
		@Override
1025
		@SuppressWarnings("unchecked")
1026
		public boolean removeAll(Collection c) {
1027
			write();
1028
			return set.removeAll( c );
1029
		}
1030

    
1031
		@Override
1032
		@SuppressWarnings("unchecked")
1033
		public boolean retainAll(Collection c) {
1034
			write();
1035
			return set.retainAll( c );
1036
		}
1037

    
1038
		@Override
1039
		public int size() {
1040
			return set.size();
1041
		}
1042

    
1043
		@Override
1044
		public Object[] toArray() {
1045
			return set.toArray();
1046
		}
1047

    
1048
		@Override
1049
		@SuppressWarnings({"unchecked"})
1050
		public Object[] toArray(Object[] array) {
1051
			return set.toArray( array );
1052
		}
1053
	}
1054

    
1055
	protected final class ListProxy implements java.util.List {
1056
		protected final List list;
1057

    
1058
		public ListProxy(List list) {
1059
			this.list = list;
1060
		}
1061

    
1062
		@Override
1063
		@SuppressWarnings({"unchecked"})
1064
		public void add(int index, Object value) {
1065
			write();
1066
			list.add( index, value );
1067
		}
1068

    
1069
		@Override
1070
		@SuppressWarnings({"unchecked"})
1071
		public boolean add(Object o) {
1072
			write();
1073
			return list.add( o );
1074
		}
1075

    
1076
		@Override
1077
		@SuppressWarnings({"unchecked"})
1078
		public boolean addAll(Collection c) {
1079
			write();
1080
			return list.addAll( c );
1081
		}
1082

    
1083
		@Override
1084
		@SuppressWarnings({"unchecked"})
1085
		public boolean addAll(int i, Collection c) {
1086
			write();
1087
			return list.addAll( i, c );
1088
		}
1089

    
1090
		@Override
1091
		public void clear() {
1092
			write();
1093
			list.clear();
1094
		}
1095

    
1096
		@Override
1097
		public boolean contains(Object o) {
1098
			return list.contains( o );
1099
		}
1100

    
1101
		@Override
1102
		@SuppressWarnings("unchecked")
1103
		public boolean containsAll(Collection c) {
1104
			return list.containsAll( c );
1105
		}
1106

    
1107
		@Override
1108
		public Object get(int i) {
1109
			return list.get( i );
1110
		}
1111

    
1112
		@Override
1113
		public int indexOf(Object o) {
1114
			return list.indexOf( o );
1115
		}
1116

    
1117
		@Override
1118
		public boolean isEmpty() {
1119
			return list.isEmpty();
1120
		}
1121

    
1122
		@Override
1123
		public Iterator iterator() {
1124
			return new IteratorProxy( list.iterator() );
1125
		}
1126

    
1127
		@Override
1128
		public int lastIndexOf(Object o) {
1129
			return list.lastIndexOf( o );
1130
		}
1131

    
1132
		@Override
1133
		public ListIterator listIterator() {
1134
			return new ListIteratorProxy( list.listIterator() );
1135
		}
1136

    
1137
		@Override
1138
		public ListIterator listIterator(int i) {
1139
			return new ListIteratorProxy( list.listIterator( i ) );
1140
		}
1141

    
1142
		@Override
1143
		public Object remove(int i) {
1144
			write();
1145
			return list.remove( i );
1146
		}
1147

    
1148
		@Override
1149
		public boolean remove(Object o) {
1150
			write();
1151
			return list.remove( o );
1152
		}
1153

    
1154
		@Override
1155
		@SuppressWarnings("unchecked")
1156
		public boolean removeAll(Collection c) {
1157
			write();
1158
			return list.removeAll( c );
1159
		}
1160

    
1161
		@Override
1162
		@SuppressWarnings("unchecked")
1163
		public boolean retainAll(Collection c) {
1164
			write();
1165
			return list.retainAll( c );
1166
		}
1167

    
1168
		@Override
1169
		@SuppressWarnings({"unchecked"})
1170
		public Object set(int i, Object o) {
1171
			write();
1172
			return list.set( i, o );
1173
		}
1174

    
1175
		@Override
1176
		public int size() {
1177
			return list.size();
1178
		}
1179

    
1180
		@Override
1181
		public List subList(int i, int j) {
1182
			return list.subList( i, j );
1183
		}
1184

    
1185
		@Override
1186
		public Object[] toArray() {
1187
			return list.toArray();
1188
		}
1189

    
1190
		@Override
1191
		@SuppressWarnings({"unchecked"})
1192
		public Object[] toArray(Object[] array) {
1193
			return list.toArray( array );
1194
		}
1195

    
1196
	}
1197

    
1198
	/**
1199
	 * Contract for operations which are part of a collection's operation queue.
1200
	 */
1201
	protected interface DelayedOperation {
1202
		public void operate();
1203

    
1204
		public Object getAddedInstance();
1205

    
1206
		public Object getOrphan();
1207
	}
1208

    
1209
	protected interface ValueDelayedOperation extends DelayedOperation {
1210
		void replace(CollectionPersister collectionPersister, Map copyCache);
1211
	}
1212

    
1213
	protected abstract class AbstractValueDelayedOperation implements ValueDelayedOperation {
1214
		private Object addedValue;
1215
		private Object orphan;
1216

    
1217
		protected AbstractValueDelayedOperation(Object addedValue, Object orphan) {
1218
			this.addedValue = addedValue;
1219
			this.orphan = orphan;
1220
		}
1221

    
1222
		public void replace(CollectionPersister persister, Map copyCache) {
1223
			if ( addedValue != null ) {
1224
				addedValue = getReplacement( persister.getElementType(), addedValue, copyCache );
1225
			}
1226
		}
1227

    
1228
		protected final Object getReplacement(Type type, Object current, Map copyCache) {
1229
			return type.replace( current, null, session, owner, copyCache );
1230
		}
1231

    
1232
		@Override
1233
		public final Object getAddedInstance() {
1234
			return addedValue;
1235
		}
1236

    
1237
		@Override
1238
		public final Object getOrphan() {
1239
			return orphan;
1240
		}
1241
	}
1242

    
1243
	/**
1244
	 * Given a collection of entity instances that used to
1245
	 * belong to the collection, and a collection of instances
1246
	 * that currently belong, return a collection of orphans
1247
	 */
1248
	@SuppressWarnings({"JavaDoc", "unchecked"})
1249
	protected static Collection getOrphans(
1250
			Collection oldElements,
1251
			Collection currentElements,
1252
			String entityName,
1253
			SessionImplementor session) throws HibernateException {
1254

    
1255
		// short-circuit(s)
1256
		if ( currentElements.size() == 0 ) {
1257
			// no new elements, the old list contains only Orphans
1258
			return oldElements;
1259
		}
1260
		if ( oldElements.size() == 0 ) {
1261
			// no old elements, so no Orphans neither
1262
			return oldElements;
1263
		}
1264

    
1265
		final EntityPersister entityPersister = session.getFactory().getEntityPersister( entityName );
1266
		final Type idType = entityPersister.getIdentifierType();
1267
		final boolean useIdDirect = mayUseIdDirect( idType );
1268

    
1269
		// create the collection holding the Orphans
1270
		final Collection res = new ArrayList();
1271

    
1272
		// collect EntityIdentifier(s) of the *current* elements - add them into a HashSet for fast access
1273
		final java.util.Set currentIds = new HashSet();
1274
		final java.util.Set currentSaving = new IdentitySet();
1275
		for ( Object current : currentElements ) {
1276
			if ( current != null && ForeignKeys.isNotTransient( entityName, current, null, session ) ) {
1277
				final EntityEntry ee = session.getPersistenceContext().getEntry( current );
1278
				if ( ee != null && ee.getStatus() == Status.SAVING ) {
1279
					currentSaving.add( current );
1280
				}
1281
				else {
1282
					final Serializable currentId = ForeignKeys.getEntityIdentifierIfNotUnsaved(
1283
							entityName,
1284
							current,
1285
							session
1286
					);
1287
					currentIds.add( useIdDirect ? currentId : new TypedValue( idType, currentId ) );
1288
				}
1289
			}
1290
		}
1291

    
1292
		// iterate over the *old* list
1293
		for ( Object old : oldElements ) {
1294
			if ( !currentSaving.contains( old ) ) {
1295
				final Serializable oldId = ForeignKeys.getEntityIdentifierIfNotUnsaved( entityName, old, session );
1296
				if ( !currentIds.contains( useIdDirect ? oldId : new TypedValue( idType, oldId ) ) ) {
1297
					res.add( old );
1298
				}
1299
			}
1300
		}
1301

    
1302
		return res;
1303
	}
1304

    
1305
	private static boolean mayUseIdDirect(Type idType) {
1306
		return idType == StringType.INSTANCE
1307
			|| idType == IntegerType.INSTANCE
1308
			|| idType == LongType.INSTANCE
1309
			|| idType == UUIDBinaryType.INSTANCE
1310
			|| idType == UUIDCharType.INSTANCE
1311
			|| idType == PostgresUUIDType.INSTANCE;
1312
	}
1313

    
1314
	/**
1315
	 * Removes entity entries that have an equal identifier with the incoming entity instance
1316
	 *
1317
	 * @param list The list containing the entity instances
1318
	 * @param entityInstance The entity instance to match elements.
1319
	 * @param entityName The entity name
1320
	 * @param session The session
1321
	 */
1322
	public static void identityRemove(
1323
			Collection list,
1324
			Object entityInstance,
1325
			String entityName,
1326
			SessionImplementor session) {
1327

    
1328
		if ( entityInstance != null && ForeignKeys.isNotTransient( entityName, entityInstance, null, session ) ) {
1329
			final EntityPersister entityPersister = session.getFactory().getEntityPersister( entityName );
1330
			final Type idType = entityPersister.getIdentifierType();
1331

    
1332
			final Serializable idOfCurrent = ForeignKeys.getEntityIdentifierIfNotUnsaved( entityName, entityInstance, session );
1333
			final Iterator itr = list.iterator();
1334
			while ( itr.hasNext() ) {
1335
				final Serializable idOfOld = ForeignKeys.getEntityIdentifierIfNotUnsaved( entityName, itr.next(), session );
1336
				if ( idType.isEqual( idOfCurrent, idOfOld, session.getFactory() ) ) {
1337
					itr.remove();
1338
					break;
1339
				}
1340
			}
1341

    
1342
		}
1343
	}
1344

    
1345
	@Override
1346
	public Object getIdentifier(Object entry, int i) {
1347
		throw new UnsupportedOperationException();
1348
	}
1349

    
1350
	@Override
1351
	public Object getOwner() {
1352
		return owner;
1353
	}
1354

    
1355
	@Override
1356
	public void setOwner(Object owner) {
1357
		this.owner = owner;
1358
	}
1359

    
1360
/** #################################################################### 
1361
    
1362
    ADDED PART: Below is section of code which makes remote service calls 
1363
    
1364
    #################################################################### */
1365
    
1366
	
1367
	// The affected methods are :
1368
	// initialize(final boolean writing)
1369
	// readSize()
1370
	// readIndexExistence(final Object index)
1371
	// readElementExistence(final Object element)
1372
	// readElementByIndex(final Object index)
1373

    
1374
	private static CdmApplicationRemoteConfiguration configuration;
1375
	private static boolean remoting = false;
1376

    
1377
	public static void setConfiguration(CdmApplicationRemoteConfiguration conf) {
1378
	    remoting = true;
1379
		configuration = conf;
1380
	}
1381

    
1382
	private void remoteInitialize() {
1383

    
1384
		if (getOwner() != null && !initialized) {
1385

    
1386
			try {
1387
				String role = getRole();
1388
				String fieldName = role.substring(role.lastIndexOf(".") + 1);
1389
				LOG.info("--> Remote Lazy Initializing Collection " + getRole() + " , owner : " + getOwner().getClass() + "/" + getKey() + " , field : " + fieldName);
1390
				Object owner = getOwner();
1391
				CdmBase cdmBase;
1392
				if(owner instanceof CdmBase) {
1393
				    cdmBase = (CdmBase)owner;
1394
				} else {
1395
				    throw new HibernateException("Owner of persistent collection is not a cdm entity");
1396
				}
1397
				if(configuration == null) {
1398
					throw new HibernateException("CdmApplicationRemoteConfiguration not initialized (null)");
1399
				}
1400
				ICachedCommonService cachedCommonService = configuration.getCachedCommonService();
1401
				if(cachedCommonService == null) {
1402
					throw new HibernateException("commonService not initialized (null)");
1403
				}
1404

    
1405
				//Object obj = ProxyUtils.deproxyIfInitialized(cachedCommonService.initializeCollection(this));
1406
				Object obj = ProxyUtils.deproxyIfInitialized(cachedCommonService.initializeCollection(cdmBase.getUuid(), fieldName));
1407
				if(ProxyUtils.isUninitializedProxy(obj)) {
1408
				    throw new HibernateException("Persistent Collection initialized but is still a proxy");
1409
				}
1410
				afterInitialize();
1411

    
1412
				Class<?> clazz = getClass();
1413
				if (clazz != null) {
1414
					//CollectionField cf = cachedCommonService.getCollectionField(col);
1415
					//cachedCommonService.updatePersistentCollection(cf);
1416
				    Object collectionType = ProxyUtils.getCollectionType(obj, clazz);
1417
					Field field = clazz.getDeclaredField(collectionType.toString());
1418
					field.setAccessible(true);
1419
					field.set(this, obj);
1420
					ProxyUtils.setRoleValueInOwner(owner, role, obj);
1421

    
1422
				}
1423
			} catch (Exception ex) {
1424
				throw new CdmEagerLoadingException(ex);
1425
			}
1426
		}
1427
	}
1428
}
    (1-1/1)