org.activiti.impl.persistence.IbatisPersistenceSessionFactory.java Source code

Java tutorial

Introduction

Here is the source code for org.activiti.impl.persistence.IbatisPersistenceSessionFactory.java

Source

/* 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.activiti.impl.persistence;

import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.sql.Connection;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.sql.DataSource;

import org.activiti.ActivitiException;
import org.activiti.ActivitiWrongDbException;
import org.activiti.ProcessEngine;
import org.activiti.impl.db.IdGenerator;
import org.activiti.impl.definition.ProcessDefinitionDbImpl;
import org.activiti.impl.interceptor.CommandContext;
import org.activiti.impl.util.IoUtil;
import org.activiti.impl.variable.Type;
import org.activiti.impl.variable.VariableTypes;
import org.apache.ibatis.builder.xml.XMLConfigBuilder;
import org.apache.ibatis.datasource.pooled.PooledDataSource;
import org.apache.ibatis.mapping.Environment;
import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
import org.apache.ibatis.reflection.factory.ObjectFactory;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.defaults.DefaultSqlSessionFactory;
import org.apache.ibatis.transaction.TransactionFactory;
import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;
import org.apache.ibatis.transaction.managed.ManagedTransactionFactory;
import org.apache.ibatis.type.JdbcType;

/**
 * @author Tom Baeyens
 */
public class IbatisPersistenceSessionFactory implements PersistenceSessionFactory {

    private static Logger log = Logger.getLogger(IbatisPersistenceSessionFactory.class.getName());

    protected static List<String> statements = new ArrayList<String>();
    protected static Map<String, Map<String, String>> databaseSpecificStatements = null;
    static {
        // Default statement ids
        statements.add("selectExecution");
        statements.add("selectDbSchemaVersion");
        statements.add("selectRootExecutionsForProcessDefinition");
        statements.add("selectChildExecutions");
        statements.add("selectProcessInstanceCountByDynamicCriteria");
        statements.add("selectProcessInstanceByDynamicCriteria");
        statements.add("selectVariablesByExecutionId");
        statements.add("selectVariablesByTaskId");
        statements.add("selectTask");
        statements.add("selectTaskByExecution");
        statements.add("selectDeployments");
        statements.add("selectDeployment");
        statements.add("selectDeploymentByProcessDefinitionId");
        statements.add("selectByteArraysForDeployment");
        statements.add("selectResourceNamesForDeployment");
        statements.add("selectDeploymentResource");
        statements.add("selectBytesOfByteArray");
        statements.add("selectByteArrayById");
        statements.add("selectProcessDefinitionById");
        statements.add("selectLatestProcessDefinitionByKey");
        statements.add("selectProcessDefinitions");
        statements.add("selectProcessDefinitionIdsByDeployment");
        statements.add("selectProcessDefinitionByDeploymentAndKey");
        statements.add("selectJob");
        statements.add("selectJobs");
        statements.add("selectNextJobsToExecute");
        statements.add("selectFirstTimer");
        statements.add("selectTimersByExecutionId");
        statements.add("selectUser");
        statements.add("selectUsersByGroup");
        statements.add("selectUsers");
        statements.add("selectGroup");
        statements.add("selectGroupsByUser");
        statements.add("selectGroupsByUserAndType");
        statements.add("selectGroups");
        statements.add("selectCandidateTasks");
        statements.add("selectTasksByAssignee");
        statements.add("selectTaskByDynamicCriteria");
        statements.add("selectTaskCountByDynamicCriteria");
        statements.add("selectTaskInvolvementsByTask");
        statements.add("selectTableCount");
        statements.add("selectTableData");
        statements.add("selectProperty");

        statements.add("insertExecution");
        statements.add("insertJob");
        statements.add("insertTask");
        statements.add("insertTaskInvolvement");
        statements.add("insertVariableInstance");
        statements.add("insertByteArray");
        statements.add("insertMessage");
        statements.add("insertTimer");
        statements.add("insertDeployment");
        statements.add("insertByteArray");
        statements.add("insertProcessDefinition");
        statements.add("insertGroup");
        statements.add("insertUser");
        statements.add("insertMembership");

        statements.add("updateUser");
        statements.add("updateGroup");
        statements.add("updateProperty");
        statements.add("updateExecution");
        statements.add("updateTask");
        statements.add("updateTaskInvolvement");
        statements.add("updateVariableInstance");
        statements.add("updateByteArray");
        statements.add("updateMessage");
        statements.add("updateTimer");

        statements.add("deleteMembership");
        statements.add("deleteDeployment");
        statements.add("deleteExecution");
        statements.add("deleteTask");
        statements.add("deleteTaskInvolvement");
        statements.add("deleteVariableInstance");
        statements.add("deleteByteArray");
        statements.add("deleteJob");
        statements.add("deleteProcessDefinitionsForDeployment");
        statements.add("deleteByteArraysForDeployment");
        statements.add("deleteMembershipsForGroup");
        statements.add("deleteGroup");
        statements.add("deleteMembershipsForUser");
        statements.add("deleteUser");

        // DB specific statement ids
        // e.g. addDatabaseSpecificStatement("oracle", "selectExecution",
        // "selectExecution_oracle");
        addDatabaseSpecificStatement("mysql", "selectTaskByDynamicCriteria", "selectTaskByDynamicCriteria_mysql");
    }

    protected static void addDatabaseSpecificStatement(String databaseName, String activitiStatement,
            String ibatisStatement) {
        Map<String, String> specificStatements = null;
        if (databaseSpecificStatements == null) {
            databaseSpecificStatements = new HashMap<String, Map<String, String>>();
            specificStatements = new HashMap<String, String>();
            databaseSpecificStatements.put(databaseName, specificStatements);
        } else {
            specificStatements = databaseSpecificStatements.get(databaseName);
        }

        specificStatements.put(activitiStatement, ibatisStatement);
    }

    protected final String databaseName;
    protected final SqlSessionFactory sqlSessionFactory;
    protected final IdGenerator idGenerator;
    protected Map<String, String> databaseStatements;

    private final VariableTypes variableTypes;

    public IbatisPersistenceSessionFactory(VariableTypes variableTypes, IdGenerator idGenerator,
            String databaseName, String jdbcDriver, String jdbcUrl, String jdbcUsername, String jdbcPassword) {
        this(variableTypes, idGenerator, databaseName,
                new PooledDataSource(Thread.currentThread().getContextClassLoader(), jdbcDriver, jdbcUrl,
                        jdbcUsername, jdbcPassword),
                true);
    }

    public IbatisPersistenceSessionFactory(VariableTypes variableTypes, IdGenerator idGenerator,
            String databaseName, DataSource dataSource, boolean localTransactions) {
        this.variableTypes = variableTypes;
        this.idGenerator = idGenerator;
        this.databaseName = databaseName;
        sqlSessionFactory = createSessionFactory(dataSource,
                localTransactions ? new JdbcTransactionFactory() : new ManagedTransactionFactory());
    }

    private SqlSessionFactory createSessionFactory(DataSource dataSource, TransactionFactory transactionFactory) {
        try {
            initializeDatabaseStatements(databaseName);

            ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
            InputStream inputStream = classLoader
                    .getResourceAsStream("org/activiti/db/ibatis/activiti.ibatis.mem.conf.xml");

            // update the jdbc parameters to the configured ones...
            Environment environment = new Environment("default", transactionFactory, dataSource);
            Reader reader = new InputStreamReader(inputStream);
            XMLConfigBuilder parser = new XMLConfigBuilder(reader);
            Configuration configuration = parser.getConfiguration();
            configuration.setEnvironment(environment);
            configuration.getTypeHandlerRegistry().register(Type.class, JdbcType.VARCHAR,
                    new IbatisVariableTypeHandler(variableTypes));
            configuration.setObjectFactory(new ActivitiObjectFactory(variableTypes));
            configuration = parser.parse();

            return new DefaultSqlSessionFactory(configuration);

        } catch (Exception e) {
            throw new ActivitiException("Error while building ibatis SqlSessionFactory: " + e.getMessage(), e);
        }
    }

    // database statements //////////////////////////////////////////////////////

    protected void initializeDatabaseStatements(String databaseName) {
        databaseStatements = new HashMap<String, String>();
        for (String defaultStatement : statements) {
            databaseStatements.put(defaultStatement, defaultStatement);
        }

        Map<String, String> specificStatements = databaseSpecificStatements.get(databaseName);
        if (specificStatements != null) {
            databaseStatements.putAll(specificStatements);
        }
    }

    public String statement(String statement) {
        return databaseStatements.get(statement);
    }

    // database version check ///////////////////////////////////////////////////

    public void dbSchemaCheckVersion() {
        /*
         * Not quite sure if this is the right setting? We do want multiple updates
         * to be batched for performance ...
         */
        SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
        boolean success = false;
        try {
            String dbVersion = (String) sqlSession.selectOne(statement("selectDbSchemaVersion"));
            if (!ProcessEngine.VERSION.equals(dbVersion)) {
                throw new ActivitiWrongDbException(ProcessEngine.VERSION, dbVersion);
            }

            success = true;

        } catch (Exception e) {
            String exceptionMessage = e.getMessage();
            if ((exceptionMessage.indexOf("Table") != -1) && (exceptionMessage.indexOf("not found") != -1)) {
                throw new ActivitiException(
                        "no activiti tables in db.  set property db.schema.strategy=create-drop in activiti.properties for automatic schema creation",
                        e);
            } else {
                if (e instanceof RuntimeException) {
                    throw (RuntimeException) e;
                } else {
                    throw new ActivitiException("couldn't get db schema version", e);
                }
            }
        } finally {
            if (success) {
                sqlSession.commit(true);
            } else {
                sqlSession.rollback(true);
            }
            sqlSession.close();
        }

        log.fine("activiti db schema check successful");
    }

    public void dbSchemaCreate() {
        executeSchemaResource("create", sqlSessionFactory);
    }

    public void dbSchemaDrop() {
        executeSchemaResource("drop", sqlSessionFactory);
    }

    public void executeSchemaResource(String operation, SqlSessionFactory sqlSessionFactory) {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        boolean success = false;
        try {
            Connection connection = sqlSession.getConnection();
            ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
            String resource = "org/activiti/db/" + operation + "/activiti." + databaseName + "." + operation
                    + ".sql";
            InputStream inputStream = classLoader.getResourceAsStream(resource);
            if (inputStream == null) {
                throw new ActivitiException("resource '" + resource + "' is not available for creating the schema");
            }

            Exception exception = null;
            byte[] bytes = IoUtil.readInputStream(inputStream, resource);
            String ddlStatements = new String(bytes);
            StringTokenizer tokenizer = new StringTokenizer(ddlStatements, ";");
            while (tokenizer.hasMoreTokens()) {
                String ddlStatement = tokenizer.nextToken().trim();
                if (!ddlStatement.startsWith("#")) {
                    Statement jdbcStatement = connection.createStatement();
                    try {
                        log.fine("\n" + ddlStatement);
                        jdbcStatement.execute(ddlStatement);
                        jdbcStatement.close();
                    } catch (Exception e) {
                        if (exception == null) {
                            exception = e;
                        }
                        log.log(Level.SEVERE, "problem during schema " + operation + ", statement '" + ddlStatement,
                                e);
                    }
                }
            }

            if (exception != null) {
                throw exception;
            }

            success = true;

        } catch (Exception e) {
            throw new ActivitiException("couldn't create db schema", e);

        } finally {
            if (success) {
                sqlSession.commit(true);
            } else {
                sqlSession.rollback(true);
            }
            sqlSession.close();
        }

        log.fine("activiti db schema " + operation + " successful");
    }

    public PersistenceSession openPersistenceSession(CommandContext commandContext) {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        return new IbatisPersistenceSession(sqlSession, idGenerator, databaseStatements);
    }

    // getters and setters //////////////////////////////////////////////////////

    public SqlSessionFactory getSqlSessionFactory() {
        return sqlSessionFactory;
    }

    public IdGenerator getIdGenerator() {
        return idGenerator;
    }

    @SuppressWarnings("unchecked")
    private static class ActivitiObjectFactory implements ObjectFactory {

        private ObjectFactory delegate = new DefaultObjectFactory();
        private final VariableTypes variableTypes;

        public ActivitiObjectFactory(VariableTypes variableTypes) {
            this.variableTypes = variableTypes;
        }

        public Object create(Class type, List<Class> constructorArgTypes, List<Object> constructorArgs) {
            return delegate.create(type, constructorArgTypes, constructorArgs);
        }

        public Object create(Class type) {
            if (type == ProcessDefinitionDbImpl.class) {
                return new ProcessDefinitionDbImpl(variableTypes);
            }
            return delegate.create(type);
        }

        public void setProperties(Properties properties) {
            delegate.setProperties(properties);
        }
    }

}