Package au.edu.qut.yawl.engine

Source Code of au.edu.qut.yawl.engine.YPersistenceManager

/*
* This file is made available under the terms of the LGPL licence.
* This licence can be retreived from http://www.gnu.org/copyleft/lesser.html.
* The source remains the property of the YAWL Foundation.  The YAWL Foundation is a collaboration of
* individuals and organisations who are commited to improving workflow technology.
*
*/


package au.edu.qut.yawl.engine;


import au.edu.qut.yawl.authentication.User;
import au.edu.qut.yawl.elements.YAWLServiceReference;
import au.edu.qut.yawl.elements.state.YIdentifier;
import au.edu.qut.yawl.exceptions.YPersistenceException;
import au.edu.qut.yawl.admintool.model.HumanResourceRole;
import net.sf.hibernate.*;
import net.sf.hibernate.cfg.Configuration;
import net.sf.hibernate.tool.hbm2ddl.SchemaUpdate;
import org.apache.log4j.Logger;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.*;


/**
*
*
* @author Andrew Hastie (M2 Investments)
*         Date: 21/06/2005
*         Time: 13:46:54
*        
*         <p/>
*         This class acts as a simple manager for handling transactional persistence within the engine.<P>
*/
public class YPersistenceManager {
    /**
     * Flag indicating if we batch up all the peristence requests and action them at the point of commital, or
     * whether we preserve the Beta5 engine image style of performing the updates atomically.
     *
     * AJH: This currently does not work correctly as I've not fully completed the work on lazy caching.
     */
    private static final boolean LAZY_UPDATES = false;

    /**
     * Flag indicating if we attempt to optimise the caching of objects within the object_to_be_persisted cache.
     *
     * AJH: This currently does not work correctly as not all objects passed into persistence have the nested oobjects
     * present. Need to do more work on this.
     */
    private static final boolean OPTIMIZED_CACHING = false;

    // Persistable type constants
    private static int PT_IDENT = 1;
    private static int PT_CASE_DATA = 2;
    private static int PT_LOG_DATA = 3;
    private static int PT_LOG_IDENT = 4;
    private static int PT_NET_RUNNER = 5;
    private static int PT_SPEC_FILE = 6;
    private static int PT_WORK_ITEM = 7;
    private static int PT_WORK_ITEM_EVENT = 8;
    private static int PT_SERVICE_REF = 9;
    private static int PT_USER = 10;


    // Note: We use Hashtables rather than Hashmaps or anything from collections framework as ideally all of
    // persistence needs to be thread safe.
    private Hashtable objectsToStore;
    private Hashtable objectsToUpdate;
    private Hashtable objectsToDelete;

    private static SessionFactory factory = null;
    private Session session = null;
    private Transaction transaction = null;
    private Connection con = null;

    private static Logger logger = null;
    private boolean restoring = false;

    /**
     * Constructor
     */
    protected YPersistenceManager(SessionFactory factory) {
        logger = Logger.getLogger(this.getClass());

        objectsToStore = new Hashtable(5);        // Arbitary initial size
        objectsToUpdate = new Hashtable(5);
        objectsToDelete = new Hashtable(5);

        setFactory(factory);
    }

    protected Session getSession() {
        return session;
    }

    public Transaction getTransaction() {
        return transaction;
    }

    private void closeSession(Session session) {
        try {
            if ((session != null) && (session.isOpen())) {
                session.close();
            }
        } catch (HibernateException e) {
            logger.error("Failure to close Hibernate session", e);
        }
    }

    /**
     * Start a new Hibernate session.<P>
     *
     * @throws YPersistenceException
     */
    protected void startTransactionalSession() throws YPersistenceException {
        try {
            session = getFactory().openSession();
            transaction = session.beginTransaction();
        } catch (HibernateException e) {
            logger.fatal("Failure to start transactional session", e);
            throw new YPersistenceException("Failure to start transactional session", e);
        }
    }

    /**
     * Adds an object to be stored into the lazy storage cache.<P>
     * <p/>
     * Note: We use Hashtable rather than Hashmap or anything from collections framework as ideally all of
     * persistence needs to be thread safe.
     *
     * @param obj The object to be persisted
     */
    protected void storeObject(Object obj) throws YPersistenceException {
        if (!restoring) {
            Object objToStore = obj;

            if (obj instanceof YIdentifier) {
                P_YIdentifier py = createPY(obj);
                objToStore = py;
            } else if (obj instanceof YNetRunner) {
                YNetRunner runner = (YNetRunner) obj;
                P_YIdentifier py = createPY(runner.get_caseIDForNet());
                runner.set_standin_caseIDForNet(py);
            }

            if (logger.isDebugEnabled()) {
                String key = getHibernateIdentifier(objToStore);
                logger.debug("Adding to insert cache: Type=" + objToStore.getClass().getName() + " ID=" + key);
            }

            if (LAZY_UPDATES) {
                objectsToStore.put(getHibernateIdentifier(objToStore), objToStore);
            } else {
                doPersistAction(objToStore, false);
            }
        }
    }

    /**
     * Causes the supplied object to be persisted when the current transaction is commited.<P>
     *
     * @param obj The object to be persisted
     */
    protected void updateObject(Object obj) throws YPersistenceException {
        Object objToStore = obj;

        if (!restoring) {
            if (obj instanceof YIdentifier) {
                P_YIdentifier py = createPY(obj);
                objToStore = py;
            } else if (obj instanceof YNetRunner) {
                YNetRunner runner = (YNetRunner) obj;
                P_YIdentifier py = createPY(runner.get_caseIDForNet());
                runner.set_standin_caseIDForNet(py);
            }

            if (logger.isDebugEnabled()) {
                String key = getHibernateIdentifier(objToStore);
                logger.debug("Adding to update cache: Type=" + objToStore.getClass().getName() + " ID=" + key);
            }

            if (LAZY_UPDATES) {
                objectsToUpdate.put(getHibernateIdentifier(objToStore), objToStore);
            } else {
                doPersistAction(objToStore, true);
            }
        }
    }

    /**
     * Causes the supplied object to be persisted when the current transaction is commited.<P>
     *
     * This method simply calls {@link this.updateObject(Object)} but is public in scope. Ideally we need to dump this
     * method assuming we can refector all persistence dependant classes to be within the engine package.

     *
     * @param obj
     */
    public void storeObjectFromExternal(Object obj) throws YPersistenceException {
        storeObject(obj);
    }

    /**
     * Causes the supplied object to be updated within the persistence cache when the current transaction is
     * commited.<P>
     *
     * This method simply calls {@link this.updateObject(Object)} but is public in scope. Ideally we need to dump this
     * method assuming we can refector all persistence dependant classes to be within the engine package.
     *
     * @param obj
     */
    public void updateObjectExternal(Object obj) throws YPersistenceException {
        updateObject(obj);
    }


    /**
     * Causes the supplied object to be removed from the persistence cache when the current transaction is commited.<P>
     *
     * This method simply calls {@link this.updateObject(Object)} but is public in scope. Ideally we need to dump this
     * method assuming we can refector all persistence dependant classes to be within the engine package.

     * @param obj
     * @throws YPersistenceException
     */
    protected void deleteObject(Object obj) throws YPersistenceException {

        if (logger.isDebugEnabled()) {
            String key = getHibernateIdentifier(obj);
            logger.debug("Adding to delete cache: Type=" + obj.getClass().getName() + " ID=" + key);
        }
        try {
            if (LAZY_UPDATES) {
                objectsToDelete.put(getHibernateIdentifier(obj), obj);
            } else {
                getSession().delete(obj);
                getSession().flush();
            }
        } catch (HibernateException e) {
             logger.error("Failed to delete - " + e.getMessage());
        }

        try
        {
            getSession().evict(obj);
        }
        catch (HibernateException e)
        {
            // ignore !!!
        }


    }

    protected void commit() throws YPersistenceException {
        logger.debug("--> commit");

        if (logger.isDebugEnabled() && OPTIMIZED_CACHING) {
            dump();
        }

        if (OPTIMIZED_CACHING) {
            /**
             * Pre-process the cache prior to committing via Hibernate.
             *
             * Deletions:
             * If the same object+key exists in either the insert or update cache, thow an exception.
             *
             * Inserts:
             * If the same object+key exists within the update cache :-
             *  a] Take the version from the update cache and copy over the version in the insert cache
             *  b] Remove from the update cache
             */

            // Check for deletions+insert/updates
            {
                Enumeration enumeration = objectsToDelete.keys();

                while (enumeration.hasMoreElements()) {
                    String key = (String) enumeration.nextElement();
                    if (objectsToStore.containsKey(key)) {
                        throw new YPersistenceException("Object with key [" + key + "] present in both Delete and Insert cache");
                    }
                    if (objectsToUpdate.containsKey(key)) {
                        throw new YPersistenceException("Object with key [" + key + "] present in both Delete and Update cache");
                    }
                }
            }

            // Check for insert and updates
            {
                Enumeration enumeration = objectsToUpdate.keys();

                while (enumeration.hasMoreElements()) {
                    String key = (String) enumeration.nextElement();
                    if (objectsToStore.containsKey(key)) {
                        objectsToStore.put(key, objectsToUpdate.get(key));
                        objectsToUpdate.remove(key);
                    }
                }
            }

            if (logger.isDebugEnabled()) {
                logger.debug("POST DUPLICATE CHECK");
                dump();
            }
        }

        try
        {
            if (LAZY_UPDATES)
            {
                // Action object deletions
                {
                    Enumeration enumeration = objectsToDelete.elements();
                    while (enumeration.hasMoreElements()) {
                        removeData(enumeration.nextElement());
                    }
                }

                // Action object insertions - Note that we MUST insert the identifers first to cater for database RI rules
                {
                    {
                        Enumeration enumeration = objectsToStore.elements();
                        while (enumeration.hasMoreElements()) {
                            Object obj = enumeration.nextElement();
                            if (obj instanceof P_YIdentifier) {
                                storeData(obj);
                            }
                        }
                    }
                    {
                        Enumeration enumeration = objectsToStore.elements();
                        while (enumeration.hasMoreElements()) {
                            Object obj = enumeration.nextElement();
                            if (obj instanceof P_YIdentifier) {
                                // Null action
                            } else {
                                storeData(obj);
                            }
                        }

                    }
                }

                // Action object updates
                {
                    Enumeration enumeration = objectsToUpdate.elements();
                    while (enumeration.hasMoreElements()) {
                        updateData(enumeration.nextElement());
                    }
                }
            }

            // Commit
            getTransaction().commit();
           
        } catch (Exception e1) {
            logger.fatal("Failure to commit transactional session - Rolling Back Transaction", e1);

            try {
                getTransaction().rollback();
            } catch (Exception e2) {
                throw new YPersistenceException("Failure to rollback transactional session", e2);
            }
            throw new YPersistenceException("Failure to commit transactional session", e1);
        } finally {
            transaction = null;
            if (getSession() != null) {
                try {
                    getSession().close();
                } catch (HibernateException e) {
                    logger.warn("Failure to tidy close Hibernate session", e);
                }
            }
            session = null;
        }

        logger.debug("<-- commit");
    }

    /**
     * Forces a rollback of the current transaction,<P>
     */
    protected void rollbackTransaction() throws YPersistenceException {
        logger.debug("--> rollback Transaction");

        if (getTransaction() == null) {
            // Nothing to do ???
        } else {
            try {
                getTransaction().rollback();
            } catch (HibernateException e) {
                throw new YPersistenceException("Failure to rollback transaction", e);
            } finally {
                transaction = null;
                if (getSession() != null) {
                    try {
                        getSession().close();
                    } catch (HibernateException e) {
                        logger.warn("Failure to tidy close Hibernate session", e);
                    }
                }
                session = null;
            }
        }

        logger.debug("<-- rollback Transaction");
    }

    /**
     * Causes the supplied object to be removed from the persistence cache when the current transaction is
     * commited.<P>
     *
     * This method simply calls {@link this.removeData(Object)} but is public in scope. Ideally we need to dump this
     * method assuming we can refector all persistence dependant classes to be within the engine package.
     *
     * @param obj
     */
    public void removeDataFromExternal(Object obj) throws YPersistenceException {
        removeData(obj);
    }


    private void removeData(Object obj) throws YPersistenceException {
        logger.debug("--> removeData: " + obj);

        try {
            getSession().delete(obj);
        } catch (HibernateException e) {
            logger.error("Failure whilst removing persisted data", e);
            throw new YPersistenceException("Failure whilst removing persisted data", e);
        }
        logger.debug("<-- removeData");
    }

    private void storeData(Object obj) throws YPersistenceException {
        doPersistAction(obj, false);
    }

    private void updateData(Object obj) throws YPersistenceException {
        doPersistAction(obj, true);
    }

    public Query createQuery(String queryString) throws YPersistenceException {
        Query query = null;

        try {
            query = getSession().createQuery(queryString);
        } catch (HibernateException e) {
            throw new YPersistenceException("Failure to create Hibernate query object", e);
        }

        return query;
    }

//    public QueryResults executeQuery(String queryString) throws YPersistenceException
//    {
//        QueryResults results = new QueryResults();
//        Query query = null;
//        Session session = null;
//        Iterator iter = null;
//
//        try
//        {
//            session = getFactory().openSession();
//            session.setFlushMode(FlushMode.NEVER);
//            query = session.createQuery(queryString);
//            results.setQuery(query);
//            results.setSession(session);
//            return results;
//
//        }
//        catch (HibernateException e)
//        {
//            throw new YPersistenceException("Failure to create Hibernate query object", e);
//        }
//    }


    private void doPersistAction(Object obj, boolean update) throws YPersistenceException {
        if (logger.isDebugEnabled()) {
            logger.debug("--> doPersistAction: Object=" + obj.getClass().getName() + " Key=" + getHibernateIdentifier(obj) + (update ? " Mode=Update" : " Mode=Create"));
        }
        try {
            if (update) {
                getSession().update(obj);
                getSession().flush();
            } else {
                getSession().save(obj);
                getSession().flush();
            }
        } catch (Exception e) {
            logger.error("Failure detected whilst persisting instance of " + obj.getClass().getName(), e);
        }

        try
        {
            getSession().evict(obj);
        }
        catch (HibernateException e)
        {
            logger.warn("Failure whilst evicting object from Hibernate session cache", e);
        }
    }

    protected P_YIdentifier createPY(Object obj) {
        YIdentifier yid = (YIdentifier) obj;
        P_YIdentifier py = new P_YIdentifier();
        py.set_idString(yid.get_idString());
        List list = yid.get_children();
        List plist = new Vector();

        py.setLocationNames(yid.getLocationNames());

        /*
          The children must be p_yidentifiers instead of normal yidentifiers
        */
        for (int i = 0; i < list.size(); i++) {
            YIdentifier child = (YIdentifier) list.get(i);
            plist.add(createPY(child));
        }
        py.set_children(plist);

        return py;
    }

    protected static SessionFactory initialise(boolean journalising) throws YPersistenceException {
        String loggerName = "au.edu.qut.yawl.engine.YPersistenceManager";
        SessionFactory factory = null;
        Connection con = null;
        Transaction tx = null;
        Configuration cfg = null;

        // Create the Hibernate config, check and create database if required, and generally set things up .....
        if (journalising) {
            try {
                cfg = new Configuration();
                cfg.addClass(YSpecFile.class);
                cfg.addClass(YNetRunner.class);
                cfg.addClass(YWorkItem.class);
                cfg.addClass(P_YIdentifier.class);
                cfg.addClass(YCaseData.class);
                cfg.addClass(YLogData.class);
                cfg.addClass(YWorkItemEvent.class);
                cfg.addClass(YLogIdentifier.class);
                cfg.addClass(YAWLServiceReference.class);

                cfg.addClass(au.edu.qut.yawl.admintool.model.Resource.class);
                cfg.addClass(au.edu.qut.yawl.admintool.model.Role.class);
                cfg.addClass(au.edu.qut.yawl.admintool.model.HumanResourceRole.class);
                cfg.addClass(au.edu.qut.yawl.admintool.model.Capability.class);
                cfg.addClass(au.edu.qut.yawl.admintool.model.ResourceCapability.class);
                cfg.addClass(au.edu.qut.yawl.admintool.model.OrgGroup.class);
                cfg.addClass(au.edu.qut.yawl.admintool.model.Position.class);
                cfg.addClass(au.edu.qut.yawl.admintool.model.HResOccupiesPosition.class);
                cfg.addClass(au.edu.qut.yawl.exceptions.Problem.class);

                factory = cfg.buildSessionFactory();

                Logger.getLogger(loggerName).debug("Validating existance of database for persistence");

                boolean createtables = false;

                /*
                  Execute a select statement to see if tables are there
                 */
                Session session = factory.openSession();

                //AJH: See if we can establish a connection
                try {
                    con = session.connection();
                } catch (Exception e) {
                    String msg = "Failure to establish connection to persistance database";
                    Logger.getLogger(loggerName).fatal(msg, e);
                    throw new YPersistenceException(msg, e);
                }

                //AJH: Validate database connection by Selecting some data
                //todo Need a better database validation mechanism here
                Statement st = null;
                ResultSet rs = null;
                try {
                    tx = session.beginTransaction();
                    st = session.connection().createStatement();
                    rs = st.executeQuery("select * from specs");
                    tx.commit();
                } catch (Exception e) {
                    if (tx != null) {
                        tx.rollback();
                    }
                    Logger.getLogger(loggerName).warn("Database does not appear to exist - Attempting to create new database ...");
                    new SchemaUpdate(cfg).execute(false, true);
                    HumanResourceRole.addIntegrityEnforcements(session);

                } finally {
                    session.close();
                }

            } catch (Exception e) {
                e.printStackTrace();
                Logger.getLogger(loggerName).fatal("Failure initialising persistence layer", e);
                throw new YPersistenceException("Failure initialising persistence layer", e);
            }
        }
        // Finally, return the session factory
        return factory;
    }



    public String getMaxCase() {
        Session session = null;

        try {
            session = factory.openSession();

            Query query = session.createQuery("select from YLogIdentifier order by created desc");
            for (Iterator it = query.iterate(); it.hasNext();) {
                YLogIdentifier logid = (YLogIdentifier) it.next();
                session.close();
                return logid.getIdentifier();
            }
            session.close();
            return "0";
        } catch (Exception e) {
            e.printStackTrace();
            return "-1";
        }
    }

    public SessionFactory getFactory() {
        return factory;
    }

    private void setFactory(SessionFactory factory) {
        this.factory = factory;
    }

    /**
     * Derives a storage key for persistable objects. This key is used as a hashtable key and
     * is based upon the object type being persisted together with its Hibernate identifier.
     *
     * @param obj
     * @return
     * @throws YPersistenceException
     */
    private String getHibernateIdentifier(Object obj) throws YPersistenceException {
        String key = null;
        int objectType = 0;

        if (obj instanceof P_YIdentifier) {
            objectType = PT_IDENT;
            P_YIdentifier obj2 = (P_YIdentifier) obj;
            key = objectType + "/" + obj2.get_idString();
        } else if (obj instanceof YCaseData) {
            objectType = PT_CASE_DATA;
            YCaseData obj2 = (YCaseData) obj;
            key = obj2.getId();
        } else if (obj instanceof YLogData) {
            objectType = PT_LOG_DATA;
            key = objectType + "/" + new Date().getTime();
        } else if (obj instanceof YLogIdentifier) {
            objectType = PT_LOG_IDENT;
            YLogIdentifier obj2 = (YLogIdentifier) obj;
            key = obj2.getIdentifier();
        } else if (obj instanceof YNetRunner) {
            objectType = PT_NET_RUNNER;
            YNetRunner obj2 = (YNetRunner) obj;
            key = objectType + "/" + obj2.get_caseID();
        } else if (obj instanceof YSpecFile) {
            objectType = PT_SPEC_FILE;
            YSpecFile obj2 = (YSpecFile) obj;
            key = objectType + "/" + obj2.getId();
        } else if (obj instanceof YWorkItem) {
            objectType = PT_WORK_ITEM;
            YWorkItem obj2 = (YWorkItem) obj;
            //todo (by LJA) shouldn't the workitem use getUniqueID() ??
            key = objectType + "/" + obj2.thisId;
        } else if (obj instanceof YWorkItemEvent) {
            objectType = PT_WORK_ITEM_EVENT;
            YWorkItemEvent obj2 = (YWorkItemEvent) obj;
            key = obj2.getRowkey();
        } else if (obj instanceof YAWLServiceReference) {
            objectType = PT_SERVICE_REF;
            YAWLServiceReference obj2 = (YAWLServiceReference) obj;
            key = objectType + "/" + obj2.get_yawlServiceID();
        } else if (obj instanceof User) {
            objectType = PT_USER;
            User obj2 = (User) obj;
            key = obj2.getUserID();
        } else {
            logger.error("Unknown object type [" + obj.getClass().getName() + "]");
            throw new YPersistenceException("Unknown object type [" + obj.getClass().getName() + "]");
        }

        return key;
    }

    public boolean isRestoring() {
        return restoring;
    }

    protected void setRestoring(boolean restoring) {
        this.restoring = restoring;
    }

    private void dump() {
        logger.debug("*** DUMP OF PERSISTENCE_MANAGER CACHES STARTS ***");

        if (objectsToStore.size() > 0) {
            logger.debug("*** INSERTIONS ***");
            {
                Enumeration enumeration = objectsToStore.keys();
                while (enumeration.hasMoreElements()) {
                    String key = (String) enumeration.nextElement();
                    logger.debug(objectsToStore.get(key).getClass().getName() + " " + key);
                }
            }
        }

        if (objectsToUpdate.size() > 0) {
            logger.debug("*** UPDATES ***");
            {
                Enumeration enumeration = objectsToUpdate.keys();
                while (enumeration.hasMoreElements()) {
                    String key = (String) enumeration.nextElement();
                    logger.debug(objectsToUpdate.get(key).getClass().getName() + " " + key);
                }
            }
        }

        if (objectsToDelete.size() > 0) {
            logger.debug("*** DELETIONS ***");
            {
                Enumeration enumeration = objectsToDelete.keys();
                while (enumeration.hasMoreElements()) {
                    String key = (String) enumeration.nextElement();
                    logger.debug(objectsToDelete.get(key).getClass().getName() + " " + key);

                }
            }
        }
        logger.debug("*** DUMP OF PERSISTENCE_MANAGER CACHES ENDS ***");
    }
}
TOP

Related Classes of au.edu.qut.yawl.engine.YPersistenceManager

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.