org.activiti.engine.impl.persistence.db.DbSqlSessionFactory.java Source code

Java tutorial

Introduction

Here is the source code for org.activiti.engine.impl.persistence.db.DbSqlSessionFactory.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.engine.impl.persistence.db;

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

import javax.sql.DataSource;

import org.activiti.engine.ActivitiException;
import org.activiti.engine.ActivitiWrongDbException;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.impl.cfg.IdGenerator;
import org.activiti.engine.impl.cfg.ProcessEngineConfiguration;
import org.activiti.engine.impl.cfg.ProcessEngineConfigurationAware;
import org.activiti.engine.impl.interceptor.Session;
import org.activiti.engine.impl.interceptor.SessionFactory;
import org.activiti.engine.impl.persistence.PersistentObject;
import org.activiti.engine.impl.util.IoUtil;
import org.activiti.engine.impl.variable.Type;
import org.activiti.pvm.impl.util.ClassNameUtil;
import org.apache.ibatis.builder.xml.XMLConfigBuilder;
import org.apache.ibatis.datasource.pooled.PooledDataSource;
import org.apache.ibatis.mapping.Environment;
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 DbSqlSessionFactory implements SessionFactory, ProcessEngineConfigurationAware {

    private static Logger log = Logger.getLogger(DbSqlSessionFactory.class.getName());
    protected static final Map<String, Map<String, String>> databaseSpecificStatements = new HashMap<String, Map<String, String>>();

    static {
        addDatabaseSpecificStatement("mysql", "selectTaskByDynamicCriteria", "selectTaskByDynamicCriteria_mysql");
        addDatabaseSpecificStatement("mysql", "selectNextJobsToExecute", "selectNextJobsToExecute_mysql");
    }

    protected String databaseName;
    protected SqlSessionFactory sqlSessionFactory;
    protected IdGenerator idGenerator;
    protected Map<String, String> statementMappings;
    protected Map<Class<?>, String> insertStatements = Collections.synchronizedMap(new HashMap<Class<?>, String>());
    protected Map<Class<?>, String> updateStatements = Collections.synchronizedMap(new HashMap<Class<?>, String>());
    protected Map<Class<?>, String> deleteStatements = Collections.synchronizedMap(new HashMap<Class<?>, String>());

    public void configurationCompleted(ProcessEngineConfiguration processEngineConfiguration) {
        this.databaseName = processEngineConfiguration.getDatabaseName();
        this.idGenerator = processEngineConfiguration.getIdGenerator();
        this.statementMappings = databaseSpecificStatements.get(processEngineConfiguration.getDatabaseName());

        DataSource dataSource = processEngineConfiguration.getDataSource();
        if (dataSource == null) {

            String jdbcDriver = processEngineConfiguration.getJdbcDriver();
            String jdbcUrl = processEngineConfiguration.getJdbcUrl();
            String jdbcUsername = processEngineConfiguration.getJdbcUsername();
            String jdbcPassword = processEngineConfiguration.getJdbcPassword();

            if ((jdbcDriver == null) || (jdbcUrl == null) || (jdbcUsername == null)) {
                throw new ActivitiException(
                        "DataSource or JDBC properties have to be specified in a process engine configuration");
            }

            dataSource = new PooledDataSource(Thread.currentThread().getContextClassLoader(), jdbcDriver, jdbcUrl,
                    jdbcUsername, jdbcPassword);
        }

        TransactionFactory transactionFactory = null;
        if (processEngineConfiguration.isLocalTransactions()) {
            transactionFactory = new JdbcTransactionFactory();
        } else {
            transactionFactory = new ManagedTransactionFactory();
        }

        this.sqlSessionFactory = createSessionFactory(dataSource, transactionFactory);
    }

    protected SqlSessionFactory createSessionFactory(DataSource dataSource, TransactionFactory transactionFactory) {
        try {
            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());
            configuration = parser.parse();

            return new DefaultSqlSessionFactory(configuration);

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

    public Session openSession() {
        return new DbSqlSession(this);
    }

    // insert, update and delete statements /////////////////////////////////////

    public String getInsertStatement(PersistentObject object) {
        return getStatement(object.getClass(), insertStatements, "insert");
    }

    public String getUpdateStatement(PersistentObject object) {
        return getStatement(object.getClass(), updateStatements, "update");
    }

    public String getDeleteStatement(Class<?> persistentObjectClass) {
        return getStatement(persistentObjectClass, deleteStatements, "delete");
    }

    private String getStatement(Class<?> persistentObjectClass, Map<Class<?>, String> cachedStatements,
            String prefix) {
        String statement = cachedStatements.get(persistentObjectClass);
        if (statement != null) {
            return statement;
        }
        statement = prefix + ClassNameUtil.getClassNameWithoutPackage(persistentObjectClass);
        statement = statement.substring(0, statement.length() - 6);
        cachedStatements.put(persistentObjectClass, statement);
        return statement;
    }

    // db specific mappings /////////////////////////////////////////////////////

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

    public String mapStatement(String statement) {
        if (statementMappings == null) {
            return statement;
        }
        String mappedStatement = statementMappings.get(statement);
        return (mappedStatement != null ? mappedStatement : statement);
    }

    // db operations ////////////////////////////////////////////////////////////

    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 selectSchemaVersionStatement = mapStatement("selectDbSchemaVersion");
            String dbVersion = (String) sqlSession.selectOne(selectSchemaVersionStatement);
            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", databaseName, sqlSessionFactory);
    }

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

    public static void executeSchemaResource(String operation, String databaseName,
            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.finest("\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");
    }

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

    public SqlSessionFactory getSqlSessionFactory() {
        return sqlSessionFactory;
    }

    public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
        this.sqlSessionFactory = sqlSessionFactory;
    }

    public IdGenerator getIdGenerator() {
        return idGenerator;
    }

    public void setIdGenerator(IdGenerator idGenerator) {
        this.idGenerator = idGenerator;
    }
}