com.autobizlogic.abl.session.LogicTransactionContext.java Source code

Java tutorial

Introduction

Here is the source code for com.autobizlogic.abl.session.LogicTransactionContext.java

Source

package com.autobizlogic.abl.session;

import java.io.Serializable;
//import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
import java.util.concurrent.CopyOnWriteArrayList;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.metadata.ClassMetadata;
import org.hibernate.proxy.HibernateProxy;

import com.autobizlogic.abl.data.PersistentBean;
import com.autobizlogic.abl.hibernate.LogicEventListener.QueuedEventPhase;
import com.autobizlogic.abl.metadata.MetaModel;
import com.autobizlogic.abl.metadata.MetaModelFactory;
import com.autobizlogic.abl.engine.LogicRunner;
import com.autobizlogic.abl.engine.LogicRunner.LogicProcessingState;
import com.autobizlogic.abl.rule.ActionRule;
import com.autobizlogic.abl.engine.LogicException;
import com.autobizlogic.abl.event.GlobalLogicEventHandler;
import com.autobizlogic.abl.event.LogicEvent;
import com.autobizlogic.abl.event.TransactionSummary;
import com.autobizlogic.abl.event.ObjectEvent.EventType;
import com.autobizlogic.abl.util.LogicLogger;

/**
 * The class that holds all the information needed by the business rules engine
 * to keep track of the current state of things. This object is created whenever
 * a Hibernate transaction is started, and it goes away once the transaction ends.
 * <br>
 * While the transaction is open, the current instance of this class can be retrieved
 * using LogicTransactionManager:<br>
 * <code>LogicTransactionManager.getCurrentLogicTransactionContext()</code>
 */
public class LogicTransactionContext {

    private int stackLevel = 0;

    private Session topLevelSession;

    /**
     * A handle to the current Hibernate session.
     */
    private Session session;

    private QueuedEventPhase queuedEventPhase = QueuedEventPhase.SUBMIT;

    private String useCaseName = null;

    private List<LogicRunner> objectsToProcess = new CopyOnWriteArrayList<LogicRunner>();

    /**
    private List<LogicRunner> objectsToProcess = new Vector<LogicRunner>();
        
    /**
     * Keep track of objects that have been deleted. The main key is the entity name, and in the
     * value, the key is the PK and the value is the PersistentBean itself.
     */
    private Map<String, Map<Serializable, PersistentBean>> deletedObjectStates = new HashMap<String, Map<Serializable, PersistentBean>>();

    /**
     * Keep track of all the Hibernate proxies in which we have introduced our own proxies.
     * This allows us to clean them up at the end of the transaction.
     * 
     * Note : we use a Vector and not a Set because HashSet calls hashCode on all collection members, and
     * that can have unfortunate effects with Hibernate proxies.
     */
    //   private Collection<Object> beanProxies = new Vector<Object>();

    /**
     * Objects processed by LogicRunner at nestLevel = 0
     */
    private Set<PersistentBean> userSubmittedObjects = new HashSet<PersistentBean>();

    private LogicRunner masterRunner = null; // first runner we see

    /**
     * for key(domain object), set of actions already run
     */
    private Map<Object, Set<ActionRule>> executedActions = new HashMap<Object, Set<ActionRule>>();

    /**
     * Keep track of all modified objects within this transaction.
     */
    private TransactionSummary transactionSummary = new TransactionSummary();

    @SuppressWarnings("unused")
    private final static LogicLogger log = LogicLogger.getLogger(LogicLogger.LoggerName.PERSISTENCE);

    public void incrementStackLevel() {
        stackLevel++;
    }

    public void decrementStackLevel() {
        stackLevel--;
        if (stackLevel < 0)
            throw new LogicException("LogicTransactionContext stack level is less than 0!");
    }

    public int getStackLevel() {
        return stackLevel;
    }

    public Session getTopLevelSession() {
        return topLevelSession;
    }

    public void setTopLevelSession(Session sess) {
        topLevelSession = sess;
    }

    /**
     * Get the Hibernate session for this context
     */
    public Session getSession() {
        return session;
    }

    /**
     * Get the MetaModel for this context
     */
    public MetaModel getMetaModel() {
        return MetaModelFactory.getHibernateMetaModel(session.getSessionFactory());
    }

    /**
     * Initialize this context with a session
     */
    public void setSession(Session session) {
        this.session = session;
    }

    public LogicRunner getMasterRunner() {
        return masterRunner;
    }

    public void setMasterRunner(LogicRunner masterRunner) {
        this.masterRunner = masterRunner;
    }

    public Set<PersistentBean> getUserSubmittedObjects() {
        return userSubmittedObjects;
    }

    public void setUserSubmittedObjects(Set<PersistentBean> userSubmittedObjects) {
        this.userSubmittedObjects = userSubmittedObjects;
    }

    /**
     * Get all the logic runners currently lined up to run.
     */
    public List<LogicRunner> getObjectsToProcess() {
        return objectsToProcess;
    }

    /**
     * Add the given LogicRunner to the list of objects to process.
     * @param logicRunner A LogicRunner that will get added to the end of the queue.
     */
    public void addObjectToProcess(LogicRunner logicRunner) {
        objectsToProcess.add(logicRunner);
    }

    /**
     * Look through all the logic runners that have not yet run for any that is for
     * the given bean.
     * @param bean The bean in question
     * @return Null if no logic runner is queued for this bean, otherwise the logic runner.
     */
    public LogicRunner findObjectToProcess(PersistentBean bean) {

        if (bean == null)
            return null;
        for (LogicRunner runner : objectsToProcess) {
            if (bean.equals(runner.getCurrentDomainObject())
                    && runner.getLogicProcessingState() == LogicProcessingState.QUEUED)
                return runner;
        }
        return null;
    }

    /**
     * Determine whether the given logic runner is queued for execution.
     */
    public boolean logicRunnerIsQueued(LogicRunner logicRunner) {
        if (!objectsToProcess.contains(logicRunner))
            return false;
        if (logicRunner.getLogicProcessingState() != LogicProcessingState.QUEUED)
            return false;

        return true;
    }

    /**
     * Keep track of all logic runners created within the scope of a transaction. The idea is to keep
     * only the latest LogicRunner for each instance of each class, so that we can reuse them at the end
     * for commit actions and commit constraints.
     */
    private Map<String, Map<Serializable, LogicRunner>> allLogicRunners = new HashMap<String, Map<Serializable, LogicRunner>>();

    /**
     * This must be called by anyone who creates a LogicRunner. It keeps track of them so that,
     * at the end of a transaction, we can run the commit-time actions and constraints.
     */
    public void registerLogicRunner(LogicRunner runner) {
        String entityName = runner.getCurrentDomainObject().getEntityName();
        Map<Serializable, LogicRunner> runnersForClass = allLogicRunners.get(entityName);
        if (runnersForClass == null) {
            runnersForClass = new HashMap<Serializable, LogicRunner>();
            allLogicRunners.put(entityName, runnersForClass);
        }
        Serializable pk = runner.getCurrentDomainObject().getPk();
        runnersForClass.put(pk, runner); // Note that this will overwrite a LogicRunner already there for this object. This is as intended.
    }

    /**
     * Get the LogicRunners for all objects touched during the transaction.
     */
    public Set<LogicRunner> getAllLogicRunners() {
        Set<LogicRunner> allRunners = new HashSet<LogicRunner>();
        for (Map<Serializable, LogicRunner> runnerEntry : allLogicRunners.values()) {
            allRunners.addAll(runnerEntry.values());
        }
        return allRunners;
    }

    /**
     * See if we have a LogicRunner for the given PersistentBean.
     * @param aBean The bean to check for
     * @return <em>any</em> LogicRunner whose currentDomainObject matches aBean, or null
     * if none is found.
     */
    public LogicRunner findLogicRunner(PersistentBean aBean) {

        if (aBean == null)
            return null;

        for (LogicRunner eachLogicRunner : objectsToProcess) {
            PersistentBean eachLogicRunnerCurrentState = eachLogicRunner.getCurrentDomainObject();
            if (eachLogicRunnerCurrentState.equals(aBean))
                return eachLogicRunner;
        }
        return null;
    }

    /**
     * Find the most-recently-queued LogicRunner for the given PersistentBean.
     * @return Null if none is found.
     */
    public LogicRunner findNewestLogicRunner(PersistentBean aBean) {
        Vector<LogicRunner> revObjectsToProcess = new Vector<LogicRunner>(objectsToProcess);
        Collections.reverse(revObjectsToProcess);
        for (LogicRunner eachLogicRunner : revObjectsToProcess) {
            PersistentBean eachLogicRunnerCurrentState = eachLogicRunner.getCurrentDomainObject();
            if (eachLogicRunnerCurrentState.equals(aBean))
                return eachLogicRunner;
        }
        return null;
    }

    /**
     * Record the state of a deleted object for the transaction.
     */
    public void addDeletedObjectState(PersistentBean bean) {
        if (bean == null)
            throw new RuntimeException("Cannot add null object to deleted object states");
        Map<Serializable, PersistentBean> deleted = deletedObjectStates.get(bean.getEntityName());
        if (deleted == null) {
            deleted = new HashMap<Serializable, PersistentBean>();
            deletedObjectStates.put(bean.getEntityName(), deleted);
        }
        deleted.put(bean.getPk(), bean);
    }

    /**
     * Add an object event to the transaction summary.
     * @param bean The bean
     * @param pk The bean's primary key
     * @param eventType What type of event this is
     */
    public void addObjectEvent(PersistentBean bean, EventType eventType, Session theSession) {

        transactionSummary.addObjectEvent(bean, eventType, theSession);
    }

    /**
     * Get the TransactionSummary for this transaction.
     */
    public TransactionSummary getTransactionSummary() {
        return transactionSummary;
    }

    /**
     * Get the deleted ObjectState for the given object.
     * @param pBean The entity to check for
     * @return The PersistentBean for the given object at the moment it was deleted, or null
     * if the object is null, or was not deleted.
     */
    public PersistentBean getDeletedObjectState(PersistentBean pBean) {
        if (pBean == null)
            return null;
        Map<Serializable, PersistentBean> deleted = deletedObjectStates.get(pBean.getEntityName());
        if (deleted == null)
            return null;

        return deleted.get(pBean.getPk());
    }

    /**
     * Has the given object been deleted within this transaction? This works with a full object or a proxy.
     * @return True if the given object was deleted within this transaction, false otherwise or if the
     * given object is null.
     */
    public boolean objectIsDeleted(PersistentBean pBean) {
        return getDeletedObjectState(pBean) != null;
    }

    //   public void registerBeanProxy(Object proxy) {
    //      beanProxies.add(proxy);
    //   }

    /**
     * Get access to the cache of beans that have been proxied with BeanProxyHandler.
     * This is purely internal.
     */
    //   public Collection<Object> getBeanProxies() {
    //      return beanProxies;
    //   }

    public QueuedEventPhase getQueuedEventPhase() {
        return queuedEventPhase;
    }

    public void setQueuedEventPhase(QueuedEventPhase queuedEventPhase) {
        this.queuedEventPhase = queuedEventPhase;
    }

    public Map<Object, Set<ActionRule>> getExecutedActions() {
        return executedActions;
    }

    public void setExecutedActions(Map<Object, Set<ActionRule>> executedActions) {
        this.executedActions = executedActions;
    }

    /**
     * Given an object, which could be either an entity or a proxy, get its primary key.
     */
    public Serializable getPrimaryKeyForObject(Object object) {
        if (object == null)
            return null;

        Serializable fk = null;
        if (object instanceof HibernateProxy) {
            HibernateProxy proxy = (HibernateProxy) object;
            fk = proxy.getHibernateLazyInitializer().getIdentifier();
        } else {
            SessionFactory sessionFactory = session.getSessionFactory();
            ClassMetadata classMeta = sessionFactory.getClassMetadata(object.getClass());
            fk = classMeta.getIdentifier(object, (SessionImplementor) session);
        }
        return fk;
    }

    /**
     * Get the use case name for this context.
     */
    public String getUseCaseName() {
        return useCaseName;
    }

    /**
     * Set the use case name for this context.
     * @param useCaseName
     */
    public void setUseCaseName(String useCaseName) {
        this.useCaseName = useCaseName;
    }

    /**
     * Fire the given event with whoever should be notified.
     */
    public static void fireEvent(LogicEvent evt) {
        GlobalLogicEventHandler.getGlobalLogicListenerHandler().fireEvent(evt);
    }

    ////////////////////////////////////////////////////////////////////////////////////////

    @Override
    public String toString() {
        StringBuffer sb = new StringBuffer();
        sb.append("LogicTransactionContext: ");
        if (useCaseName != null)
            sb.append(" [use case : " + useCaseName + "]");
        return sb.toString();
    }

    @SuppressWarnings("unused")
    private final static String SVN_ID = "$Id: Version 2.1.5 Build 0602 Date 2012-04-28-14-13  LogicTransactionContext.java 952 2012-03-16 11:03:02Z max@automatedbusinesslogic.com $";
}

/*
 * The contents of this file are subject to the Automated Business Logic Public License Version 1.0 (the "License"),
 * which is derived from the Mozilla Public License version 1.1. You may not use this file except in compliance with the License. 
 * You may obtain a copy of the License at http://www.automatedbusinesslogic.com/license/public-license
 *
 * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, 
 * either express or implied. See the License for the specific language governing rights and limitations under the License.
 */