Please wait. This can take some minutes ...
                    
                 
             
         
        
            
                
                    
                    
                        Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. 
                        
                        Project price only 1 $ 
                        
                            You can buy this project and download/modify it how often you want.
                        
                        
                        
                     
                 
             
         
             
    
    
    org.hibernate.engine.internal.EntityEntryContext Maven / Gradle / Ivy 
    
/*
 * Hibernate, Relational Persistence for Idiomatic Java
 *
 * License: GNU Lesser General Public License (LGPL), version 2.1 or later.
 * See the lgpl.txt file in the root directory or .
 */
package org.hibernate.engine.internal;
import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
import org.hibernate.engine.spi.EntityEntry;
import org.hibernate.engine.spi.ManagedEntity;
import org.hibernate.engine.spi.PersistenceContext;
import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.IdentityHashMap;
import java.util.Map;
/**
 * Defines a context for maintaining the relation between an entity associated with the Session ultimately owning this
 * EntityEntryContext instance and that entity's corresponding EntityEntry.  2 approaches are supported:
 *     
 *         the entity->EntityEntry association is maintained in a Map within this class
 *      
 *     
 *         the EntityEntry is injected into the entity via it implementing the {@link org.hibernate.engine.spi.ManagedEntity} contract,
 *         either directly or through bytecode enhancement.
 *      
 *  
 * 
 *
 * @author Steve Ebersole
 */
public class EntityEntryContext {
	private static final CoreMessageLogger log = CoreLogging.messageLogger( EntityEntryContext.class );
	private transient PersistenceContext persistenceContext;
	private transient IdentityHashMap immutableManagedEntityXref;
	private transient ManagedEntity head;
	private transient ManagedEntity tail;
	private transient int count;
	private transient IdentityHashMap nonEnhancedEntityXref;
	@SuppressWarnings( {"unchecked"})
	private transient Map.Entry[] reentrantSafeEntries = new Map.Entry[0];
	private transient boolean dirty;
	/**
	 * Constructs a EntityEntryContext
	 */
	public EntityEntryContext(PersistenceContext persistenceContext) {
		this.persistenceContext = persistenceContext;
	}
	/**
	 * Adds the entity and entry to this context, associating them together
	 *
	 * @param entity The entity
	 * @param entityEntry The entry
	 */
	public void addEntityEntry(Object entity, EntityEntry entityEntry) {
		// IMPORTANT!!!!!
		//		add is called more than once of some entities.  In such cases the first
		//		call is simply setting up a "marker" to avoid infinite looping from reentrancy
		// any addition (even the double one described above) should invalidate the cross-ref array
		dirty = true;
		assert AbstractEntityEntry.class.isInstance( entityEntry );
		// We only need to check a mutable EntityEntry is associated with the same PersistenceContext.
		// Immutable EntityEntry can be associated with multiple PersistenceContexts, so no need to check.
		// ImmutableEntityEntry#getPersistenceContext() throws an exception (HHH-10251).
		if ( entityEntry.getPersister().isMutable() ) {
			assert AbstractEntityEntry.class.cast( entityEntry ).getPersistenceContext() == persistenceContext;
		}
		// Determine the appropriate ManagedEntity instance to use based on whether the entity is enhanced or not.
		// Throw an exception if entity is a mutable ManagedEntity that is associated with a different
		// PersistenceContext.
		ManagedEntity managedEntity = getAssociatedManagedEntity( entity );
		final boolean alreadyAssociated = managedEntity != null;
		if ( !alreadyAssociated ) {
			if ( ManagedEntity.class.isInstance( entity ) ) {
				if ( entityEntry.getPersister().isMutable() ) {
					managedEntity = (ManagedEntity) entity;
					// We know that managedEntity is not associated with the same PersistenceContext.
					// Check if managedEntity is associated with a different PersistenceContext.
					checkNotAssociatedWithOtherPersistenceContextIfMutable( managedEntity );
				}
				else {
					// Create a holder for PersistenceContext-related data.
					managedEntity = new ImmutableManagedEntityHolder( (ManagedEntity) entity );
					if ( immutableManagedEntityXref == null ) {
						immutableManagedEntityXref = new IdentityHashMap();
					}
					immutableManagedEntityXref.put(
							(ManagedEntity) entity,
							(ImmutableManagedEntityHolder) managedEntity
					);
				}
			}
			else {
				if ( nonEnhancedEntityXref == null ) {
					nonEnhancedEntityXref = new IdentityHashMap();
				}
				managedEntity = new ManagedEntityImpl( entity );
				nonEnhancedEntityXref.put( entity, managedEntity );
			}
		}
		// associate the EntityEntry with the entity
		managedEntity.$$_hibernate_setEntityEntry( entityEntry );
		if ( alreadyAssociated ) {
			// if the entity was already associated with the context, skip the linking step.
			return;
		}
		// TODO: can dirty be set to true here?
		// finally, set up linking and count
		if ( tail == null ) {
			assert head == null;
			// Protect against stale data in the ManagedEntity and nullify previous/next references.
			managedEntity.$$_hibernate_setPreviousManagedEntity( null );
			managedEntity.$$_hibernate_setNextManagedEntity( null );
			head = managedEntity;
			tail = head;
			count = 1;
		}
		else {
			tail.$$_hibernate_setNextManagedEntity( managedEntity );
			managedEntity.$$_hibernate_setPreviousManagedEntity( tail );
			// Protect against stale data left in the ManagedEntity nullify next reference.
			managedEntity.$$_hibernate_setNextManagedEntity( null );
			tail = managedEntity;
			count++;
		}
	}
	private ManagedEntity getAssociatedManagedEntity(Object entity) {
		if ( ManagedEntity.class.isInstance( entity ) ) {
			final ManagedEntity managedEntity = (ManagedEntity) entity;
			if ( managedEntity.$$_hibernate_getEntityEntry() == null ) {
				// it is not associated
				return null;
			}
			final AbstractEntityEntry entityEntry = (AbstractEntityEntry) managedEntity.$$_hibernate_getEntityEntry();
			if ( entityEntry.getPersister().isMutable() ) {
				return entityEntry.getPersistenceContext() == persistenceContext
						? managedEntity // it is associated
						: null;
			}
			else {
				// if managedEntity is associated with this EntityEntryContext, then
				// it will have an entry in immutableManagedEntityXref and its
				// holder will be returned.
				return immutableManagedEntityXref != null
						? immutableManagedEntityXref.get( managedEntity )
						: null;
			}
		}
		else {
			return nonEnhancedEntityXref != null
					? nonEnhancedEntityXref.get( entity )
					: null;
		}
	}
	private void checkNotAssociatedWithOtherPersistenceContextIfMutable(ManagedEntity managedEntity) {
		// we only have to check mutable managedEntity
		final AbstractEntityEntry entityEntry = (AbstractEntityEntry) managedEntity.$$_hibernate_getEntityEntry();
		if ( entityEntry == null ||
				!entityEntry.getPersister().isMutable() ||
				entityEntry.getPersistenceContext() == null ||
				entityEntry.getPersistenceContext() == persistenceContext ) {
			return;
		}
		if ( entityEntry.getPersistenceContext().getSession().isOpen() ) {
			// NOTE: otherPersistenceContext may be operating on the entityEntry in a different thread.
			//       it is not safe to associate entityEntry with this EntityEntryContext.
			throw new HibernateException(
					"Illegal attempt to associate a ManagedEntity with two open persistence contexts. " + entityEntry
			);
		}
		else {
			// otherPersistenceContext is associated with a closed PersistenceContext
			log.stalePersistenceContextInEntityEntry( entityEntry.toString() );
		}
	}
	/**
	 * Does this entity exist in this context, associated with an EntityEntry?
	 *
	 * @param entity The entity to check
	 *
	 * @return {@code true} if it is associated with this context
	 */
	public boolean hasEntityEntry(Object entity) {
		return getEntityEntry( entity ) != null;
	}
	/**
	 * Retrieve the associated EntityEntry for the entity
	 *
	 * @param entity The entity to retrieve the EntityEntry for
	 *
	 * @return The associated EntityEntry
	 */
	public EntityEntry getEntityEntry(Object entity) {
		// locate a ManagedEntity for the entity, but only if it is associated with the same PersistenceContext.
		final ManagedEntity managedEntity = getAssociatedManagedEntity( entity );
		// and get/return the EntityEntry from the ManagedEntry
		return managedEntity == null
				? null
				: managedEntity.$$_hibernate_getEntityEntry();
	}
	/**
	 * Remove an entity from the context, returning the EntityEntry which was associated with it
	 *
	 * @param entity The entity to remove
	 *
	 * @return Tjee EntityEntry
	 */
	public EntityEntry removeEntityEntry(Object entity) {
		// locate a ManagedEntity for the entity, but only if it is associated with the same PersistenceContext.
		// no need to check if the entity is a ManagedEntity that is associated with a different PersistenceContext
		final ManagedEntity managedEntity = getAssociatedManagedEntity( entity );
		if ( managedEntity == null ) {
			// not associated with this EntityEntryContext, so nothing to do.
			return null;
		}
		dirty = true;
		if ( ImmutableManagedEntityHolder.class.isInstance( managedEntity ) ) {
			assert entity == ( (ImmutableManagedEntityHolder) managedEntity ).managedEntity;
			immutableManagedEntityXref.remove( (ManagedEntity) entity );
		}
		else if ( !ManagedEntity.class.isInstance( entity ) ) {
			nonEnhancedEntityXref.remove( entity );
		}
		// prepare for re-linking...
		final ManagedEntity previous = managedEntity.$$_hibernate_getPreviousManagedEntity();
		final ManagedEntity next = managedEntity.$$_hibernate_getNextManagedEntity();
		managedEntity.$$_hibernate_setPreviousManagedEntity( null );
		managedEntity.$$_hibernate_setNextManagedEntity( null );
		// re-link
		count--;
		if ( count == 0 ) {
			// handle as a special case...
			head = null;
			tail = null;
			assert previous == null;
			assert next == null;
		}
		else {
			// otherwise, previous or next (or both) should be non-null
			if ( previous == null ) {
				// we are removing head
				assert managedEntity == head;
				head = next;
			}
			else {
				previous.$$_hibernate_setNextManagedEntity( next );
			}
			if ( next == null ) {
				// we are removing tail
				assert managedEntity == tail;
				tail = previous;
			}
			else {
				next.$$_hibernate_setPreviousManagedEntity( previous );
			}
		}
		// finally clean out the ManagedEntity and return the associated EntityEntry
		final EntityEntry theEntityEntry = managedEntity.$$_hibernate_getEntityEntry();
		managedEntity.$$_hibernate_setEntityEntry( null );
		return theEntityEntry;
	}
	/**
	 * The main bugaboo with IdentityMap that warranted this class in the first place.
	 *
	 * Return an array of all the entity/EntityEntry pairs in this context.  The array is to make sure
	 * that the iterators built off of it are safe from concurrency/reentrancy
	 *
	 * @return The safe array
	 */
	public Map.Entry[] reentrantSafeEntityEntries() {
		if ( dirty ) {
			reentrantSafeEntries = new EntityEntryCrossRefImpl[count];
			int i = 0;
			ManagedEntity managedEntity = head;
			while ( managedEntity != null ) {
				reentrantSafeEntries[i++] = new EntityEntryCrossRefImpl(
						managedEntity.$$_hibernate_getEntityInstance(),
						managedEntity.$$_hibernate_getEntityEntry()
				);
				managedEntity = managedEntity.$$_hibernate_getNextManagedEntity();
			}
			dirty = false;
		}
		return reentrantSafeEntries;
	}
	/**
	 * Clear this context of all managed entities
	 */
	public void clear() {
		dirty = true;
		ManagedEntity node = head;
		while ( node != null ) {
			final ManagedEntity nextNode = node.$$_hibernate_getNextManagedEntity();
			node.$$_hibernate_setEntityEntry( null );
			node.$$_hibernate_setPreviousManagedEntity( null );
			node.$$_hibernate_setNextManagedEntity( null );
			node = nextNode;
		}
		if ( immutableManagedEntityXref != null ) {
			immutableManagedEntityXref.clear();
		}
		if ( nonEnhancedEntityXref != null ) {
			nonEnhancedEntityXref.clear();
		}
		head = null;
		tail = null;
		count = 0;
		reentrantSafeEntries = null;
	}
	/**
	 * Down-grade locks to NONE for all entities in this context
	 */
	public void downgradeLocks() {
		if ( head == null ) {
			return;
		}
		ManagedEntity node = head;
		while ( node != null ) {
			node.$$_hibernate_getEntityEntry().setLockMode( LockMode.NONE );
			node = node.$$_hibernate_getNextManagedEntity();
		}
	}
	/**
	 * JDK serialization hook for serializing
	 *
	 * @param oos The stream to write ourselves to
	 *
	 * @throws IOException Indicates an IO exception accessing the given stream
	 */
	public void serialize(ObjectOutputStream oos) throws IOException {
		log.tracef( "Starting serialization of [%s] EntityEntry entries", count );
		oos.writeInt( count );
		if ( count == 0 ) {
			return;
		}
		ManagedEntity managedEntity = head;
		while ( managedEntity != null ) {
			// so we know whether or not to build a ManagedEntityImpl on deserialize
			oos.writeBoolean( managedEntity == managedEntity.$$_hibernate_getEntityInstance() );
			oos.writeObject( managedEntity.$$_hibernate_getEntityInstance() );
			// we need to know which implementation of EntityEntry is being serialized
			oos.writeInt( managedEntity.$$_hibernate_getEntityEntry().getClass().getName().length() );
			oos.writeChars( managedEntity.$$_hibernate_getEntityEntry().getClass().getName() );
			managedEntity.$$_hibernate_getEntityEntry().serialize( oos );
			managedEntity = managedEntity.$$_hibernate_getNextManagedEntity();
		}
	}
	/**
	 * JDK serialization hook for deserializing
	 *
	 * @param ois The stream to read ourselves from
	 * @param rtn The persistence context we belong to
	 *
	 * @return The deserialized EntityEntryContext
	 *
	 * @throws IOException Indicates an IO exception accessing the given stream
	 * @throws ClassNotFoundException Problem reading stream data
	 */
	public static EntityEntryContext deserialize(ObjectInputStream ois, StatefulPersistenceContext rtn)
			throws IOException, ClassNotFoundException {
		final int count = ois.readInt();
		log.tracef( "Starting deserialization of [%s] EntityEntry entries", count );
		final EntityEntryContext context = new EntityEntryContext( rtn );
		context.count = count;
		context.dirty = true;
		if ( count == 0 ) {
			return context;
		}
		ManagedEntity previous = null;
		for ( int i = 0; i < count; i++ ) {
			final boolean isEnhanced = ois.readBoolean();
			final Object entity = ois.readObject();
			//Call deserialize method dynamically via reflection
			final int numChars = ois.readInt();
			final char[] entityEntryClassNameArr = new char[numChars];
			for ( int j = 0; j < numChars; j++ ) {
				entityEntryClassNameArr[j] = ois.readChar();
			}
			final EntityEntry entry = deserializeEntityEntry( entityEntryClassNameArr, ois, rtn );
			final ManagedEntity managedEntity;
			if ( isEnhanced ) {
				if ( entry.getPersister().isMutable() ) {
					managedEntity = (ManagedEntity) entity;
				}
				else {
					managedEntity = new ImmutableManagedEntityHolder( (ManagedEntity) entity );
					if ( context.immutableManagedEntityXref == null ) {
						context.immutableManagedEntityXref =
								new IdentityHashMap();
					}
					context.immutableManagedEntityXref.put(
							(ManagedEntity) entity,
							(ImmutableManagedEntityHolder) managedEntity
					);
				}
			}
			else {
				managedEntity = new ManagedEntityImpl( entity );
				if ( context.nonEnhancedEntityXref == null ) {
					context.nonEnhancedEntityXref = new IdentityHashMap();
				}
				context.nonEnhancedEntityXref.put( entity, managedEntity );
			}
			managedEntity.$$_hibernate_setEntityEntry( entry );
			if ( previous == null ) {
				context.head = managedEntity;
			}
			else {
				previous.$$_hibernate_setNextManagedEntity( managedEntity );
				managedEntity.$$_hibernate_setPreviousManagedEntity( previous );
			}
			previous = managedEntity;
		}
		context.tail = previous;
		return context;
	}
	private static EntityEntry deserializeEntityEntry(char[] entityEntryClassNameArr, ObjectInputStream ois, StatefulPersistenceContext rtn){
		EntityEntry entry = null;
		final String entityEntryClassName = new String( entityEntryClassNameArr );
		final Class entityEntryClass =   rtn.getSession().getFactory().getServiceRegistry().getService( ClassLoaderService.class ).classForName( entityEntryClassName );
		try {
			final Method deserializeMethod = entityEntryClass.getDeclaredMethod( "deserialize", ObjectInputStream.class,	PersistenceContext.class );
			entry = (EntityEntry) deserializeMethod.invoke( null, ois, rtn );
		}
		catch (NoSuchMethodException e) {
			log.errorf( "Enable to deserialize [%s]", entityEntryClassName );
		}
		catch (InvocationTargetException e) {
			log.errorf( "Enable to deserialize [%s]", entityEntryClassName );
		}
		catch (IllegalAccessException e) {
			log.errorf( "Enable to deserialize [%s]", entityEntryClassName );
		}
		return entry;
	}
	public int getNumberOfManagedEntities() {
		return count;
	}
	/**
	 * The wrapper for entity classes which do not implement ManagedEntity
	 */
	private static class ManagedEntityImpl implements ManagedEntity {
		private final Object entityInstance;
		private EntityEntry entityEntry;
		private ManagedEntity previous;
		private ManagedEntity next;
		public ManagedEntityImpl(Object entityInstance) {
			this.entityInstance = entityInstance;
		}
		@Override
		public Object $$_hibernate_getEntityInstance() {
			return entityInstance;
		}
		@Override
		public EntityEntry $$_hibernate_getEntityEntry() {
			return entityEntry;
		}
		@Override
		public void $$_hibernate_setEntityEntry(EntityEntry entityEntry) {
			this.entityEntry = entityEntry;
		}
		@Override
		public ManagedEntity $$_hibernate_getNextManagedEntity() {
			return next;
		}
		@Override
		public void $$_hibernate_setNextManagedEntity(ManagedEntity next) {
			this.next = next;
		}
		@Override
		public ManagedEntity $$_hibernate_getPreviousManagedEntity() {
			return previous;
		}
		@Override
		public void $$_hibernate_setPreviousManagedEntity(ManagedEntity previous) {
			this.previous = previous;
		}
	}
	private static class ImmutableManagedEntityHolder implements ManagedEntity {
		private ManagedEntity managedEntity;
		private ManagedEntity previous;
		private ManagedEntity next;
		public ImmutableManagedEntityHolder(ManagedEntity immutableManagedEntity) {
			this.managedEntity = immutableManagedEntity;
		}
		@Override
		public Object $$_hibernate_getEntityInstance() {
			return managedEntity.$$_hibernate_getEntityInstance();
		}
		@Override
		public EntityEntry $$_hibernate_getEntityEntry() {
			return managedEntity.$$_hibernate_getEntityEntry();
		}
		@Override
		public void $$_hibernate_setEntityEntry(EntityEntry entityEntry) {
			// need to think about implications for memory leaks here if we don't removed reference to EntityEntry
			if ( entityEntry == null ) {
				if ( canClearEntityEntryReference() ) {
					managedEntity.$$_hibernate_setEntityEntry( null );
				}
				// otherwise, do nothing.
			}
			else {
				// TODO: we may want to do something different here if
				// managedEntity is in the process of being deleted.
				// An immutable ManagedEntity can be associated with
				// multiple PersistenceContexts. Changing the status
				// to DELETED probably should not happen directly
				// in the ManagedEntity because that would affect all
				// PersistenceContexts to which the ManagedEntity is
				// associated.
				managedEntity.$$_hibernate_setEntityEntry( entityEntry );
			}
		}
		@Override
		public ManagedEntity $$_hibernate_getPreviousManagedEntity() {
			// previous reference cannot be stored in an immutable ManagedEntity;
			// previous reference is maintained by this ManagedEntityHolder.
			return previous;
		}
		@Override
		public void $$_hibernate_setPreviousManagedEntity(ManagedEntity previous) {
			// previous reference cannot be stored in an immutable ManagedEntity;
			// previous reference is maintained by this ManagedEntityHolder.
			this.previous = previous;
		}
		@Override
		public ManagedEntity $$_hibernate_getNextManagedEntity() {
			// next reference cannot be stored in an immutable ManagedEntity;
			// next reference is maintained by this ManagedEntityHolder.
			return next;
		}
		@Override
		public void $$_hibernate_setNextManagedEntity(ManagedEntity next) {
			// next reference cannot be stored in an immutable ManagedEntity;
			// next reference is maintained by this ManagedEntityHolder.
			this.next = next;
		}
		/*
		Check instance type of EntityEntry and if type is ImmutableEntityEntry, check to see if entity is referenced cached in the second level cache
		 */
		private boolean canClearEntityEntryReference(){
			if( managedEntity.$$_hibernate_getEntityEntry() == null ) {
				return true;
			}
			if( !(managedEntity.$$_hibernate_getEntityEntry() instanceof ImmutableEntityEntry) ) {
				return true;
			}
			else if( managedEntity.$$_hibernate_getEntityEntry().getPersister().canUseReferenceCacheEntries() ) {
				return false;
			}
			return true;
		}
	}
	/**
	 * Used in building the {@link #reentrantSafeEntityEntries()} entries
	 */
	public static interface EntityEntryCrossRef extends Map.Entry {
		/**
		 * The entity
		 *
		 * @return The entity
		 */
		public Object getEntity();
		/**
		 * The associated EntityEntry
		 *
		 * @return The EntityEntry associated with the entity in this context
		 */
		public EntityEntry getEntityEntry();
	}
	/**
	 * Implementation of the EntityEntryCrossRef interface
	 */
	private static class EntityEntryCrossRefImpl implements EntityEntryCrossRef {
		private final Object entity;
		private EntityEntry entityEntry;
		private EntityEntryCrossRefImpl(Object entity, EntityEntry entityEntry) {
			this.entity = entity;
			this.entityEntry = entityEntry;
		}
		@Override
		public Object getEntity() {
			return entity;
		}
		@Override
		public EntityEntry getEntityEntry() {
			return entityEntry;
		}
		@Override
		public Object getKey() {
			return getEntity();
		}
		@Override
		public EntityEntry getValue() {
			return getEntityEntry();
		}
		@Override
		public EntityEntry setValue(EntityEntry entityEntry) {
			final EntityEntry old = this.entityEntry;
			this.entityEntry = entityEntry;
			return old;
		}
	}
}