Project

General

Profile

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

    
2

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

    
28
import java.io.Serializable;
29
import java.lang.reflect.Field;
30
import java.util.ArrayList;
31
import java.util.Collection;
32
import java.util.Collections;
33
import java.util.HashSet;
34
import java.util.Iterator;
35
import java.util.List;
36
import java.util.ListIterator;
37

    
38
import javax.naming.NamingException;
39

    
40
import org.hibernate.AssertionFailure;
41
import org.hibernate.HibernateException;
42
import org.hibernate.LazyInitializationException;
43
import org.hibernate.Session;
44
import org.hibernate.collection.spi.PersistentCollection;
45
import org.hibernate.engine.internal.ForeignKeys;
46
import org.hibernate.engine.spi.CollectionEntry;
47
import org.hibernate.engine.spi.EntityEntry;
48
import org.hibernate.engine.spi.SessionFactoryImplementor;
49
import org.hibernate.engine.spi.SessionImplementor;
50
import org.hibernate.engine.spi.Status;
51
import org.hibernate.engine.spi.TypedValue;
52
import org.hibernate.internal.SessionFactoryRegistry;
53
import org.hibernate.internal.util.MarkerObject;
54
import org.hibernate.internal.util.collections.EmptyIterator;
55
import org.hibernate.internal.util.collections.IdentitySet;
56
import org.hibernate.persister.collection.CollectionPersister;
57
import org.hibernate.persister.entity.EntityPersister;
58
import org.hibernate.pretty.MessageHelper;
59
import org.hibernate.type.Type;
60
import org.jboss.logging.Logger;
61

    
62
import eu.etaxonomy.cdm.api.application.CdmApplicationRemoteConfiguration;
63
import eu.etaxonomy.cdm.cache.ProxyUtils;
64
import eu.etaxonomy.cdm.model.common.CdmBase;
65
import eu.etaxonomy.taxeditor.remoting.CdmEagerLoadingException;
66
import eu.etaxonomy.taxeditor.service.ICachedCommonService;
67

    
68
/**
69
 * Base class implementing {@link org.hibernate.collection.spi.PersistentCollection}
70
 *
71
 * This a extended copy of the original class from hibernate. It has been extended to
72
 * allow making remote service calls to spring httpinvoker services (see section at the bottom
73
 * of this class).
74
 *
75
 *
76
 * @author Gavin King
77
 * @author Cherian Mathew
78
 */
79
public abstract class AbstractPersistentCollection implements Serializable, PersistentCollection {
80
	private static final Logger log = Logger.getLogger( AbstractPersistentCollection.class );
81

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

    
102
	private transient SessionImplementor session;
103
	private boolean initialized;
104
	private transient List<DelayedOperation> operationQueue;
105
	private transient boolean directlyAccessible;
106
	private transient boolean initializing;
107
	private Object owner;
108
	private int cachedSize = -1;
109

    
110
	private String role;
111
	private Serializable key;
112
	// collections detect changes made via their public interface and mark
113
	// themselves as dirty as a performance optimization
114
	private boolean dirty;
115
	private Serializable storedSnapshot;
116

    
117
	private String sessionFactoryUuid;
118
	private boolean specjLazyLoad = false;
119

    
120
	@Override
121
    public final String getRole() {
122
		return role;
123
	}
124

    
125
	@Override
126
    public final Serializable getKey() {
127
		return key;
128
	}
129

    
130
	@Override
131
    public final boolean isUnreferenced() {
132
		return role == null;
133
	}
134

    
135
	@Override
136
    public final boolean isDirty() {
137
		return dirty;
138
	}
139

    
140
	@Override
141
    public final void clearDirty() {
142
		dirty = false;
143
	}
144

    
145
	@Override
146
    public final void dirty() {
147
		dirty = true;
148
	}
149

    
150
	@Override
151
    public final Serializable getStoredSnapshot() {
152
		return storedSnapshot;
153
	}
154

    
155
	//Careful: these methods do not initialize the collection.
156

    
157
	/**
158
	 * Is the initialized collection empty?
159
	 */
160
	@Override
161
    public abstract boolean empty();
162

    
163
	/**
164
	 * Called by any read-only method of the collection interface
165
	 */
166
	protected final void read() {
167
		initialize( false );
168
	}
169

    
170
	/**
171
	 * Called by the {@link Collection#size} method
172
	 */
173
	@SuppressWarnings({"JavaDoc"})
174
	protected boolean readSize() {
175
		if ( !initialized ) {
176
			if ( cachedSize != -1 && !hasQueuedOperations() ) {
177
				return true;
178
			}
179
			else {
180
				// In remoting we are sure that session is null
181
				// both when using property paths and switching off conversations
182
				if(session == null && remoting) {
183
					log.info("--> readSize, of " + getRole() + " with key " + getKey());
184
					read();
185
				} else {
186
					boolean isExtraLazy = withTemporarySessionIfNeeded(
187
							new LazyInitializationWork<Boolean>() {
188
								@Override
189
								public Boolean doWork() {
190
									CollectionEntry entry = session.getPersistenceContext().getCollectionEntry( AbstractPersistentCollection.this );
191

    
192
									if ( entry != null ) {
193
										CollectionPersister persister = entry.getLoadedPersister();
194
										if ( persister.isExtraLazy() ) {
195
											if ( hasQueuedOperations() ) {
196
												session.flush();
197
											}
198
											cachedSize = persister.getSize( entry.getLoadedKey(), session );
199
											return true;
200
										}
201
										else {
202
											read();
203
										}
204
									}
205
									else{
206
										throwLazyInitializationExceptionIfNotConnected();
207
									}
208
									return false;
209
								}
210
							}
211
							);
212
					if ( isExtraLazy ) {
213
						return true;
214
					}
215
				}
216
			}
217
		}
218
		return false;
219
	}
220

    
221
	public static interface LazyInitializationWork<T> {
222
		public T doWork();
223
	}
224

    
225
	private <T> T withTemporarySessionIfNeeded(LazyInitializationWork<T> lazyInitializationWork) {
226
		SessionImplementor originalSession = null;
227
		boolean isTempSession = false;
228
		boolean isJTA = false;
229

    
230
		if ( session == null ) {
231
			if ( specjLazyLoad ) {
232
				session = openTemporarySessionForLoading();
233
				isTempSession = true;
234
			}
235
			else {
236
				throwLazyInitializationException( "could not initialize proxy - no Session" );
237
			}
238
		}
239
		else if ( !session.isOpen() ) {
240
			if ( specjLazyLoad ) {
241
				originalSession = session;
242
				session = openTemporarySessionForLoading();
243
				isTempSession = true;
244
			}
245
			else {
246
				throwLazyInitializationException( "could not initialize proxy - the owning Session was closed" );
247
			}
248
		}
249
		else if ( !session.isConnected() ) {
250
			if ( specjLazyLoad ) {
251
				originalSession = session;
252
				session = openTemporarySessionForLoading();
253
				isTempSession = true;
254
			}
255
			else {
256
				throwLazyInitializationException( "could not initialize proxy - the owning Session is disconnected" );
257
			}
258
		}
259

    
260
		if ( isTempSession ) {
261
			// TODO: On the next major release, add an
262
			// 'isJTA' or 'getTransactionFactory' method to Session.
263
			/*isJTA = session.getTransactionCoordinator()
264
					.getTransactionContext().getTransactionEnvironment()
265
					.getTransactionFactory()
266
					.compatibleWithJtaSynchronization();*/
267
			isJTA = session.getTransactionCoordinator().getTransactionCoordinatorBuilder().isJta();
268
			if ( !isJTA ) {
269
				// Explicitly handle the transactions only if we're not in
270
				// a JTA environment.  A lazy loading temporary session can
271
				// be created even if a current session and transaction are
272
				// open (ex: session.clear() was used).  We must prevent
273
				// multiple transactions.
274
				( ( Session) session ).beginTransaction();
275
			}
276

    
277
			session.getPersistenceContext().addUninitializedDetachedCollection(
278
					session.getFactory().getCollectionPersister( getRole() ),
279
					this
280
			);
281
		}
282

    
283
		try {
284
			return lazyInitializationWork.doWork();
285
		}
286
		finally {
287
			if ( isTempSession ) {
288
				// make sure the just opened temp session gets closed!
289
				try {
290
					if ( !isJTA ) {
291
						( ( Session) session ).getTransaction().commit();
292
					}
293
					( (Session) session ).close();
294
				}
295
				catch (Exception e) {
296
					log.warn( "Unable to close temporary session used to load lazy collection associated to no session" );
297
				}
298
				session = originalSession;
299
			}
300
		}
301
	}
302

    
303
	private SessionImplementor openTemporarySessionForLoading() {
304
		if ( sessionFactoryUuid == null ) {
305
			throwLazyInitializationException( "SessionFactory UUID not known to create temporary Session for loading" );
306
		}
307

    
308
		SessionFactoryImplementor sf = (SessionFactoryImplementor)
309
				SessionFactoryRegistry.INSTANCE.getSessionFactory( sessionFactoryUuid );
310
		return (SessionImplementor) sf.openSession();
311
	}
312

    
313
	protected Boolean readIndexExistence(final Object index) {
314
		if ( !initialized ) {
315
			// In remoting we are sure that session is null
316
			// both when using property paths and switching off conversations
317
			if(session == null && remoting) {
318
				log.info("--> readIndexExistence, of " + getRole() + " with key " + getKey());
319
				read();
320
			} else {
321
				Boolean extraLazyExistenceCheck = withTemporarySessionIfNeeded(
322
						new LazyInitializationWork<Boolean>() {
323
							@Override
324
							public Boolean doWork() {
325
								CollectionEntry entry = session.getPersistenceContext().getCollectionEntry( AbstractPersistentCollection.this );
326
								CollectionPersister persister = entry.getLoadedPersister();
327
								if ( persister.isExtraLazy() ) {
328
									if ( hasQueuedOperations() ) {
329
										session.flush();
330
									}
331
									return persister.indexExists( entry.getLoadedKey(), index, session );
332
								}
333
								else {
334
									read();
335
								}
336
								return null;
337
							}
338
						}
339
						);
340
				if ( extraLazyExistenceCheck != null ) {
341
					return extraLazyExistenceCheck;
342
				}
343
			}
344
		}
345
		return null;
346
	}
347

    
348
	protected Boolean readElementExistence(final Object element) {
349
		if ( !initialized ) {
350
			// In remoting we are sure that session is null
351
			// both when using property paths and switching off conversations
352
			if(session == null && remoting) {
353
				log.info("--> readElementExistence, of " + getRole() + " with key " + getKey());
354
				read();
355

    
356
			} else {
357
				Boolean extraLazyExistenceCheck = withTemporarySessionIfNeeded(
358
						new LazyInitializationWork<Boolean>() {
359
							@Override
360
							public Boolean doWork() {
361
								CollectionEntry entry = session.getPersistenceContext().getCollectionEntry( AbstractPersistentCollection.this );
362
								CollectionPersister persister = entry.getLoadedPersister();
363
								if ( persister.isExtraLazy() ) {
364
									if ( hasQueuedOperations() ) {
365
										session.flush();
366
									}
367
									return persister.elementExists( entry.getLoadedKey(), element, session );
368
								}
369
								else {
370
									read();
371
								}
372
								return null;
373
							}
374
						}
375
						);
376
				if ( extraLazyExistenceCheck != null ) {
377
					return extraLazyExistenceCheck;
378
				}
379
			}
380
		}
381
		return null;
382
	}
383

    
384
	protected static final Object UNKNOWN = new MarkerObject( "UNKNOWN" );
385

    
386
	protected Object readElementByIndex(final Object index) {
387
		if ( !initialized ) {
388
			// In remoting we are sure that session is null
389
			// both when using property paths and switching off conversations
390
			if(session == null && remoting) {
391
				log.info("--> readElementByIndex, of " + getRole() + " with key " + getKey());
392
				read();
393

    
394
			} else {
395
				class ExtraLazyElementByIndexReader implements LazyInitializationWork {
396
					private boolean isExtraLazy;
397
					private Object element;
398

    
399
					@Override
400
					public Object doWork() {
401
						CollectionEntry entry = session.getPersistenceContext().getCollectionEntry( AbstractPersistentCollection.this );
402
						CollectionPersister persister = entry.getLoadedPersister();
403
						isExtraLazy = persister.isExtraLazy();
404
						if ( isExtraLazy ) {
405
							if ( hasQueuedOperations() ) {
406
								session.flush();
407
							}
408
							element = persister.getElementByIndex( entry.getLoadedKey(), index, session, owner );
409
						}
410
						else {
411
							read();
412
						}
413
						return null;
414
					}
415
				}
416

    
417
				ExtraLazyElementByIndexReader reader = new ExtraLazyElementByIndexReader();
418
				//noinspection unchecked
419
				withTemporarySessionIfNeeded( reader );
420
				if ( reader.isExtraLazy ) {
421
					return reader.element;
422
				}
423
			}
424
		}
425
		return UNKNOWN;
426

    
427
	}
428

    
429
	protected int getCachedSize() {
430
		return cachedSize;
431
	}
432

    
433
	private boolean isConnectedToSession() {
434
		return session != null &&
435
				session.isOpen() &&
436
				session.getPersistenceContext().containsCollection( this );
437
	}
438

    
439
	/**
440
	 * Called by any writer method of the collection interface
441
	 */
442
	protected final void write() {
443
		initialize( true );
444
		dirty();
445
	}
446

    
447
	/**
448
	 * Is this collection in a state that would allow us to
449
	 * "queue" operations?
450
	 */
451
	@SuppressWarnings({"JavaDoc"})
452
	protected boolean isOperationQueueEnabled() {
453
		return !initialized &&
454
				isConnectedToSession() &&
455
				isInverseCollection();
456
	}
457

    
458
	/**
459
	 * Is this collection in a state that would allow us to
460
	 * "queue" puts? This is a special case, because of orphan
461
	 * delete.
462
	 */
463
	@SuppressWarnings({"JavaDoc"})
464
	protected boolean isPutQueueEnabled() {
465
		return !initialized &&
466
				isConnectedToSession() &&
467
				isInverseOneToManyOrNoOrphanDelete();
468
	}
469

    
470
	/**
471
	 * Is this collection in a state that would allow us to
472
	 * "queue" clear? This is a special case, because of orphan
473
	 * delete.
474
	 */
475
	@SuppressWarnings({"JavaDoc"})
476
	protected boolean isClearQueueEnabled() {
477
		return !initialized &&
478
				isConnectedToSession() &&
479
				isInverseCollectionNoOrphanDelete();
480
	}
481

    
482
	/**
483
	 * Is this the "inverse" end of a bidirectional association?
484
	 */
485
	@SuppressWarnings({"JavaDoc"})
486
	private boolean isInverseCollection() {
487
		CollectionEntry ce = session.getPersistenceContext().getCollectionEntry( this );
488
		return ce != null && ce.getLoadedPersister().isInverse();
489
	}
490

    
491
	/**
492
	 * Is this the "inverse" end of a bidirectional association with
493
	 * no orphan delete enabled?
494
	 */
495
	@SuppressWarnings({"JavaDoc"})
496
	private boolean isInverseCollectionNoOrphanDelete() {
497
		CollectionEntry ce = session.getPersistenceContext().getCollectionEntry( this );
498
		return ce != null &&
499
				ce.getLoadedPersister().isInverse() &&
500
				!ce.getLoadedPersister().hasOrphanDelete();
501
	}
502

    
503
	/**
504
	 * Is this the "inverse" end of a bidirectional one-to-many, or
505
	 * of a collection with no orphan delete?
506
	 */
507
	@SuppressWarnings({"JavaDoc"})
508
	private boolean isInverseOneToManyOrNoOrphanDelete() {
509
		CollectionEntry ce = session.getPersistenceContext().getCollectionEntry( this );
510
		return ce != null && ce.getLoadedPersister().isInverse() && (
511
				ce.getLoadedPersister().isOneToMany() ||
512
						!ce.getLoadedPersister().hasOrphanDelete()
513
		);
514
	}
515

    
516
	/**
517
	 * Queue an addition
518
	 */
519
	@SuppressWarnings({"JavaDoc"})
520
	protected final void queueOperation(DelayedOperation operation) {
521
		if ( operationQueue == null ) {
522
			operationQueue = new ArrayList<DelayedOperation>( 10 );
523
		}
524
		operationQueue.add( operation );
525
		dirty = true; //needed so that we remove this collection from the second-level cache
526
	}
527

    
528
	/**
529
	 * After reading all existing elements from the database,
530
	 * add the queued elements to the underlying collection.
531
	 */
532
	protected final void performQueuedOperations() {
533
		for ( DelayedOperation operation : operationQueue ) {
534
			operation.operate();
535
		}
536
	}
537

    
538
	/**
539
	 * After flushing, re-init snapshot state.
540
	 */
541
	@Override
542
    public void setSnapshot(Serializable key, String role, Serializable snapshot) {
543
		this.key = key;
544
		this.role = role;
545
		this.storedSnapshot = snapshot;
546
	}
547

    
548
	/**
549
	 * After flushing, clear any "queued" additions, since the
550
	 * database state is now synchronized with the memory state.
551
	 */
552
	@Override
553
    public void postAction() {
554
		operationQueue = null;
555
		cachedSize = -1;
556
		clearDirty();
557
	}
558

    
559
	/**
560
	 * Not called by Hibernate, but used by non-JDK serialization,
561
	 * eg. SOAP libraries.
562
	 */
563
	public AbstractPersistentCollection() {
564
	}
565

    
566
	protected AbstractPersistentCollection(SessionImplementor session) {
567
		this.session = session;
568
	}
569

    
570
	/**
571
	 * return the user-visible collection (or array) instance
572
	 */
573
	@Override
574
    public Object getValue() {
575
		return this;
576
	}
577

    
578
	/**
579
	 * Called just before reading any rows from the JDBC result set
580
	 */
581
	@Override
582
    public void beginRead() {
583
		// override on some subclasses
584
		initializing = true;
585
	}
586

    
587
	/**
588
	 * Called after reading all rows from the JDBC result set
589
	 */
590
	@Override
591
    public boolean endRead() {
592
		//override on some subclasses
593
		return afterInitialize();
594
	}
595

    
596
	@Override
597
    public boolean afterInitialize() {
598
		setInitialized();
599
		//do this bit after setting initialized to true or it will recurse
600
		if ( operationQueue != null ) {
601
			performQueuedOperations();
602
			operationQueue = null;
603
			cachedSize = -1;
604
			return false;
605
		}
606
		else {
607
			return true;
608
		}
609
	}
610

    
611
	/**
612
	 * Initialize the collection, if possible, wrapping any exceptions
613
	 * in a runtime exception
614
	 *
615
	 * @param writing currently obsolete
616
	 *
617
	 * @throws LazyInitializationException if we cannot initialize
618
	 */
619
	protected final void initialize(final boolean writing) {
620
	    if ( initialized ) {
621
	        return;
622
	    }
623

    
624
	    // In remoting we are sure that session is null
625
	    // both when using property paths and switching off conversations
626
	    if(session == null && remoting) {
627
	        remoteInitialize();
628
	    } else {
629
	        withTemporarySessionIfNeeded(
630
	                new LazyInitializationWork<Object>() {
631
	                    @Override
632
	                    public Object doWork() {
633
	                        session.initializeCollection( AbstractPersistentCollection.this, writing );
634
	                        return null;
635
	                    }
636
	                }
637
	                );
638
	    }
639
	}
640

    
641
	private void throwLazyInitializationExceptionIfNotConnected() {
642
		if ( !isConnectedToSession() ) {
643
			throwLazyInitializationException( "no session or session was closed" );
644
		}
645
		if ( !session.isConnected() ) {
646
			throwLazyInitializationException( "session is disconnected" );
647
		}
648
	}
649

    
650
	private void throwLazyInitializationException(String message) {
651
		throw new LazyInitializationException(
652
				"failed to lazily initialize a collection" +
653
						(role == null ? "" : " of role: " + role) +
654
						", " + message
655
		);
656
	}
657

    
658
	protected final void setInitialized() {
659
		this.initializing = false;
660
		this.initialized = true;
661
	}
662

    
663
	protected final void setDirectlyAccessible(boolean directlyAccessible) {
664
		this.directlyAccessible = directlyAccessible;
665
	}
666

    
667
	/**
668
	 * Could the application possibly have a direct reference to
669
	 * the underlying collection implementation?
670
	 */
671
	@Override
672
    public boolean isDirectlyAccessible() {
673
		return directlyAccessible;
674
	}
675

    
676
	/**
677
	 * Disassociate this collection from the given session.
678
	 *
679
	 * @return true if this was currently associated with the given session
680
	 */
681
	@Override
682
    public final boolean unsetSession(SessionImplementor currentSession) {
683
		prepareForPossibleSpecialSpecjInitialization();
684
		if ( currentSession == this.session ) {
685
			this.session = null;
686
			return true;
687
		}
688
		else {
689
			return false;
690
		}
691
	}
692

    
693
	protected void prepareForPossibleSpecialSpecjInitialization() {
694
		if ( session != null ) {
695
			specjLazyLoad = session.getFactory().getSettings().isInitializeLazyStateOutsideTransactionsEnabled();
696

    
697
			if ( specjLazyLoad && sessionFactoryUuid == null ) {
698
				try {
699
					sessionFactoryUuid = (String) session.getFactory().getReference().get( "uuid" ).getContent();
700
				}
701
				catch (NamingException e) {
702
					//not much we can do if this fails...
703
				}
704
			}
705
		}
706
	}
707

    
708

    
709
	/**
710
	 * Associate the collection with the given session.
711
	 *
712
	 * @return false if the collection was already associated with the session
713
	 *
714
	 * @throws HibernateException if the collection was already associated
715
	 * with another open session
716
	 */
717
	@Override
718
    public final boolean setCurrentSession(SessionImplementor session) throws HibernateException {
719
		if ( session == this.session ) {
720
			return false;
721
		}
722
		else {
723
			if ( isConnectedToSession() ) {
724
				CollectionEntry ce = session.getPersistenceContext().getCollectionEntry( this );
725
				if ( ce == null ) {
726
					throw new HibernateException(
727
							"Illegal attempt to associate a collection with two open sessions"
728
					);
729
				}
730
				else {
731
					throw new HibernateException(
732
							"Illegal attempt to associate a collection with two open sessions: " +
733
									MessageHelper.collectionInfoString(
734
											ce.getLoadedPersister(), this,
735
											ce.getLoadedKey(), session
736
									)
737
					);
738
				}
739
			}
740
			else {
741
				this.session = session;
742
				return true;
743
			}
744
		}
745
	}
746

    
747
	/**
748
	 * Do we need to completely recreate this collection when it changes?
749
	 */
750
	@Override
751
    public boolean needsRecreate(CollectionPersister persister) {
752
		return false;
753
	}
754

    
755
	/**
756
	 * To be called internally by the session, forcing
757
	 * immediate initialization.
758
	 */
759
	@Override
760
    public final void forceInitialization() throws HibernateException {
761
		if ( !initialized ) {
762
			if ( initializing ) {
763
				throw new AssertionFailure( "force initialize loading collection" );
764
			}
765
			if ( session == null ) {
766
				throw new HibernateException( "collection is not associated with any session" );
767
			}
768
			if ( !session.isConnected() ) {
769
				throw new HibernateException( "disconnected session" );
770
			}
771
			session.initializeCollection( this, false );
772
		}
773
	}
774

    
775

    
776
	/**
777
	 * Get the current snapshot from the session
778
	 */
779
	@SuppressWarnings({"JavaDoc"})
780
	protected final Serializable getSnapshot() {
781
		return session.getPersistenceContext().getSnapshot( this );
782
	}
783

    
784
	/**
785
	 * Is this instance initialized?
786
	 */
787
	@Override
788
    public final boolean wasInitialized() {
789
		return initialized;
790
	}
791

    
792
	@Override
793
    public boolean isRowUpdatePossible() {
794
		return true;
795
	}
796

    
797
	/**
798
	 * Does this instance have any "queued" additions?
799
	 */
800
	@Override
801
    public final boolean hasQueuedOperations() {
802
		return operationQueue != null;
803
	}
804

    
805
	/**
806
	 * Iterate the "queued" additions
807
	 */
808
	@Override
809
    public final Iterator queuedAdditionIterator() {
810
		if ( hasQueuedOperations() ) {
811
			return new Iterator() {
812
				int i = 0;
813

    
814
				@Override
815
                public Object next() {
816
					return operationQueue.get( i++ ).getAddedInstance();
817
				}
818

    
819
				@Override
820
                public boolean hasNext() {
821
					return i < operationQueue.size();
822
				}
823

    
824
				@Override
825
                public void remove() {
826
					throw new UnsupportedOperationException();
827
				}
828
			};
829
		}
830
		else {
831
			return EmptyIterator.INSTANCE;
832
		}
833
	}
834

    
835
	/**
836
	 * Iterate the "queued" additions
837
	 */
838
	@Override
839
    @SuppressWarnings({"unchecked"})
840
	public final Collection getQueuedOrphans(String entityName) {
841
		if ( hasQueuedOperations() ) {
842
			Collection additions = new ArrayList( operationQueue.size() );
843
			Collection removals = new ArrayList( operationQueue.size() );
844
			for ( DelayedOperation operation : operationQueue ) {
845
				additions.add( operation.getAddedInstance() );
846
				removals.add( operation.getOrphan() );
847
			}
848
			return getOrphans( removals, additions, entityName, session );
849
		}
850
		else {
851
			return Collections.EMPTY_LIST;
852
		}
853
	}
854

    
855
	/**
856
	 * Called before inserting rows, to ensure that any surrogate keys
857
	 * are fully generated
858
	 */
859
	@Override
860
    public void preInsert(CollectionPersister persister) throws HibernateException {
861
	}
862

    
863
	/**
864
	 * Called after inserting a row, to fetch the natively generated id
865
	 */
866
	@Override
867
    public void afterRowInsert(CollectionPersister persister, Object entry, int i) throws HibernateException {
868
	}
869

    
870
	/**
871
	 * get all "orphaned" elements
872
	 */
873
	@Override
874
    public abstract Collection getOrphans(Serializable snapshot, String entityName) throws HibernateException;
875

    
876
	/**
877
	 * Get the current session
878
	 */
879
	@SuppressWarnings({"JavaDoc"})
880
	public final SessionImplementor getSession() {
881
		return session;
882
	}
883

    
884
	protected final class IteratorProxy implements Iterator {
885
		protected final Iterator itr;
886

    
887
		public IteratorProxy(Iterator itr) {
888
			this.itr = itr;
889
		}
890

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

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

    
901
		@Override
902
        public void remove() {
903
			write();
904
			itr.remove();
905
		}
906

    
907
	}
908

    
909
	protected final class ListIteratorProxy implements ListIterator {
910
		protected final ListIterator itr;
911

    
912
		public ListIteratorProxy(ListIterator itr) {
913
			this.itr = itr;
914
		}
915

    
916
		@Override
917
        @SuppressWarnings({"unchecked"})
918
		public void add(Object o) {
919
			write();
920
			itr.add( o );
921
		}
922

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

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

    
933
		@Override
934
        public Object next() {
935
			return itr.next();
936
		}
937

    
938
		@Override
939
        public int nextIndex() {
940
			return itr.nextIndex();
941
		}
942

    
943
		@Override
944
        public Object previous() {
945
			return itr.previous();
946
		}
947

    
948
		@Override
949
        public int previousIndex() {
950
			return itr.previousIndex();
951
		}
952

    
953
		@Override
954
        public void remove() {
955
			write();
956
			itr.remove();
957
		}
958

    
959
		@Override
960
        @SuppressWarnings({"unchecked"})
961
		public void set(Object o) {
962
			write();
963
			itr.set( o );
964
		}
965

    
966
	}
967

    
968
	protected class SetProxy implements java.util.Set {
969
		protected final Collection set;
970

    
971
		public SetProxy(Collection set) {
972
			this.set = set;
973
		}
974

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

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

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

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

    
1000
		@Override
1001
        public boolean containsAll(Collection c) {
1002
			return set.containsAll( c );
1003
		}
1004

    
1005
		@Override
1006
        public boolean isEmpty() {
1007
			return set.isEmpty();
1008
		}
1009

    
1010
		@Override
1011
        public Iterator iterator() {
1012
			return new IteratorProxy( set.iterator() );
1013
		}
1014

    
1015
		@Override
1016
        public boolean remove(Object o) {
1017
			write();
1018
			return set.remove( o );
1019
		}
1020

    
1021
		@Override
1022
        public boolean removeAll(Collection c) {
1023
			write();
1024
			return set.removeAll( c );
1025
		}
1026

    
1027
		@Override
1028
        public boolean retainAll(Collection c) {
1029
			write();
1030
			return set.retainAll( c );
1031
		}
1032

    
1033
		@Override
1034
        public int size() {
1035
			return set.size();
1036
		}
1037

    
1038
		@Override
1039
        public Object[] toArray() {
1040
			return set.toArray();
1041
		}
1042

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

    
1049
	}
1050

    
1051
	protected final class ListProxy implements java.util.List {
1052
		protected final List list;
1053

    
1054
		public ListProxy(List list) {
1055
			this.list = list;
1056
		}
1057

    
1058
		@Override
1059
		@SuppressWarnings({"unchecked"})
1060
		public void add(int index, Object value) {
1061
			write();
1062
			list.add( index, value );
1063
		}
1064

    
1065
		@Override
1066
		@SuppressWarnings({"unchecked"})
1067
		public boolean add(Object o) {
1068
			write();
1069
			return list.add( o );
1070
		}
1071

    
1072
		@Override
1073
		@SuppressWarnings({"unchecked"})
1074
		public boolean addAll(Collection c) {
1075
			write();
1076
			return list.addAll( c );
1077
		}
1078

    
1079
		@Override
1080
		@SuppressWarnings({"unchecked"})
1081
		public boolean addAll(int i, Collection c) {
1082
			write();
1083
			return list.addAll( i, c );
1084
		}
1085

    
1086
		@Override
1087
		public void clear() {
1088
			write();
1089
			list.clear();
1090
		}
1091

    
1092
		@Override
1093
		public boolean contains(Object o) {
1094
			return list.contains( o );
1095
		}
1096

    
1097
		@Override
1098
		public boolean containsAll(Collection c) {
1099
			return list.containsAll( c );
1100
		}
1101

    
1102
		@Override
1103
		public Object get(int i) {
1104
			return list.get( i );
1105
		}
1106

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

    
1112
		@Override
1113
		public boolean isEmpty() {
1114
			return list.isEmpty();
1115
		}
1116

    
1117
		@Override
1118
		public Iterator iterator() {
1119
			return new IteratorProxy( list.iterator() );
1120
		}
1121

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

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

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

    
1137
		@Override
1138
		public Object remove(int i) {
1139
			write();
1140
			return list.remove( i );
1141
		}
1142

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

    
1149
		@Override
1150
		public boolean removeAll(Collection c) {
1151
			write();
1152
			return list.removeAll( c );
1153
		}
1154

    
1155
		@Override
1156
		public boolean retainAll(Collection c) {
1157
			write();
1158
			return list.retainAll( c );
1159
		}
1160

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

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

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

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

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

    
1189
	}
1190

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

    
1197
		public Object getAddedInstance();
1198

    
1199
		public Object getOrphan();
1200
	}
1201

    
1202
	/**
1203
	 * Given a collection of entity instances that used to
1204
	 * belong to the collection, and a collection of instances
1205
	 * that currently belong, return a collection of orphans
1206
	 */
1207
	@SuppressWarnings({"JavaDoc", "unchecked"})
1208
	protected static Collection getOrphans(
1209
			Collection oldElements,
1210
			Collection currentElements,
1211
			String entityName,
1212
			SessionImplementor session) throws HibernateException {
1213

    
1214
		// short-circuit(s)
1215
		if ( currentElements.size() == 0 ) {
1216
			return oldElements; // no new elements, the old list contains only Orphans
1217
		}
1218
		if ( oldElements.size() == 0 ) {
1219
			return oldElements; // no old elements, so no Orphans neither
1220
		}
1221

    
1222
		final EntityPersister entityPersister = session.getFactory().getEntityPersister( entityName );
1223
		final Type idType = entityPersister.getIdentifierType();
1224

    
1225
		// create the collection holding the Orphans
1226
		Collection res = new ArrayList();
1227

    
1228
		// collect EntityIdentifier(s) of the *current* elements - add them into a HashSet for fast access
1229
		java.util.Set currentIds = new HashSet();
1230
		java.util.Set currentSaving = new IdentitySet();
1231
		for ( Object current : currentElements ) {
1232
			if ( current != null && ForeignKeys.isNotTransient( entityName, current, null, session ) ) {
1233
				EntityEntry ee = session.getPersistenceContext().getEntry( current );
1234
				if ( ee != null && ee.getStatus() == Status.SAVING ) {
1235
					currentSaving.add( current );
1236
				}
1237
				else {
1238
					Serializable currentId = ForeignKeys.getEntityIdentifierIfNotUnsaved(
1239
							entityName,
1240
							current,
1241
							session
1242
					);
1243
					currentIds.add( new TypedValue( idType, currentId, entityPersister.getEntityMode() ) );
1244
				}
1245
			}
1246
		}
1247

    
1248
		// iterate over the *old* list
1249
		for ( Object old : oldElements ) {
1250
			if ( !currentSaving.contains( old ) ) {
1251
				Serializable oldId = ForeignKeys.getEntityIdentifierIfNotUnsaved( entityName, old, session );
1252
				if ( !currentIds.contains( new TypedValue( idType, oldId, entityPersister.getEntityMode() ) ) ) {
1253
					res.add( old );
1254
				}
1255
			}
1256
		}
1257

    
1258
		return res;
1259
	}
1260

    
1261
	public static void identityRemove(
1262
			Collection list,
1263
			Object object,
1264
			String entityName,
1265
			SessionImplementor session) throws HibernateException {
1266

    
1267
		if ( object != null && ForeignKeys.isNotTransient( entityName, object, null, session ) ) {
1268
			final EntityPersister entityPersister = session.getFactory().getEntityPersister( entityName );
1269
			Type idType = entityPersister.getIdentifierType();
1270

    
1271
			Serializable idOfCurrent = ForeignKeys.getEntityIdentifierIfNotUnsaved( entityName, object, session );
1272
			Iterator itr = list.iterator();
1273
			while ( itr.hasNext() ) {
1274
				Serializable idOfOld = ForeignKeys.getEntityIdentifierIfNotUnsaved( entityName, itr.next(), session );
1275
				if ( idType.isEqual( idOfCurrent, idOfOld, session.getFactory() ) ) {
1276
					itr.remove();
1277
					break;
1278
				}
1279
			}
1280

    
1281
		}
1282
	}
1283

    
1284
	@Override
1285
    public Object getIdentifier(Object entry, int i) {
1286
		throw new UnsupportedOperationException();
1287
	}
1288

    
1289
	@Override
1290
    public Object getOwner() {
1291
		return owner;
1292
	}
1293

    
1294
	@Override
1295
    public void setOwner(Object owner) {
1296
		this.owner = owner;
1297
	}
1298

    
1299
	/** ------ Below is section of code which makes remote service calls ----- */
1300
	// The affected methods are :
1301
	// initialize(final boolean writing)
1302
	// readSize()
1303
	// readIndexExistence(final Object index)
1304
	// readElementExistence(final Object element)
1305
	// readElementByIndex(final Object index)
1306

    
1307
	private static CdmApplicationRemoteConfiguration configuration;
1308
	private static boolean remoting = false;
1309

    
1310
	public static void setConfiguration(CdmApplicationRemoteConfiguration conf) {
1311
	    remoting = true;
1312
		configuration = conf;
1313
	}
1314

    
1315

    
1316
	private void remoteInitialize() {
1317

    
1318
		if (getOwner() != null && !initialized) {
1319

    
1320
			try {
1321
				String role = getRole();
1322
				String fieldName = role.substring(role.lastIndexOf(".") + 1);
1323
				log.info("--> Remote Lazy Initializing Collection " + getRole() + " , owner : " + getOwner().getClass() + "/" + getKey() + " , field : " + fieldName);
1324
				Object owner = getOwner();
1325
				CdmBase cdmBase;
1326
				if(owner instanceof CdmBase) {
1327
				    cdmBase = (CdmBase)owner;
1328
				} else {
1329
				    throw new HibernateException("Owner of persistent collection is not a cdm entity");
1330
				}
1331
				if(configuration == null) {
1332
					throw new HibernateException("CdmApplicationRemoteConfiguration not initialized (null)");
1333
				}
1334
				ICachedCommonService cachedCommonService = configuration.getCachedCommonService();
1335
				if(cachedCommonService == null) {
1336
					throw new HibernateException("commonService not initialized (null)");
1337
				}
1338

    
1339
				//Object obj = ProxyUtils.deproxyIfInitialized(cachedCommonService.initializeCollection(this));
1340
				Object obj = ProxyUtils.deproxyIfInitialized(cachedCommonService.initializeCollection(cdmBase.getUuid(), fieldName));
1341
				if(ProxyUtils.isUninitializedProxy(obj)) {
1342
				    throw new HibernateException("Persistent Collection initialized but is still a proxy");
1343
				}
1344
				afterInitialize();
1345

    
1346
				Class<?> clazz = getClass();
1347
				if (clazz != null) {
1348
					//CollectionField cf = cachedCommonService.getCollectionField(col);
1349
					//cachedCommonService.updatePersistentCollection(cf);
1350
				    Object collectionType = ProxyUtils.getCollectionType(obj, clazz);
1351
					Field field = clazz.getDeclaredField(collectionType.toString());
1352
					field.setAccessible(true);
1353
					field.set(this, obj);
1354
					ProxyUtils.setRoleValueInOwner(owner, role, obj);
1355

    
1356
				}
1357
			} catch (Exception ex) {
1358
				throw new CdmEagerLoadingException(ex);
1359
			}
1360
		}
1361
	}
1362

    
1363

    
1364

    
1365
}
1366

    
    (1-1/1)