Package org.springframework.webflow.persistence

Source Code of org.springframework.webflow.persistence.HibernateFlowExecutionListener

/*
* Copyright 2004-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.webflow.persistence;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import org.hibernate.FlushMode;
import org.hibernate.Interceptor;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.orm.hibernate4.SessionHolder;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.webflow.core.collection.AttributeMap;
import org.springframework.webflow.core.collection.MutableAttributeMap;
import org.springframework.webflow.definition.FlowDefinition;
import org.springframework.webflow.execution.FlowExecutionException;
import org.springframework.webflow.execution.FlowExecutionListener;
import org.springframework.webflow.execution.FlowExecutionListenerAdapter;
import org.springframework.webflow.execution.FlowSession;
import org.springframework.webflow.execution.RequestContext;

/**
* A {@link FlowExecutionListener} that implements the Flow Managed Persistence Context (FMPC) pattern using the native
* Hibernate API.
* <p>
* The general pattern is as follows:
* <ul>
* <li>When a flow execution starts, create a new Hibernate Session and bind it to flow scope under the name
* {@link #PERSISTENCE_CONTEXT_ATTRIBUTE}.
* <li>Before processing a flow execution request, expose the conversationally-bound session as the "current session"
* for the current thread.
* <li>When an existing flow pauses, unbind the session from the current thread.
* <li>When an existing flow ends, commit the changes made to the session in a transaction if the ending state is a
* commit state. Then, unbind the context and close it.
* </ul>
*
* The general data access pattern implemented here is:
* <ul>
* <li>Create a new persistence context when a new flow execution with the 'persistenceContext' attribute starts
* <li>Load some objects into this persistence context
* <li>Perform edits to those objects over a series of requests into the flow
* <li>On successful flow completion, commit and flush those edits to the database, applying a version check if
* necessary.
* </ul>
*
* <p>
* Note: All data access except for the final commit will, by default, be non-transactional. However, a flow may call
* into a transactional service layer to fetch objects during the conversation in the context of a read-only system
* transaction. In that case, the session's flush mode will be set to Manual and no intermediate changes will be
* flushed.
* <p>
* Care should be taken to prevent premature commits of flow data while the flow is in progress. You would generally not
* want intermediate flushing to happen, as the nature of a flow implies a transient, isolated resource that can be
* canceled before it ends. Generally, the only time a read-write transaction should be started is upon successful
* completion of the conversation, triggered by reaching a 'commit' end state.
*
* @author Keith Donald
* @author Juergen Hoeller
* @author Ben Hale
*/
public class HibernateFlowExecutionListener extends FlowExecutionListenerAdapter {

  private static final boolean hibernate3Present = ClassUtils.isPresent("org.hibernate.connection.ConnectionProvider",
      HibernateFlowExecutionListener.class.getClassLoader());

  private static final Method openSessionMethod =
      ReflectionUtils.findMethod(SessionFactory.class, "openSession");

  private static final Method openSessionWithInterceptorMethod =
      ReflectionUtils.findMethod(SessionFactory.class, "openSession", Interceptor.class);

  private static final Method currentSessionMethod = ClassUtils.getMethod(SessionFactory.class, "getCurrentSession");


  /**
   * The name of the attribute the flow {@link Session persistence context} is indexed under.
   */
  public static final String PERSISTENCE_CONTEXT_ATTRIBUTE = "persistenceContext";

  private SessionFactory sessionFactory;

  private TransactionTemplate transactionTemplate;

  private Interceptor entityInterceptor;

  /**
   * Create a new Hibernate Flow Execution Listener using giving Hibernate session factory and transaction manager.
   * @param sessionFactory the session factory to use
   * @param transactionManager the transaction manager to drive transactions
   */
  public HibernateFlowExecutionListener(SessionFactory sessionFactory, PlatformTransactionManager transactionManager) {
    this.sessionFactory = sessionFactory;
    this.transactionTemplate = new TransactionTemplate(transactionManager);
  }

  /**
   * Sets the entity interceptor to attach to sessions opened by this listener.
   * @param entityInterceptor the entity interceptor
   */
  public void setEntityInterceptor(Interceptor entityInterceptor) {
    this.entityInterceptor = entityInterceptor;
  }

  public void sessionStarting(RequestContext context, FlowSession session, MutableAttributeMap<?> input) {
    boolean reusePersistenceContext = false;
    if (isParentPersistenceContext(session)) {
      if (isPersistenceContext(session.getDefinition())) {
        setHibernateSession(session, getHibernateSession(session.getParent()));
        reusePersistenceContext = true;
      } else {
        unbind(getHibernateSession(session.getParent()));
      }
    }
    if (isPersistenceContext(session.getDefinition()) && (!reusePersistenceContext)) {
      Session hibernateSession = createSession(context);
      setHibernateSession(session, hibernateSession);
      bind(hibernateSession);
    }
  }

  public void paused(RequestContext context) {
    if (isPersistenceContext(context.getActiveFlow())) {
      Session session = getHibernateSession(context.getFlowExecutionContext().getActiveSession());
      unbind(session);
      session.disconnect();
    }
  }

  public void resuming(RequestContext context) {
    if (isPersistenceContext(context.getActiveFlow())) {
      bind(getHibernateSession(context.getFlowExecutionContext().getActiveSession()));
    }
  }

  public void sessionEnding(RequestContext context, FlowSession session, String outcome, MutableAttributeMap<?> output) {
    if (isParentPersistenceContext(session)) {
      return;
    }
    if (isPersistenceContext(session.getDefinition())) {
      final Session hibernateSession = getHibernateSession(session);
      Boolean commitStatus = session.getState().getAttributes().getBoolean("commit");
      if (Boolean.TRUE.equals(commitStatus)) {
        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
          protected void doInTransactionWithoutResult(TransactionStatus status) {
            if (hibernate3Present) {
              ReflectionUtils.invokeMethod(currentSessionMethod, sessionFactory);
            }
            else {
              sessionFactory.getCurrentSession();
            }
            // nothing to do; a flush will happen on commit automatically as this is a read-write
            // transaction
          }
        });
      }
      unbind(hibernateSession);
      hibernateSession.close();
    }
  }

  public void sessionEnded(RequestContext context, FlowSession session, String outcome, AttributeMap<?> output) {
    if (isParentPersistenceContext(session)) {
      if (!isPersistenceContext(session.getDefinition())) {
        bind(getHibernateSession(session.getParent()));
      }
    }
  }

  public void exceptionThrown(RequestContext context, FlowExecutionException exception) {
    if (context.getFlowExecutionContext().isActive()) {
      if (isPersistenceContext(context.getActiveFlow())) {
        unbind(getHibernateSession(context.getFlowExecutionContext().getActiveSession()));
      }
    }
  }

  // internal helpers

  private boolean isPersistenceContext(FlowDefinition flow) {
    return flow.getAttributes().contains(PERSISTENCE_CONTEXT_ATTRIBUTE);
  }

  private boolean isParentPersistenceContext(FlowSession flowSession) {
    return ((!flowSession.isRoot()) && isPersistenceContext(flowSession.getParent().getDefinition()));
  }

  private Session createSession(RequestContext context) {
    Session session;
    if (entityInterceptor != null) {
      if (hibernate3Present) {
        try {
          session = (Session) openSessionWithInterceptorMethod.invoke(sessionFactory, entityInterceptor);
        } catch (IllegalAccessException ex) {
          throw new IllegalStateException("Unable to open Hibernate 3 session", ex);
        } catch (InvocationTargetException ex) {
          throw new IllegalStateException("Unable to open Hibernate 3 session", ex);
        }
      } else {
        session = sessionFactory.withOptions().interceptor(entityInterceptor).openSession();
      }
    } else {
      if (hibernate3Present) {
        try {
          session = (Session) openSessionMethod.invoke(sessionFactory);
        } catch (IllegalAccessException ex) {
          throw new IllegalStateException("Unable to open Hibernate 3 session", ex);
        } catch (InvocationTargetException ex) {
          throw new IllegalStateException("Unable to open Hibernate 3 session", ex);
        }
      }
      else {
        session = sessionFactory.openSession();
      }
    }
    session.setFlushMode(FlushMode.MANUAL);
    return session;
  }

  private Session getHibernateSession(FlowSession session) {
    return (Session) session.getScope().get(PERSISTENCE_CONTEXT_ATTRIBUTE);
  }

  private void setHibernateSession(FlowSession session, Session hibernateSession) {
    session.getScope().put(PERSISTENCE_CONTEXT_ATTRIBUTE, hibernateSession);
  }

  private void bind(Session session) {
    Object sessionHolder = (hibernate3Present ?
        new org.springframework.orm.hibernate3.SessionHolder(session) : new SessionHolder(session));
    TransactionSynchronizationManager.bindResource(sessionFactory, sessionHolder);
  }

  private void unbind(Session session) {
    if (TransactionSynchronizationManager.hasResource(sessionFactory)) {
      TransactionSynchronizationManager.unbindResource(sessionFactory);
    }
  }
}
TOP

Related Classes of org.springframework.webflow.persistence.HibernateFlowExecutionListener

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.