Project

General

Profile

Download (38.3 KB) Statistics
| Branch: | Tag: | Revision:
1

    
2

    
3
/*
4
 * Hibernate, Relational Persistence for Idiomatic Java
5
 *
6
 * Copyright (c) 2008-2011, Red Hat Inc. or third-party contributors as
7
 * indicated by the @author tags or express copyright attribution
8
 * statements applied by the authors.  All third-party contributions are
9
 * distributed under license by Red Hat Inc.
10
 *
11
 * This copyrighted material is made available to anyone wishing to use, modify,
12
 * copy, or redistribute it subject to the terms and conditions of the GNU
13
 * Lesser General Public License, as published by the Free Software Foundation.
14
 *
15
 * This program is distributed in the hope that it will be useful,
16
 * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
17
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
18
 * for more details.
19
 *
20
 * You should have received a copy of the GNU Lesser General Public License
21
 * along with this distribution; if not, write to:
22
 * Free Software Foundation, Inc.
23
 * 51 Franklin Street, Fifth Floor
24
 * Boston, MA  02110-1301  USA
25
 */
26
package org.hibernate.collection.internal;
27

    
28
import java.io.Serializable;
29
import java.lang.reflect.Field;
30
import java.util.ArrayList;
31
import java.util.Collection;
32
import java.util.Collections;
33
import java.util.HashSet;
34
import java.util.Iterator;
35
import java.util.List;
36
import java.util.ListIterator;
37
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
				Boolean extraLazyExistenceCheck = withTemporarySessionIfNeeded(
356
						new LazyInitializationWork<Boolean>() {
357
							@Override
358
							public Boolean doWork() {
359
								CollectionEntry entry = session.getPersistenceContext().getCollectionEntry( AbstractPersistentCollection.this );
360
								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
				Boolean extraLazyExistenceCheck = withTemporarySessionIfNeeded(
392
						new LazyInitializationWork<Boolean>() {
393
							@Override
394
							public Boolean doWork() {
395
								CollectionEntry entry = session.getPersistenceContext().getCollectionEntry( AbstractPersistentCollection.this );
396
								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
						CollectionEntry entry = session.getPersistenceContext().getCollectionEntry( AbstractPersistentCollection.this );
436
						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
				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

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

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

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

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

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

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

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

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

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

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

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

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

    
801

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1197
	}
1198

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

    
1205
		public Object getAddedInstance();
1206

    
1207
		public Object getOrphan();
1208
	}
1209

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1303
		return res;
1304
	}
1305

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

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

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

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

    
1343
		}
1344
	}
1345

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

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

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

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

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

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

    
1383
	private void remoteInitialize() {
1384

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

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

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

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

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