ref #10067 adapt AbstractLazyInitializer to new hibernate version in taxeditor
[taxeditor.git] / eu.etaxonomy.taxeditor.cdmlib / src / main / java / org / hibernate / proxy / AbstractLazyInitializer.java
index de085411f6010086d5f8383062b05ddbd944714a..39adf883027acf8d4a4628111cc4e3d1c4ee1131 100644 (file)
-/*
- * Hibernate, Relational Persistence for Idiomatic Java
- *
- * Copyright (c) 2008-2011, Red Hat Inc. or third-party contributors as
- * indicated by the @author tags or express copyright attribution
- * statements applied by the authors.  All third-party contributions are
- * distributed under license by Red Hat Inc.
- *
- * This copyrighted material is made available to anyone wishing to use, modify,
- * copy, or redistribute it subject to the terms and conditions of the GNU
- * Lesser General Public License, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this distribution; if not, write to:
- * Free Software Foundation, Inc.
- * 51 Franklin Street, Fifth Floor
- * Boston, MA  02110-1301  USA
- */
-package org.hibernate.proxy;
-
-import java.io.Serializable;
-
-import javax.naming.NamingException;
-
-import org.hibernate.HibernateException;
-import org.hibernate.LazyInitializationException;
-import org.hibernate.Session;
-import org.hibernate.SessionException;
-import org.hibernate.TransientObjectException;
-import org.hibernate.engine.spi.EntityKey;
-import org.hibernate.engine.spi.SessionFactoryImplementor;
-import org.hibernate.engine.spi.SessionImplementor;
-import org.hibernate.internal.SessionFactoryRegistry;
-import org.hibernate.persister.entity.EntityPersister;
-import org.jboss.logging.Logger;
-
-import eu.etaxonomy.cdm.api.application.CdmApplicationRemoteConfiguration;
-import eu.etaxonomy.cdm.cache.ProxyUtils;
-import eu.etaxonomy.cdm.model.common.CdmBase;
-import eu.etaxonomy.taxeditor.service.ICachedCommonService;
-
-/**
- * Convenience base class for lazy initialization handlers.  Centralizes the basic plumbing of doing lazy
- * initialization freeing subclasses to acts as essentially adapters to their intended entity mode and/or
- * proxy generation strategy.
- *
- * @author Gavin King
- */
-public abstract class AbstractLazyInitializer implements LazyInitializer {
-       private static final Logger log = Logger.getLogger( AbstractLazyInitializer.class );
-
-       private String entityName;
-       private Serializable id;
-       private Object target;
-       private boolean initialized;
-       private boolean readOnly;
-       private boolean unwrap;
-       private transient SessionImplementor session;
-       private Boolean readOnlyBeforeAttachedToSession;
-
-       private String sessionFactoryUuid;
-       private boolean specjLazyLoad = false;
-
-       /**
-        * For serialization from the non-pojo initializers (HHH-3309)
-        */
-       protected AbstractLazyInitializer() {
-       }
-
-       /**
-        * Main constructor.
-        *
-        * @param entityName The name of the entity being proxied.
-        * @param id The identifier of the entity being proxied.
-        * @param session The session owning the proxy.
-        */
-       protected AbstractLazyInitializer(String entityName, Serializable id, SessionImplementor session) {
-               this.entityName = entityName;
-               this.id = id;
-               // initialize other fields depending on session state
-               if ( session == null ) {
-                       unsetSession();
-               }
-               else {
-                       setSession( session );
-               }
-       }
-
-       @Override
-       public final String getEntityName() {
-               return entityName;
-       }
-
-       @Override
-       public final Serializable getIdentifier() {
-               return id;
-       }
-
-       @Override
-       public final void setIdentifier(Serializable id) {
-               this.id = id;
-       }
-
-       @Override
-       public final boolean isUninitialized() {
-               return !initialized;
-       }
-
-       @Override
-       public final SessionImplementor getSession() {
-               return session;
-       }
-
-       @Override
-       public final void setSession(SessionImplementor s) throws HibernateException {
-               if ( s != session ) {
-                       // check for s == null first, since it is least expensive
-                       if ( s == null ) {
-                               unsetSession();
-                       }
-                       else if ( isConnectedToSession() ) {
-                               //TODO: perhaps this should be some other RuntimeException...
-                               throw new HibernateException( "illegally attempted to associate a proxy with two open Sessions" );
-                       }
-                       else {
-                               // s != null
-                               session = s;
-                               if ( readOnlyBeforeAttachedToSession == null ) {
-                                       // use the default read-only/modifiable setting
-                                       final EntityPersister persister = s.getFactory().getEntityPersister( entityName );
-                                       setReadOnly( s.getPersistenceContext().isDefaultReadOnly() || !persister.isMutable() );
-                               }
-                               else {
-                                       // use the read-only/modifiable setting indicated during deserialization
-                                       setReadOnly( readOnlyBeforeAttachedToSession.booleanValue() );
-                                       readOnlyBeforeAttachedToSession = null;
-                               }
-                       }
-               }
-       }
-
-       private static EntityKey generateEntityKeyOrNull(Serializable id, SessionImplementor s, String entityName) {
-               if ( id == null || s == null || entityName == null ) {
-                       return null;
-               }
-               return s.generateEntityKey( id, s.getFactory().getEntityPersister( entityName ) );
-       }
-
-       @Override
-       public final void unsetSession() {
-               prepareForPossibleSpecialSpecjInitialization();
-               session = null;
-               readOnly = false;
-               readOnlyBeforeAttachedToSession = null;
-       }
-
-       @Override
-       public final void initialize() throws HibernateException {
-               // In remoting we are sure that session is null
-               // both when using property paths and switching off conversations
-               if(session == null && remoting) {
-                       remoteInitialize();
-               }
-               if ( !initialized ) {
-                       if ( specjLazyLoad ) {
-                               specialSpecjInitialization();
-                       }
-                       else if ( session == null ) {
-                               throw new LazyInitializationException( "could not initialize proxy - no Session" );
-                       }
-                       else if ( !session.isOpen() ) {
-                               throw new LazyInitializationException( "could not initialize proxy - the owning Session was closed" );
-                       }
-                       else if ( !session.isConnected() ) {
-                               throw new LazyInitializationException( "could not initialize proxy - the owning Session is disconnected" );
-                       }
-                       else {
-                               target = session.immediateLoad( entityName, id );
-                               initialized = true;
-                               checkTargetState();
-                       }
-               }
-               else {
-                       checkTargetState();
-               }
-       }
-
-       protected void specialSpecjInitialization() {
-               if ( session == null ) {
-                       //we have a detached collection thats set to null, reattach
-                       if ( sessionFactoryUuid == null ) {
-                               throw new LazyInitializationException( "could not initialize proxy - no Session" );
-                       }
-                       try {
-                               SessionFactoryImplementor sf = (SessionFactoryImplementor)
-                                               SessionFactoryRegistry.INSTANCE.getSessionFactory( sessionFactoryUuid );
-                               SessionImplementor session = (SessionImplementor) sf.openSession();
-
-                               // TODO: On the next major release, add an
-                               // 'isJTA' or 'getTransactionFactory' method to Session.
-                               /*boolean isJTA = session.getTransactionCoordinator()
-                                               .getTransactionContext().getTransactionEnvironment()
-                                               .getTransactionFactory()
-                                               .compatibleWithJtaSynchronization();
-                               */
-                               boolean isJTA = session.getTransactionCoordinator().getTransactionCoordinatorBuilder().isJta();
-
-                               if ( !isJTA ) {
-                                       // Explicitly handle the transactions only if we're not in
-                                       // a JTA environment.  A lazy loading temporary session can
-                                       // be created even if a current session and transaction are
-                                       // open (ex: session.clear() was used).  We must prevent
-                                       // multiple transactions.
-                                       ( ( Session) session ).beginTransaction();
-                               }
-
-                               try {
-                                       target = session.immediateLoad( entityName, id );
-                               }
-                               finally {
-                                       // make sure the just opened temp session gets closed!
-                                       try {
-                                               if ( !isJTA ) {
-                                                       ( ( Session) session ).getTransaction().commit();
-                                               }
-                                               ( (Session) session ).close();
-                                       }
-                                       catch (Exception e) {
-                                               log.warn( "Unable to close temporary session used to load lazy proxy associated to no session" );
-                                       }
-                               }
-                               initialized = true;
-                               checkTargetState();
-                       }
-                       catch (Exception e) {
-                               e.printStackTrace();
-                               throw new LazyInitializationException( e.getMessage() );
-                       }
-               }
-               else if ( session.isOpen() && session.isConnected() ) {
-                       target = session.immediateLoad( entityName, id );
-                       initialized = true;
-                       checkTargetState();
-               }
-               else {
-                       throw new LazyInitializationException( "could not initialize proxy - Session was closed or disced" );
-               }
-       }
-
-       protected void prepareForPossibleSpecialSpecjInitialization() {
-               if ( session != null ) {
-                       specjLazyLoad = session.getFactory().getSettings().isInitializeLazyStateOutsideTransactionsEnabled();
-
-                       if ( specjLazyLoad && sessionFactoryUuid == null ) {
-                               try {
-                                       sessionFactoryUuid = (String) session.getFactory().getReference().get( "uuid" ).getContent();
-                               }
-                               catch (NamingException e) {
-                                       //not much we can do if this fails...
-                               }
-                       }
-               }
-       }
-
-       private void checkTargetState() {
-               if ( !unwrap ) {
-                       if ( target == null ) {
-                               getSession().getFactory().getEntityNotFoundDelegate().handleEntityNotFound( entityName, id );
-                       }
-               }
-       }
-
-       /**
-        * Getter for property 'connectedToSession'.
-        *
-        * @return Value for property 'connectedToSession'.
-        */
-       protected final boolean isConnectedToSession() {
-               return getProxyOrNull() != null;
-       }
-
-       private Object getProxyOrNull() {
-               final EntityKey entityKey = generateEntityKeyOrNull( getIdentifier(), session, getEntityName() );
-               if ( entityKey != null && session != null && session.isOpen() ) {
-                       return session.getPersistenceContext().getProxy( entityKey );
-               }
-               return null;
-       }
-
-       @Override
-       public final Object getImplementation() {
-               initialize();
-               return target;
-       }
-
-       @Override
-       public final void setImplementation(Object target) {
-               this.target = target;
-               initialized = true;
-       }
-
-       @Override
-       public final Object getImplementation(SessionImplementor s) throws HibernateException {
-               final EntityKey entityKey = generateEntityKeyOrNull( getIdentifier(), s, getEntityName() );
-               return (entityKey == null ? null : s.getPersistenceContext().getEntity( entityKey ));
-       }
-
-       /**
-        * Getter for property 'target'.
-        * <p/>
-        * Same as {@link #getImplementation()} except that this method will not force initialization.
-        *
-        * @return Value for property 'target'.
-        */
-       protected final Object getTarget() {
-               return target;
-       }
-
-       @Override
-       public final boolean isReadOnlySettingAvailable() {
-               return (session != null && !session.isClosed());
-       }
-
-       private void errorIfReadOnlySettingNotAvailable() {
-               if ( session == null ) {
-                       throw new TransientObjectException(
-                                       "Proxy is detached (i.e, session is null). The read-only/modifiable setting is only accessible when the proxy is associated with an open session."
-                       );
-               }
-               if ( session.isClosed() ) {
-                       throw new SessionException(
-                                       "Session is closed. The read-only/modifiable setting is only accessible when the proxy is associated with an open session."
-                       );
-               }
-       }
-
-       @Override
-       public final boolean isReadOnly() {
-               errorIfReadOnlySettingNotAvailable();
-               return readOnly;
-       }
-
-       @Override
-       public final void setReadOnly(boolean readOnly) {
-               errorIfReadOnlySettingNotAvailable();
-               // only update if readOnly is different from current setting
-               if ( this.readOnly != readOnly ) {
-                       final EntityPersister persister = session.getFactory().getEntityPersister( entityName );
-                       if ( !persister.isMutable() && !readOnly ) {
-                               throw new IllegalStateException( "cannot make proxies for immutable entities modifiable" );
-                       }
-                       this.readOnly = readOnly;
-                       if ( initialized ) {
-                               EntityKey key = generateEntityKeyOrNull( getIdentifier(), session, getEntityName() );
-                               if ( key != null && session.getPersistenceContext().containsEntity( key ) ) {
-                                       session.getPersistenceContext().setReadOnly( target, readOnly );
-                               }
-                       }
-               }
-       }
-
-       /**
-        * Get the read-only/modifiable setting that should be put in affect when it is
-        * attached to a session.
-        * <p/>
-        * This method should only be called during serialization when read-only/modifiable setting
-        * is not available (i.e., isReadOnlySettingAvailable() == false)
-        *
-        * @return null, if the default setting should be used;
-        *         true, for read-only;
-        *         false, for modifiable
-        *
-        * @throws IllegalStateException if isReadOnlySettingAvailable() == true
-        */
-       protected final Boolean isReadOnlyBeforeAttachedToSession() {
-               if ( isReadOnlySettingAvailable() ) {
-                       throw new IllegalStateException(
-                                       "Cannot call isReadOnlyBeforeAttachedToSession when isReadOnlySettingAvailable == true"
-                       );
-               }
-               return readOnlyBeforeAttachedToSession;
-       }
-
-       /**
-        * Set the read-only/modifiable setting that should be put in affect when it is
-        * attached to a session.
-        * <p/>
-        * This method should only be called during deserialization, before associating
-        * the proxy with a session.
-        *
-        * @param readOnlyBeforeAttachedToSession, the read-only/modifiable setting to use when
-        * associated with a session; null indicates that the default should be used.
-        *
-        * @throws IllegalStateException if isReadOnlySettingAvailable() == true
-        */
-       /* package-private */
-       final void setReadOnlyBeforeAttachedToSession(Boolean readOnlyBeforeAttachedToSession) {
-               if ( isReadOnlySettingAvailable() ) {
-                       throw new IllegalStateException(
-                                       "Cannot call setReadOnlyBeforeAttachedToSession when isReadOnlySettingAvailable == true"
-                       );
-               }
-               this.readOnlyBeforeAttachedToSession = readOnlyBeforeAttachedToSession;
-       }
-
-       @Override
-       public boolean isUnwrap() {
-               return unwrap;
-       }
-
-       @Override
-       public void setUnwrap(boolean unwrap) {
-               this.unwrap = unwrap;
-       }
-
-       /** Below is section of code which makes remote service calls */
-
-       private static CdmApplicationRemoteConfiguration configuration;
-       private static boolean remoting = false;
-
-       public static void setConfiguration(CdmApplicationRemoteConfiguration conf) {
-           remoting = true;
-           configuration = conf;
-       }
-
-
-       private void remoteInitialize() {
-
-               if(!initialized) {
-                       int classid = ((Integer)getIdentifier()).intValue();
-                       log.info("--> Remote Lazy Initializing Object " + getEntityName() + " with id " + classid);
-                       Class clazz;
-                       try {
-                               clazz = Class.forName(getEntityName());
-                       } catch (ClassNotFoundException e) {
-                               throw new HibernateException("Class for " + getEntityName() + " not found", e);
-                       }
-                       if(configuration == null) {
-                               throw new HibernateException("CdmApplicationRemoteConfiguration not initialized (null)");
-                       }
-                       ICachedCommonService cachedCommonService = configuration.getCachedCommonService();
-                       if(cachedCommonService == null) {
-                               throw new HibernateException("commonService not initialized (null)");
-                       }
-
-                       CdmBase cdmBase = cachedCommonService.find(clazz,classid);
-            if(ProxyUtils.isUninitializedProxy(cdmBase)) {
-                throw new HibernateException("CdmBase Object initialized but is still a proxy");
-            }
-                       setImplementation(cdmBase);
-
-               }
-       }
-
-       public static boolean isInitialized(AbstractLazyInitializer obj) {
-               return obj.initialized;
-       }
-}
+/* Hibernate, Relational Persistence for Idiomatic Java\r
+ *\r
+ * License: GNU Lesser General Public License (LGPL), version 2.1 or later.\r
+ * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.\r
+ */\r
+package org.hibernate.proxy;\r
+\r
+import java.io.Serializable;\r
+\r
+import org.hibernate.FlushMode;\r
+import org.hibernate.HibernateException;\r
+import org.hibernate.LazyInitializationException;\r
+import org.hibernate.SessionException;\r
+import org.hibernate.TransientObjectException;\r
+import org.hibernate.boot.spi.SessionFactoryOptions;\r
+import org.hibernate.engine.spi.EntityKey;\r
+import org.hibernate.engine.spi.PersistenceContext;\r
+import org.hibernate.engine.spi.SessionFactoryImplementor;\r
+import org.hibernate.engine.spi.SharedSessionContractImplementor;\r
+import org.hibernate.internal.CoreLogging;\r
+import org.hibernate.internal.CoreMessageLogger;\r
+import org.hibernate.internal.SessionFactoryRegistry;\r
+import org.hibernate.persister.entity.EntityPersister;\r
+\r
+/**\r
+ * Convenience base class for lazy initialization handlers.  Centralizes the basic plumbing of doing lazy\r
+ * initialization freeing subclasses to acts as essentially adapters to their intended entity mode and/or\r
+ * proxy generation strategy.\r
+ *\r
+ * @author Gavin King\r
+ */\r
+public abstract class AbstractLazyInitializer implements LazyInitializer {\r
+    private static final CoreMessageLogger LOG = CoreLogging.messageLogger( AbstractLazyInitializer.class );\r
+\r
+    private String entityName;\r
+    private Serializable id;\r
+    private Object target;\r
+    private boolean initialized;\r
+    private boolean readOnly;\r
+    private boolean unwrap;\r
+    private transient SharedSessionContractImplementor session;\r
+    private Boolean readOnlyBeforeAttachedToSession;\r
+\r
+    private String sessionFactoryUuid;\r
+    private boolean allowLoadOutsideTransaction;\r
+\r
+    /**\r
+     * @deprecated This constructor was initially intended for serialization only, and is not useful anymore.\r
+     * In any case it should not be relied on by user code.\r
+     * Subclasses should rather implement Serializable with an {@code Object writeReplace()} method returning\r
+     * a subclass of {@link AbstractSerializableProxy},\r
+     * which in turn implements Serializable and an {@code Object readResolve()} method\r
+     * instantiating the {@link AbstractLazyInitializer} subclass\r
+     * and calling {@link AbstractSerializableProxy#afterDeserialization(AbstractLazyInitializer)} on it.\r
+     * See {@link org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor} and\r
+     * {@link org.hibernate.proxy.pojo.bytebuddy.SerializableProxy} for examples.\r
+     */\r
+    @Deprecated\r
+    protected AbstractLazyInitializer() {\r
+    }\r
+\r
+    /**\r
+     * Main constructor.\r
+     *\r
+     * @param entityName The name of the entity being proxied.\r
+     * @param id The identifier of the entity being proxied.\r
+     * @param session The session owning the proxy.\r
+     */\r
+    protected AbstractLazyInitializer(String entityName, Serializable id, SharedSessionContractImplementor session) {\r
+        this.entityName = entityName;\r
+        this.id = id;\r
+        // initialize other fields depending on session state\r
+        if ( session == null ) {\r
+            unsetSession();\r
+        }\r
+        else {\r
+            setSession( session );\r
+        }\r
+    }\r
+\r
+    @Override\r
+    public final String getEntityName() {\r
+        return entityName;\r
+    }\r
+\r
+    @Override\r
+    public final Serializable getInternalIdentifier() {\r
+        return id;\r
+    }\r
+\r
+    @Override\r
+    public final Serializable getIdentifier() {\r
+        if ( isUninitialized() && isInitializeProxyWhenAccessingIdentifier() ) {\r
+            initialize();\r
+        }\r
+        return id;\r
+    }\r
+\r
+    private boolean isInitializeProxyWhenAccessingIdentifier() {\r
+        return getSession() != null && getSession().getFactory()\r
+                .getSessionFactoryOptions()\r
+                .getJpaCompliance().isJpaProxyComplianceEnabled();\r
+    }\r
+\r
+    @Override\r
+    public final void setIdentifier(Serializable id) {\r
+        this.id = id;\r
+    }\r
+\r
+    @Override\r
+    public final boolean isUninitialized() {\r
+        return !initialized;\r
+    }\r
+\r
+    @Override\r
+    public final SharedSessionContractImplementor getSession() {\r
+        return session;\r
+    }\r
+\r
+    @Override\r
+    public final void setSession(SharedSessionContractImplementor s) throws HibernateException {\r
+        if ( s != session ) {\r
+            // check for s == null first, since it is least expensive\r
+            if ( s == null ) {\r
+                unsetSession();\r
+            }\r
+            else if ( isConnectedToSession() ) {\r
+                //TODO: perhaps this should be some other RuntimeException...\r
+                LOG.attemptToAssociateProxyWithTwoOpenSessions(\r
+                    entityName,\r
+                    id\r
+                );\r
+                throw new HibernateException( "illegally attempted to associate proxy [" + entityName + "#" + id + "] with two open Sessions" );\r
+            }\r
+            else {\r
+                // s != null\r
+                session = s;\r
+                if ( readOnlyBeforeAttachedToSession == null ) {\r
+                    // use the default read-only/modifiable setting\r
+                    final EntityPersister persister = s.getFactory().getEntityPersister( entityName );\r
+                    setReadOnly( s.getPersistenceContext().isDefaultReadOnly() || !persister.isMutable() );\r
+                }\r
+                else {\r
+                    // use the read-only/modifiable setting indicated during deserialization\r
+                    setReadOnly( readOnlyBeforeAttachedToSession );\r
+                    readOnlyBeforeAttachedToSession = null;\r
+                }\r
+            }\r
+        }\r
+    }\r
+\r
+    private static EntityKey generateEntityKeyOrNull(Serializable id, SharedSessionContractImplementor s, String entityName) {\r
+        if ( id == null || s == null || entityName == null ) {\r
+            return null;\r
+        }\r
+        return s.generateEntityKey( id, s.getFactory().getEntityPersister( entityName ) );\r
+    }\r
+\r
+    @Override\r
+    public final void unsetSession() {\r
+        prepareForPossibleLoadingOutsideTransaction();\r
+        session = null;\r
+        readOnly = false;\r
+        readOnlyBeforeAttachedToSession = null;\r
+    }\r
+\r
+    @Override\r
+    public final void initialize() throws HibernateException {\r
+        if ( !initialized ) {\r
+            if ( allowLoadOutsideTransaction ) {\r
+                permissiveInitialization();\r
+            }\r
+            else if ( session == null ) {\r
+                throw new LazyInitializationException( "could not initialize proxy [" + entityName + "#" + id + "] - no Session" );\r
+            }\r
+            else if ( !session.isOpenOrWaitingForAutoClose() ) {\r
+                throw new LazyInitializationException( "could not initialize proxy [" + entityName + "#" + id + "] - the owning Session was closed" );\r
+            }\r
+            else if ( !session.isConnected() ) {\r
+                throw new LazyInitializationException( "could not initialize proxy [" + entityName + "#" + id + "] - the owning Session is disconnected" );\r
+            }\r
+            else {\r
+                target = session.immediateLoad( entityName, id );\r
+                initialized = true;\r
+                checkTargetState(session);\r
+            }\r
+        }\r
+        else {\r
+            checkTargetState(session);\r
+        }\r
+    }\r
+\r
+    protected void permissiveInitialization() {\r
+        if ( session == null ) {\r
+            //we have a detached collection that is set to null, reattach\r
+            if ( sessionFactoryUuid == null ) {\r
+                throw new LazyInitializationException( "could not initialize proxy [" + entityName + "#" + id + "] - no Session" );\r
+            }\r
+            try {\r
+                SessionFactoryImplementor sf = (SessionFactoryImplementor)\r
+                        SessionFactoryRegistry.INSTANCE.getSessionFactory( sessionFactoryUuid );\r
+                SharedSessionContractImplementor session = (SharedSessionContractImplementor) sf.openSession();\r
+                session.getPersistenceContext().setDefaultReadOnly( true );\r
+                session.setFlushMode( FlushMode.MANUAL );\r
+\r
+                boolean isJTA = session.getTransactionCoordinator().getTransactionCoordinatorBuilder().isJta();\r
+\r
+                if ( !isJTA ) {\r
+                    // Explicitly handle the transactions only if we're not in\r
+                    // a JTA environment.  A lazy loading temporary session can\r
+                    // be created even if a current session and transaction are\r
+                    // open (ex: session.clear() was used).  We must prevent\r
+                    // multiple transactions.\r
+                    session.beginTransaction();\r
+                }\r
+\r
+                try {\r
+                    target = session.immediateLoad( entityName, id );\r
+                    initialized = true;\r
+                    checkTargetState(session);\r
+                }\r
+                finally {\r
+                    // make sure the just opened temp session gets closed!\r
+                    try {\r
+                        if ( !isJTA ) {\r
+                            session.getTransaction().commit();\r
+                        }\r
+                        session.close();\r
+                    }\r
+                    catch (Exception e) {\r
+                        LOG.warn( "Unable to close temporary session used to load lazy proxy associated to no session" );\r
+                    }\r
+                }\r
+            }\r
+            catch (Exception e) {\r
+                LOG.error( "Initialization failure [" + entityName + "#" + id + "]", e );\r
+                throw new LazyInitializationException( e.getMessage() );\r
+            }\r
+        }\r
+        else if ( session.isOpenOrWaitingForAutoClose() && session.isConnected() ) {\r
+            target = session.immediateLoad( entityName, id );\r
+            initialized = true;\r
+            checkTargetState(session);\r
+        }\r
+        else {\r
+            throw new LazyInitializationException( "could not initialize proxy [" + entityName + "#" + id + "] - Session was closed or disced" );\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Attempt to initialize the proxy without loading anything from the database.\r
+     *\r
+     * This will only have any effect if the proxy is still attached to a session,\r
+     * and the entity being proxied has been loaded and added to the persistence context\r
+     * of that session since the proxy was created.\r
+     */\r
+    public final void initializeWithoutLoadIfPossible() {\r
+        if ( !initialized && session != null && session.isOpenOrWaitingForAutoClose() ) {\r
+            final EntityKey key = session.generateEntityKey(\r
+                    getInternalIdentifier(),\r
+                    session.getFactory().getMetamodel().entityPersister( getEntityName() )\r
+            );\r
+            final Object entity = session.getPersistenceContextInternal().getEntity( key );\r
+            if ( entity != null ) {\r
+                setImplementation( entity );\r
+            }\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Initialize internal state based on the currently attached session,\r
+     * in order to be ready to load data even after the proxy is detached from the session.\r
+     *\r
+     * This method only has any effect if\r
+     * {@link SessionFactoryOptions#isInitializeLazyStateOutsideTransactionsEnabled()} is {@code true}.\r
+     */\r
+    protected void prepareForPossibleLoadingOutsideTransaction() {\r
+        if ( session != null ) {\r
+            allowLoadOutsideTransaction = session.getFactory().getSessionFactoryOptions().isInitializeLazyStateOutsideTransactionsEnabled();\r
+\r
+            if ( allowLoadOutsideTransaction && sessionFactoryUuid == null ) {\r
+                sessionFactoryUuid = session.getFactory().getUuid();\r
+            }\r
+        }\r
+    }\r
+\r
+    private void checkTargetState(SharedSessionContractImplementor session) {\r
+        if ( !unwrap ) {\r
+            if ( target == null ) {\r
+                session.getFactory().getEntityNotFoundDelegate().handleEntityNotFound( entityName, id );\r
+            }\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Getter for property 'connectedToSession'.\r
+     *\r
+     * @return Value for property 'connectedToSession'.\r
+     */\r
+    protected final boolean isConnectedToSession() {\r
+        return getProxyOrNull() != null;\r
+    }\r
+\r
+    private Object getProxyOrNull() {\r
+        final EntityKey entityKey = generateEntityKeyOrNull( getInternalIdentifier(), session, getEntityName() );\r
+        if ( entityKey != null && session != null && session.isOpenOrWaitingForAutoClose() ) {\r
+            return session.getPersistenceContextInternal().getProxy( entityKey );\r
+        }\r
+        return null;\r
+    }\r
+\r
+    @Override\r
+    public final Object getImplementation() {\r
+        initialize();\r
+        return target;\r
+    }\r
+\r
+    @Override\r
+    public final void setImplementation(Object target) {\r
+        this.target = target;\r
+        initialized = true;\r
+    }\r
+\r
+    @Override\r
+    public final Object getImplementation(SharedSessionContractImplementor s) throws HibernateException {\r
+        final EntityKey entityKey = generateEntityKeyOrNull( getInternalIdentifier(), s, getEntityName() );\r
+        return ( entityKey == null ? null : s.getPersistenceContext().getEntity( entityKey ) );\r
+    }\r
+\r
+    /**\r
+     * Getter for property 'target'.\r
+     * <p/>\r
+     * Same as {@link #getImplementation()} except that this method will not force initialization.\r
+     *\r
+     * @return Value for property 'target'.\r
+     */\r
+    protected final Object getTarget() {\r
+        return target;\r
+    }\r
+\r
+    @Override\r
+    public final boolean isReadOnlySettingAvailable() {\r
+        return (session != null && !session.isClosed());\r
+    }\r
+\r
+    private void errorIfReadOnlySettingNotAvailable() {\r
+        if ( session == null ) {\r
+            throw new TransientObjectException(\r
+                    "Proxy [" + entityName + "#" + id + "] is detached (i.e, session is null). The read-only/modifiable setting is only accessible when the proxy is associated with an open session."\r
+            );\r
+        }\r
+        if ( !session.isOpenOrWaitingForAutoClose() ) {\r
+            throw new SessionException(\r
+                    "Session is closed. The read-only/modifiable setting is only accessible when the proxy [" + entityName + "#" + id + "] is associated with an open session."\r
+            );\r
+        }\r
+    }\r
+\r
+    @Override\r
+    public final boolean isReadOnly() {\r
+        errorIfReadOnlySettingNotAvailable();\r
+        return readOnly;\r
+    }\r
+\r
+    @Override\r
+    public final void setReadOnly(boolean readOnly) {\r
+        errorIfReadOnlySettingNotAvailable();\r
+        // only update if readOnly is different from current setting\r
+        if ( this.readOnly != readOnly ) {\r
+            final EntityPersister persister = session.getFactory().getEntityPersister( entityName );\r
+            if ( !persister.isMutable() && !readOnly ) {\r
+                throw new IllegalStateException( "cannot make proxies [" + entityName + "#" + id + "] for immutable entities modifiable" );\r
+            }\r
+            this.readOnly = readOnly;\r
+            if ( initialized ) {\r
+                EntityKey key = generateEntityKeyOrNull( getInternalIdentifier(), session, getEntityName() );\r
+                final PersistenceContext persistenceContext = session.getPersistenceContext();\r
+                if ( key != null && persistenceContext.containsEntity( key ) ) {\r
+                    persistenceContext.setReadOnly( target, readOnly );\r
+                }\r
+            }\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Get the read-only/modifiable setting that should be put in affect when it is\r
+     * attached to a session.\r
+     * <p/>\r
+     * This method should only be called during serialization when read-only/modifiable setting\r
+     * is not available (i.e., isReadOnlySettingAvailable() == false)\r
+     *\r
+     * @return null, if the default setting should be used;\r
+     *         true, for read-only;\r
+     *         false, for modifiable\r
+     *\r
+     * @throws IllegalStateException if isReadOnlySettingAvailable() == true\r
+     */\r
+    public final Boolean isReadOnlyBeforeAttachedToSession() {\r
+        if ( isReadOnlySettingAvailable() ) {\r
+            throw new IllegalStateException(\r
+                    "Cannot call isReadOnlyBeforeAttachedToSession when isReadOnlySettingAvailable == true [" + entityName + "#" + id + "]"\r
+            );\r
+        }\r
+        return readOnlyBeforeAttachedToSession;\r
+    }\r
+\r
+    /**\r
+     * Get whether the proxy can load data even\r
+     * if it's not attached to a session with an ongoing transaction.\r
+     *\r
+     * This method should only be called during serialization,\r
+     * and only makes sense after a call to {@link #prepareForPossibleLoadingOutsideTransaction()}.\r
+     *\r
+     * @return {@code true} if out-of-transaction loads are allowed, {@code false} otherwise.\r
+     */\r
+    protected boolean isAllowLoadOutsideTransaction() {\r
+        return allowLoadOutsideTransaction;\r
+    }\r
+\r
+    /**\r
+     * Get the session factory UUID.\r
+     *\r
+     * This method should only be called during serialization,\r
+     * and only makes sense after a call to {@link #prepareForPossibleLoadingOutsideTransaction()}.\r
+     *\r
+     * @return the session factory UUID.\r
+     */\r
+    protected String getSessionFactoryUuid() {\r
+        return sessionFactoryUuid;\r
+    }\r
+\r
+    /**\r
+     * Restore settings that are not passed to the constructor,\r
+     * but are still preserved during serialization.\r
+     *\r
+     * This method should only be called during deserialization, before associating\r
+     * the proxy with a session.\r
+     *\r
+     * @param readOnlyBeforeAttachedToSession the read-only/modifiable setting to use when\r
+     * associated with a session; null indicates that the default should be used.\r
+     * @param sessionFactoryUuid the session factory uuid, to be used if {@code allowLoadOutsideTransaction} is {@code true}.\r
+     * @param allowLoadOutsideTransaction whether the proxy can load data even\r
+     * if it's not attached to a session with an ongoing transaction.\r
+     *\r
+     * @throws IllegalStateException if isReadOnlySettingAvailable() == true\r
+     */\r
+    /* package-private */\r
+    final void afterDeserialization(Boolean readOnlyBeforeAttachedToSession,\r
+            String sessionFactoryUuid, boolean allowLoadOutsideTransaction) {\r
+        if ( isReadOnlySettingAvailable() ) {\r
+            throw new IllegalStateException(\r
+                    "Cannot call afterDeserialization when isReadOnlySettingAvailable == true [" + entityName + "#" + id + "]"\r
+            );\r
+        }\r
+        this.readOnlyBeforeAttachedToSession = readOnlyBeforeAttachedToSession;\r
+\r
+        this.sessionFactoryUuid = sessionFactoryUuid;\r
+        this.allowLoadOutsideTransaction = allowLoadOutsideTransaction;\r
+    }\r
+\r
+    @Override\r
+    public boolean isUnwrap() {\r
+        return unwrap;\r
+    }\r
+\r
+    @Override\r
+    public void setUnwrap(boolean unwrap) {\r
+        this.unwrap = unwrap;\r
+    }\r
+}\r