Java tutorial
/* 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 uap.workflow.engine.db; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.StringReader; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.ResultSet; import java.sql.Statement; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import org.apache.ibatis.session.RowBounds; import org.apache.ibatis.session.SqlSession; import uap.workflow.engine.cfg.ProcessEngineConfigurationImpl; import uap.workflow.engine.context.Context; import uap.workflow.engine.entity.PropertyEntity; import uap.workflow.engine.entity.VariableInstanceEntity; import uap.workflow.engine.exception.WorkflowException; import uap.workflow.engine.exception.WorkflowOptimisticLockingException; import uap.workflow.engine.exception.WorkflowWrongDbException; import uap.workflow.engine.interceptor.Session; import uap.workflow.engine.io.IoUtil; import uap.workflow.engine.query.DeploymentQueryImpl; import uap.workflow.engine.query.ExecutionQueryImpl; import uap.workflow.engine.query.GroupQueryImpl; import uap.workflow.engine.query.HistoricActivityInstanceQueryImpl; import uap.workflow.engine.query.HistoricDetailQueryImpl; import uap.workflow.engine.query.HistoricProcessInstanceQueryImpl; import uap.workflow.engine.query.HistoricTaskInstanceQueryImpl; import uap.workflow.engine.query.JobQueryImpl; import uap.workflow.engine.query.Page; import uap.workflow.engine.query.ProcessDefinitionQueryImpl; import uap.workflow.engine.query.ProcessInstanceQueryImpl; import uap.workflow.engine.query.TaskQueryImpl; import uap.workflow.engine.query.UserQueryImpl; import uap.workflow.engine.service.ProcessEngine; import uap.workflow.engine.service.ProcessEngineConfiguration; import uap.workflow.engine.util.ClassNameUtil; import uap.workflow.engine.util.ReflectUtil; import uap.workflow.engine.variable.DeserializedObject; /** * responsibilities: - delayed flushing of inserts updates and deletes - * optional dirty checking - db specific statement name mapping * * @author Tom Baeyens */ public class DbSqlSession implements Session { private static Logger log = Logger.getLogger(DbSqlSession.class.getName()); protected SqlSession sqlSession; protected DbSqlSessionFactory dbSqlSessionFactory; protected List<PersistentObject> insertedObjects = new ArrayList<PersistentObject>(); protected Map<Class<?>, Map<String, CachedObject>> cachedObjects = new HashMap<Class<?>, Map<String, CachedObject>>(); protected List<DeleteOperation> deletedObjects = new ArrayList<DeleteOperation>(); protected List<DeserializedObject> deserializedObjects = new ArrayList<DeserializedObject>(); protected String connectionMetadataDefaultCatalog = null; protected String connectionMetadataDefaultSchema = null; public DbSqlSession(DbSqlSessionFactory dbSqlSessionFactory) { this.dbSqlSessionFactory = dbSqlSessionFactory; this.sqlSession = dbSqlSessionFactory.getSqlSessionFactory().openSession(); } public DbSqlSession(DbSqlSessionFactory dbSqlSessionFactory, Connection connection, String catalog, String schema) { this.dbSqlSessionFactory = dbSqlSessionFactory; this.sqlSession = dbSqlSessionFactory.getSqlSessionFactory().openSession(connection); this.connectionMetadataDefaultCatalog = catalog; this.connectionMetadataDefaultSchema = schema; } // insert // /////////////////////////////////////////////////////////////////// public void insert(PersistentObject persistentObject) { System.out.println(persistentObject.getClass().getName()); if (persistentObject.getId() == null) { String id = dbSqlSessionFactory.getIdGenerator().getNextId(); persistentObject.setId(id); } insertedObjects.add(persistentObject); cachePut(persistentObject, false); } // delete // /////////////////////////////////////////////////////////////////// public void delete(Class<?> persistentObjectClass, String persistentObjectId) { for (DeleteOperation deleteOperation : deletedObjects) { if (deleteOperation instanceof DeleteById) { DeleteById deleteById = (DeleteById) deleteOperation; if (persistentObjectClass.equals(deleteById.persistenceObjectClass) && persistentObjectId.equals(deleteById.persistentObjectId)) { // skip this delete return; } } } deletedObjects.add(new DeleteById(persistentObjectClass, persistentObjectId)); } public interface DeleteOperation { void execute(); } public class DeleteById implements DeleteOperation { Class<?> persistenceObjectClass; String persistentObjectId; public DeleteById(Class<?> clazz, String id) { this.persistenceObjectClass = clazz; this.persistentObjectId = id; } public void execute() { String deleteStatement = dbSqlSessionFactory.getDeleteStatement(persistenceObjectClass); deleteStatement = dbSqlSessionFactory.mapStatement(deleteStatement); if (deleteStatement == null) { throw new WorkflowException( "no delete statement for " + persistenceObjectClass + " in the ibatis mapping files"); } log.fine("deleting: " + ClassNameUtil.getClassNameWithoutPackage(persistenceObjectClass) + "[" + persistentObjectId + "]"); sqlSession.delete(deleteStatement, persistentObjectId); } public String toString() { return "delete " + ClassNameUtil.getClassNameWithoutPackage(persistenceObjectClass) + "[" + persistentObjectId + "]"; } } public void delete(String statement, Object parameter) { deletedObjects.add(new DeleteBulk(statement, parameter)); } public class DeleteBulk implements DeleteOperation { String statement; Object parameter; public DeleteBulk(String statement, Object parameter) { this.statement = dbSqlSessionFactory.mapStatement(statement); this.parameter = parameter; } public void execute() { sqlSession.delete(statement, parameter); } public String toString() { return "bulk delete: " + statement; } } // select // /////////////////////////////////////////////////////////////////// @SuppressWarnings("rawtypes") public List selectList(String statement) { return selectList(statement, null); } @SuppressWarnings({ "rawtypes", "unchecked" }) public List selectList(String statement, Object parameter) { statement = dbSqlSessionFactory.mapStatement(statement); List loadedObjects = sqlSession.selectList(statement, parameter); return filterLoadedObjects(loadedObjects); } @SuppressWarnings({ "rawtypes", "unchecked" }) public List selectList(String statement, Object parameter, Page page) { statement = dbSqlSessionFactory.mapStatement(statement); List loadedObjects; if (page != null) { loadedObjects = sqlSession.selectList(statement, parameter, new RowBounds(page.getFirstResult(), page.getMaxResults())); } else { loadedObjects = sqlSession.selectList(statement, parameter); } return filterLoadedObjects(loadedObjects); } public Object selectOne(String statement, Object parameter) { statement = dbSqlSessionFactory.mapStatement(statement); Object result = sqlSession.selectOne(statement, parameter); if (result instanceof PersistentObject) { PersistentObject loadedObject = (PersistentObject) result; result = cacheFilter(loadedObject); } return result; } @SuppressWarnings("unchecked") public <T extends PersistentObject> T selectById(Class<T> entityClass, String id) { if (id == null) { id = ""; } T persistentObject = cacheGet(entityClass, id); if (persistentObject != null) { return persistentObject; } String selectStatement = dbSqlSessionFactory.getSelectStatement(entityClass); selectStatement = dbSqlSessionFactory.mapStatement(selectStatement); persistentObject = (T) sqlSession.selectOne(selectStatement, id); if (persistentObject == null) { return null; } cachePut(persistentObject, true); return persistentObject; } // internal session cache // /////////////////////////////////////////////////// @SuppressWarnings({ "rawtypes" }) protected List filterLoadedObjects(List<Object> loadedObjects) { if (loadedObjects.isEmpty()) { return loadedObjects; } if (!(PersistentObject.class.isAssignableFrom(loadedObjects.get(0).getClass()))) { return loadedObjects; } List<PersistentObject> filteredObjects = new ArrayList<PersistentObject>(loadedObjects.size()); for (Object loadedObject : loadedObjects) { PersistentObject cachedPersistentObject = cacheFilter((PersistentObject) loadedObject); filteredObjects.add(cachedPersistentObject); } return filteredObjects; } protected CachedObject cachePut(PersistentObject persistentObject, boolean storeState) { Map<String, CachedObject> classCache = cachedObjects.get(persistentObject.getClass()); if (classCache == null) { classCache = new HashMap<String, CachedObject>(); cachedObjects.put(persistentObject.getClass(), classCache); } CachedObject cachedObject = new CachedObject(persistentObject, storeState); classCache.put(persistentObject.getId(), cachedObject); return cachedObject; } /** * returns the object in the cache. if this object was loaded before, then * the original object is returned. if this is the first time this object is * loaded, then the loadedObject is added to the cache. */ protected PersistentObject cacheFilter(PersistentObject persistentObject) { PersistentObject cachedPersistentObject = cacheGet(persistentObject.getClass(), persistentObject.getId()); if (cachedPersistentObject != null) { return cachedPersistentObject; } cachePut(persistentObject, true); return persistentObject; } @SuppressWarnings("unchecked") protected <T> T cacheGet(Class<T> entityClass, String id) { CachedObject cachedObject = null; Map<String, CachedObject> classCache = cachedObjects.get(entityClass); if (classCache != null) { cachedObject = classCache.get(id); } if (cachedObject != null) { return (T) cachedObject.getPersistentObject(); } return null; } protected void cacheRemove(Class<?> persistentObjectClass, String persistentObjectId) { Map<String, CachedObject> classCache = cachedObjects.get(persistentObjectClass); if (classCache == null) { return; } classCache.remove(persistentObjectId); } @SuppressWarnings("unchecked") public <T> List<T> findInCache(Class<T> entityClass) { Map<String, CachedObject> classCache = cachedObjects.get(entityClass); if (classCache != null) { ArrayList<T> entities = new ArrayList<T>(classCache.size()); for (CachedObject cachedObject : classCache.values()) { entities.add((T) cachedObject.getPersistentObject()); } return entities; } return Collections.emptyList(); } public static class CachedObject { protected PersistentObject persistentObject; protected Object persistentObjectState; public CachedObject(PersistentObject persistentObject, boolean storeState) { this.persistentObject = persistentObject; if (storeState) { this.persistentObjectState = persistentObject.getPersistentState(); } } public PersistentObject getPersistentObject() { return persistentObject; } public Object getPersistentObjectState() { return persistentObjectState; } } // deserialized objects // ///////////////////////////////////////////////////// public void addDeserializedObject(Object deserializedObject, byte[] serializedBytes, VariableInstanceEntity variableInstanceEntity) { deserializedObjects .add(new DeserializedObject(deserializedObject, serializedBytes, variableInstanceEntity)); } // flush // //////////////////////////////////////////////////////////////////// public void flush() { removeUnnecessaryOperations(); flushDeserializedObjects(); List<PersistentObject> updatedObjects = getUpdatedObjects(); if (log.isLoggable(Level.FINE)) { log.fine("flush summary:"); for (PersistentObject insertedObject : insertedObjects) { log.fine(" insert " + toString(insertedObject)); } for (PersistentObject updatedObject : updatedObjects) { log.fine(" update " + toString(updatedObject)); } for (Object deleteOperation : deletedObjects) { log.fine(" " + deleteOperation); } log.fine("now executing flush..."); } flushInserts(); flushUpdates(updatedObjects); flushDeletes(); } protected void removeUnnecessaryOperations() { List<DeleteOperation> deletedObjectsCopy = new ArrayList<DeleteOperation>(deletedObjects); // for all deleted objects for (DeleteOperation deleteOperation : deletedObjectsCopy) { if (deleteOperation instanceof DeleteById) { DeleteById deleteById = (DeleteById) deleteOperation; PersistentObject insertedObject = findInsertedObject(deleteById.persistenceObjectClass, deleteById.persistentObjectId); // if the deleted object is inserted, if (insertedObject != null) { // remove the insert and the delete insertedObjects.remove(insertedObject); deletedObjects.remove(deleteOperation); } // in any case, remove the deleted object from the cache cacheRemove(deleteById.persistenceObjectClass, deleteById.persistentObjectId); } } for (PersistentObject insertedObject : insertedObjects) { cacheRemove(insertedObject.getClass(), insertedObject.getId()); } } protected PersistentObject findInsertedObject(Class<?> persistenceObjectClass, String persistentObjectId) { for (PersistentObject insertedObject : insertedObjects) { if (insertedObject.getClass().equals(persistenceObjectClass) && insertedObject.getId().equals(persistentObjectId)) { return insertedObject; } } return null; } protected void flushDeserializedObjects() { for (DeserializedObject deserializedObject : deserializedObjects) { deserializedObject.flush(); } } public List<PersistentObject> getUpdatedObjects() { List<PersistentObject> updatedObjects = new ArrayList<PersistentObject>(); for (Class<?> clazz : cachedObjects.keySet()) { Map<String, CachedObject> classCache = cachedObjects.get(clazz); for (CachedObject cachedObject : classCache.values()) { PersistentObject persistentObject = (PersistentObject) cachedObject.getPersistentObject(); if (!deletedObjects.contains(persistentObject)) { Object originalState = cachedObject.getPersistentObjectState(); if (!originalState.equals(persistentObject.getPersistentState())) { updatedObjects.add(persistentObject); } else { log.finest("loaded object '" + persistentObject + "' was not updated"); } } } } return updatedObjects; } public <T extends PersistentObject> List<T> pruneDeletedEntities(List<T> listToPrune) { ArrayList<T> prunedList = new ArrayList<T>(listToPrune); for (T potentiallyDeleted : listToPrune) { for (DeleteOperation deleteOperation : deletedObjects) { if (deleteOperation instanceof DeleteById) { DeleteById deleteById = (DeleteById) deleteOperation; if (potentiallyDeleted.getClass().equals(deleteById.persistenceObjectClass) && potentiallyDeleted.getId().equals(deleteById.persistentObjectId)) { prunedList.remove(potentiallyDeleted); } } } } return prunedList; } protected void flushInserts() { for (PersistentObject insertedObject : insertedObjects) { String insertStatement = dbSqlSessionFactory.getInsertStatement(insertedObject); insertStatement = dbSqlSessionFactory.mapStatement(insertStatement); if (insertStatement == null) { throw new WorkflowException( "no insert statement for " + insertedObject.getClass() + " in the ibatis mapping files"); } log.fine("inserting: " + toString(insertedObject)); sqlSession.insert(insertStatement, insertedObject); } insertedObjects.clear(); } protected void flushUpdates(List<PersistentObject> updatedObjects) { for (PersistentObject updatedObject : updatedObjects) { String updateStatement = dbSqlSessionFactory.getUpdateStatement(updatedObject); updateStatement = dbSqlSessionFactory.mapStatement(updateStatement); if (updateStatement == null) { throw new WorkflowException( "no update statement for " + updatedObject.getClass() + " in the ibatis mapping files"); } log.fine("updating: " + toString(updatedObject) + "]"); int updatedRecords = sqlSession.update(updateStatement, updatedObject); if (updatedRecords != 1) { throw new WorkflowOptimisticLockingException( toString(updatedObject) + " was updated by another transaction concurrently"); } } updatedObjects.clear(); } protected void flushDeletes() { for (DeleteOperation delete : deletedObjects) { log.fine("executing: " + delete); delete.execute(); } deletedObjects.clear(); } public void close() { sqlSession.close(); } public void commit() { sqlSession.commit(); } public void rollback() { sqlSession.rollback(); } protected String toString(PersistentObject persistentObject) { if (persistentObject == null) { return "null"; } return ClassNameUtil.getClassNameWithoutPackage(persistentObject) + "[" + persistentObject.getId() + "]"; } // schema operations // //////////////////////////////////////////////////////// public void dbSchemaCheckVersion() { try { String dbVersion = getDbVersion(); if (!ProcessEngine.VERSION.equals(dbVersion)) { throw new WorkflowWrongDbException(ProcessEngine.VERSION, dbVersion); } String errorMessage = null; if (!isEngineTablePresent()) { errorMessage = addMissingComponent(errorMessage, "engine"); } if (dbSqlSessionFactory.isDbHistoryUsed() && !isHistoryTablePresent()) { errorMessage = addMissingComponent(errorMessage, "history"); } if (dbSqlSessionFactory.isDbIdentityUsed() && !isIdentityTablePresent()) { errorMessage = addMissingComponent(errorMessage, "identity"); } Integer configuredHistoryLevel = Context.getProcessEngineConfiguration().getHistoryLevel(); PropertyEntity historyLevelProperty = selectById(PropertyEntity.class, "historyLevel"); if (historyLevelProperty == null) { if (errorMessage == null) { errorMessage = ""; } errorMessage += "no historyLevel property specified"; } else { Integer databaseHistoryLevel = new Integer(historyLevelProperty.getValue()); if (!configuredHistoryLevel.equals(databaseHistoryLevel)) { if (errorMessage == null) { errorMessage = ""; } errorMessage += "historyLevel mismatch: configuration says " + configuredHistoryLevel + " and database says " + databaseHistoryLevel; } } if (errorMessage != null) { throw new WorkflowException("Activiti database problem: " + errorMessage); } } catch (Exception e) { if (isMissingTablesException(e)) { throw new WorkflowException( "no activiti tables in db. set <property name=\"databaseSchemaUpdate\" to value=\"true\" or value=\"create-drop\" (use create-drop for testing only!) in bean processEngineConfiguration in activiti.cfg.xml for automatic schema creation", e); } else { if (e instanceof RuntimeException) { throw (RuntimeException) e; } else { throw new WorkflowException("couldn't get db schema version", e); } } } log.fine("activiti db schema check successful"); } protected String addMissingComponent(String missingComponents, String component) { if (missingComponents == null) { return "Tables missing for component(s) " + component; } return missingComponents + ", " + component; } protected String getDbVersion() { String selectSchemaVersionStatement = dbSqlSessionFactory.mapStatement("selectDbSchemaVersion"); return (String) sqlSession.selectOne(selectSchemaVersionStatement); } public void dbSchemaCreate() { ProcessEngineConfigurationImpl processEngineConfiguration = Context.getProcessEngineConfiguration(); int configuredHistoryLevel = processEngineConfiguration.getHistoryLevel(); if ((!processEngineConfiguration.isDbHistoryUsed()) && (configuredHistoryLevel > ProcessEngineConfigurationImpl.HISTORYLEVEL_NONE)) { throw new WorkflowException( "historyLevel config is higher then 'none' and dbHistoryUsed is set to false"); } if (isEngineTablePresent()) { String dbVersion = getDbVersion(); if (!ProcessEngine.VERSION.equals(dbVersion)) { throw new WorkflowWrongDbException(ProcessEngine.VERSION, dbVersion); } } else { dbSchemaCreateEngine(); } if (processEngineConfiguration.isDbHistoryUsed()) { dbSchemaCreateHistory(); } if (processEngineConfiguration.isDbIdentityUsed()) { dbSchemaCreateIdentity(); } } protected void dbSchemaCreateIdentity() { executeMandatorySchemaResource("create", "identity"); } protected void dbSchemaCreateHistory() { executeMandatorySchemaResource("create", "history"); } protected void dbSchemaCreateEngine() { executeMandatorySchemaResource("create", "engine"); int configuredHistoryLevel = Context.getProcessEngineConfiguration().getHistoryLevel(); PropertyEntity property = new PropertyEntity("historyLevel", Integer.toString(configuredHistoryLevel)); insert(property); } public void dbSchemaDrop() { executeMandatorySchemaResource("drop", "engine"); if (dbSqlSessionFactory.isDbHistoryUsed()) { executeMandatorySchemaResource("drop", "history"); } if (dbSqlSessionFactory.isDbIdentityUsed()) { executeMandatorySchemaResource("drop", "identity"); } } public void dbSchemaPrune() { if (isHistoryTablePresent() && !dbSqlSessionFactory.isDbHistoryUsed()) { executeMandatorySchemaResource("drop", "history"); } if (isIdentityTablePresent() && dbSqlSessionFactory.isDbIdentityUsed()) { executeMandatorySchemaResource("drop", "identity"); } } public void executeMandatorySchemaResource(String operation, String component) { executeSchemaResource(operation, component, getResourceForDbOperation(operation, operation, component), false); } public static String[] JDBC_METADATA_TABLE_TYPES = { "TABLE" }; public String dbSchemaUpdate() { String feedback = null; String dbVersion = null; boolean isUpgradeNeeded = false; if (isEngineTablePresent()) { // the next piece assumes both DB version and library versions are // formatted 5.x PropertyEntity dbVersionProperty = selectById(PropertyEntity.class, "schema.version"); dbVersion = dbVersionProperty.getValue(); isUpgradeNeeded = !ProcessEngine.VERSION.equals(dbVersion); if (isUpgradeNeeded) { dbVersionProperty.setValue(ProcessEngine.VERSION); PropertyEntity dbHistoryProperty; if ("5.0".equals(dbVersion)) { dbHistoryProperty = new PropertyEntity("schema.history", "create(5.0)"); insert(dbHistoryProperty); } else { dbHistoryProperty = selectById(PropertyEntity.class, "schema.history"); } String dbHistoryValue = dbHistoryProperty.getValue() + " upgrade(" + dbVersion + "->" + ProcessEngine.VERSION + ")"; dbHistoryProperty.setValue(dbHistoryValue); dbSchemaUpgrade("engine", dbVersion); feedback = "upgraded Activiti from " + dbVersion + " to " + ProcessEngine.VERSION; } } else { dbSchemaCreateEngine(); } if (isHistoryTablePresent()) { if (isUpgradeNeeded) { dbSchemaUpgrade("history", dbVersion); } } else if (dbSqlSessionFactory.isDbHistoryUsed()) { dbSchemaCreateHistory(); } if (isIdentityTablePresent()) { if (isUpgradeNeeded) { dbSchemaUpgrade("identity", dbVersion); } } else if (dbSqlSessionFactory.isDbIdentityUsed()) { dbSchemaCreateIdentity(); } return feedback; } public boolean isEngineTablePresent() { return isTablePresent("ACT_RU_EXECUTION"); } public boolean isHistoryTablePresent() { return isTablePresent("ACT_HI_PROCINST"); } public boolean isIdentityTablePresent() { return isTablePresent("ACT_ID_USER"); } public boolean isTablePresent(String tableName) { Connection connection = null; try { connection = sqlSession.getConnection(); DatabaseMetaData databaseMetaData = connection.getMetaData(); ResultSet tables = null; String schema = this.connectionMetadataDefaultSchema; String databaseType = dbSqlSessionFactory.getDatabaseType(); if ("postgres".equals(databaseType)) { tableName = tableName.toLowerCase(); } if ("oracle".equals(databaseType)) { // avoid problems if multiple schemas are visible to the current // oracle user (https://jira.codehaus.org/browse/ACT-1062) if (schema == null) { schema = databaseMetaData.getUserName(); log.info("oracle database used and schema not set; assuming schema " + schema); } } try { tables = databaseMetaData.getTables(this.connectionMetadataDefaultCatalog, schema, tableName, JDBC_METADATA_TABLE_TYPES); return tables.next(); } finally { tables.close(); } } catch (Exception e) { throw new WorkflowException( "couldn't check if tables are already present using metadata: " + e.getMessage(), e); } } protected void dbSchemaUpgrade(String component, String dbVersion) { log.info("upgrading activiti " + component + " schema from " + dbVersion + " to " + ProcessEngine.VERSION); if (dbVersion.endsWith("-SNAPSHOT")) { dbVersion = dbVersion.substring(0, dbVersion.length() - "-SNAPSHOT".length()); } int minorDbVersionNumber = Integer.parseInt(dbVersion.substring(2)); String libraryVersion = ProcessEngine.VERSION; if (ProcessEngine.VERSION.endsWith("-SNAPSHOT")) { libraryVersion = ProcessEngine.VERSION.substring(0, ProcessEngine.VERSION.length() - "-SNAPSHOT".length()); } int minorLibraryVersionNumber = Integer.parseInt(libraryVersion.substring(2)); while (minorDbVersionNumber < minorLibraryVersionNumber) { executeSchemaResource("upgrade", component, getResourceForDbOperation("upgrade", "upgradestep.5" + minorDbVersionNumber + ".to.5" + (minorDbVersionNumber + 1), component), true); minorDbVersionNumber++; } } public String getResourceForDbOperation(String directory, String operation, String component) { String databaseType = dbSqlSessionFactory.getDatabaseType(); return "org/activiti/db/" + directory + "/activiti." + databaseType + "." + operation + "." + component + ".sql"; } public void executeSchemaResource(String operation, String component, String resourceName, boolean isOptional) { InputStream inputStream = null; try { inputStream = ReflectUtil.getResourceAsStream(resourceName); if (inputStream == null) { if (isOptional) { log.fine("no schema resource " + resourceName + " for " + operation); } else { throw new WorkflowException("resource '" + resourceName + "' is not available"); } } else { executeSchemaResource(operation, component, resourceName, inputStream); } } finally { IoUtil.closeSilently(inputStream); } } private void executeSchemaResource(String operation, String component, String resourceName, InputStream inputStream) { log.info("performing " + operation + " on " + component + " with resource " + resourceName); String sqlStatement = null; String exceptionSqlStatement = null; try { Connection connection = sqlSession.getConnection(); Exception exception = null; byte[] bytes = IoUtil.readInputStream(inputStream, resourceName); String ddlStatements = new String(bytes); BufferedReader reader = new BufferedReader(new StringReader(ddlStatements)); String line = readNextTrimmedLine(reader); while (line != null) { if (line.startsWith("# ")) { log.fine(line.substring(2)); } else if (line.startsWith("-- ")) { log.fine(line.substring(3)); } else if (line.startsWith("execute java ")) { String upgradestepClassName = line.substring(13).trim(); DbUpgradeStep dbUpgradeStep = null; try { dbUpgradeStep = (DbUpgradeStep) ReflectUtil.instantiate(upgradestepClassName); } catch (WorkflowException e) { throw new WorkflowException("database update java class '" + upgradestepClassName + "' can't be instantiated: " + e.getMessage(), e); } try { dbUpgradeStep.execute(this); } catch (Exception e) { throw new WorkflowException("error while executing database update java class '" + upgradestepClassName + "': " + e.getMessage(), e); } } else if (line.length() > 0) { if (line.endsWith(";")) { sqlStatement = addSqlStatementPiece(sqlStatement, line.substring(0, line.length() - 1)); Statement jdbcStatement = connection.createStatement(); try { // no logging needed as the connection will log it jdbcStatement.execute(sqlStatement); jdbcStatement.close(); } catch (Exception e) { if (exception == null) { exception = e; exceptionSqlStatement = sqlStatement; } log.log(Level.SEVERE, "problem during schema " + operation + ", statement '" + sqlStatement, e); } finally { sqlStatement = null; } } else { sqlStatement = addSqlStatementPiece(sqlStatement, line); } } line = readNextTrimmedLine(reader); } if (exception != null) { throw exception; } log.fine("activiti db schema " + operation + " for component " + component + " successful"); } catch (Exception e) { throw new WorkflowException("couldn't " + operation + " db schema: " + exceptionSqlStatement, e); } } protected String addSqlStatementPiece(String sqlStatement, String line) { if (sqlStatement == null) { return line; } return sqlStatement + " \n" + line; } protected String readNextTrimmedLine(BufferedReader reader) throws IOException { String line = reader.readLine(); if (line != null) { line = line.trim(); } return line; } protected boolean isMissingTablesException(Exception e) { String exceptionMessage = e.getMessage(); if (e.getMessage() != null) { // Matches message returned from H2 if ((exceptionMessage.indexOf("Table") != -1) && (exceptionMessage.indexOf("not found") != -1)) { return true; } // Message returned from MySQL and Oracle if (((exceptionMessage.indexOf("Table") != -1 || exceptionMessage.indexOf("table") != -1)) && (exceptionMessage.indexOf("doesn't exist") != -1)) { return true; } // Message returned from Postgres if (((exceptionMessage.indexOf("relation") != -1 || exceptionMessage.indexOf("table") != -1)) && (exceptionMessage.indexOf("does not exist") != -1)) { return true; } } return false; } public void performSchemaOperationsProcessEngineBuild() { String databaseSchemaUpdate = Context.getProcessEngineConfiguration().getDatabaseSchemaUpdate(); if (ProcessEngineConfigurationImpl.DB_SCHEMA_UPDATE_DROP_CREATE.equals(databaseSchemaUpdate)) { try { dbSchemaDrop(); } catch (RuntimeException e) { // ignore } } if (uap.workflow.engine.service.ProcessEngineConfiguration.DB_SCHEMA_UPDATE_CREATE_DROP .equals(databaseSchemaUpdate) || ProcessEngineConfigurationImpl.DB_SCHEMA_UPDATE_DROP_CREATE.equals(databaseSchemaUpdate) || ProcessEngineConfigurationImpl.DB_SCHEMA_UPDATE_CREATE.equals(databaseSchemaUpdate)) { dbSchemaCreate(); } else if (uap.workflow.engine.service.ProcessEngineConfiguration.DB_SCHEMA_UPDATE_FALSE .equals(databaseSchemaUpdate)) { dbSchemaCheckVersion(); } else if (ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE.equals(databaseSchemaUpdate)) { dbSchemaUpdate(); } } public void performSchemaOperationsProcessEngineClose() { String databaseSchemaUpdate = Context.getProcessEngineConfiguration().getDatabaseSchemaUpdate(); if (uap.workflow.engine.service.ProcessEngineConfiguration.DB_SCHEMA_UPDATE_CREATE_DROP .equals(databaseSchemaUpdate)) { dbSchemaDrop(); } } // query factory methods // //////////////////////////////////////////////////// public DeploymentQueryImpl createDeploymentQuery() { return new DeploymentQueryImpl(); } public ProcessDefinitionQueryImpl createProcessDefinitionQuery() { return new ProcessDefinitionQueryImpl(); } public ProcessInstanceQueryImpl createProcessInstanceQuery() { return new ProcessInstanceQueryImpl(); } public ExecutionQueryImpl createExecutionQuery() { return new ExecutionQueryImpl(); } public TaskQueryImpl createTaskQuery() { return new TaskQueryImpl(); } public JobQueryImpl createJobQuery() { return new JobQueryImpl(); } public HistoricProcessInstanceQueryImpl createHistoricProcessInstanceQuery() { return new HistoricProcessInstanceQueryImpl(); } public HistoricActivityInstanceQueryImpl createHistoricActivityInstanceQuery() { return new HistoricActivityInstanceQueryImpl(); } public HistoricTaskInstanceQueryImpl createHistoricTaskInstanceQuery() { return new HistoricTaskInstanceQueryImpl(); } public HistoricDetailQueryImpl createHistoricDetailQuery() { return new HistoricDetailQueryImpl(); } public UserQueryImpl createUserQuery() { return new UserQueryImpl(); } public GroupQueryImpl createGroupQuery() { return new GroupQueryImpl(); } // getters and setters // ////////////////////////////////////////////////////// public SqlSession getSqlSession() { return sqlSession; } public DbSqlSessionFactory getDbSqlSessionFactory() { return dbSqlSessionFactory; } }