package org.jbpm.persistence.scripts;
import java.io.File;
import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import bitronix.tm.resource.jdbc.PoolingDataSource;
import org.drools.core.impl.KnowledgeBaseImpl;
import org.drools.persistence.jta.JtaTransactionManager;
import org.jbpm.persistence.map.impl.ProcessCreatorForHelp;
import org.jbpm.persistence.scripts.oldentities.ProcessInstanceInfo;
import org.jbpm.persistence.scripts.oldentities.SessionInfo;
import org.jbpm.persistence.scripts.oldentities.TaskImpl;
import org.jbpm.persistence.scripts.util.SQLCommandUtil;
import org.jbpm.persistence.scripts.util.SQLScriptUtil;
import org.jbpm.persistence.scripts.util.TestsUtil;
import org.jbpm.persistence.util.PersistenceUtil;
import org.kie.api.KieBase;
import org.kie.api.runtime.Environment;
import org.kie.api.runtime.EnvironmentName;
import org.kie.api.task.model.I18NText;
import org.kie.api.task.model.OrganizationalEntity;
import org.kie.internal.KnowledgeBase;
import org.kie.internal.KnowledgeBaseFactory;
import org.kie.internal.persistence.jpa.JPAKnowledgeService;
import org.kie.internal.runtime.StatefulKnowledgeSession;
import org.kie.internal.task.api.TaskModelProvider;
import org.kie.internal.task.api.model.InternalI18NText;
import org.kie.internal.task.api.model.InternalOrganizationalEntity;
import org.kie.internal.task.api.model.InternalPeopleAssignments;
import org.kie.internal.task.api.model.InternalTaskData;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Central context that hides persistence from tests, so there is no need to work with persistence in the tests
* (transactions etc).
*/
public final class TestPersistenceContext {
private static final Logger logger = LoggerFactory.getLogger(TestPersistenceContext.class);
private HashMap<String, Object> context;
private EntityManagerFactory entityManagerFactory;
private JtaTransactionManager transactionManager;
private Environment environment;
private final Properties dataSourceProperties;
private final DatabaseType databaseType;
public TestPersistenceContext() {
this.dataSourceProperties = PersistenceUtil.getDatasourceProperties();
this.databaseType = TestsUtil.getDatabaseType(dataSourceProperties);
}
/**
* Initializes persistence context from specified persistence unit.
* @param persistenceUnit Persistence unit which is used to initialize this persistence context.
*/
public void init(final PersistenceUnit persistenceUnit) {
try {
context = PersistenceUtil.setupWithPoolingDataSource(persistenceUnit.getName(), persistenceUnit
.getDataSourceName());
entityManagerFactory = (EntityManagerFactory) context.get(EnvironmentName.ENTITY_MANAGER_FACTORY);
environment = PersistenceUtil.createEnvironment(context);
Object tm = this.environment.get(EnvironmentName.TRANSACTION_MANAGER);
transactionManager = new JtaTransactionManager(environment.get(EnvironmentName.TRANSACTION),
environment.get(EnvironmentName.TRANSACTION_SYNCHRONIZATION_REGISTRY),
tm);
} catch (RuntimeException ex) {
// log the whole exception stacktrace as for some reason junit is not able to do so and only prints
// the highest level exception, which makes debugging very hard
logger.error("Failed to initialize persistence unit {}", persistenceUnit, ex);
if (entityManagerFactory != null) {
entityManagerFactory.close();
}
throw ex;
}
}
/**
* Cleans up this persistence context. Closes all instances that need to be closed.
*/
public void clean() {
PersistenceUtil.cleanUp(context);
}
/**
* Executes SQL scripts from specified root SQL scripts folder. Selects appropriate scripts from root folder
* by using dialect that is defined in datasource.properties file.
* @param scriptsRootFolder Root folder containing folders with SQL scripts for all supported database systems.
* @throws IOException
*/
public void executeScripts(final File scriptsRootFolder) throws IOException, SQLException {
executeScripts(scriptsRootFolder, null);
}
public void executeScripts(final File scriptsRootFolder, String type) throws IOException, SQLException {
testIsInitialized();
final File[] sqlScripts = TestsUtil.getDDLScriptFilesByDatabaseType(scriptsRootFolder, databaseType, true);
final Connection connection = ((PoolingDataSource) context.get(PersistenceUtil.DATASOURCE)).getConnection();
connection.setAutoCommit(false);
try {
for (File script : sqlScripts) {
if (type == null || script.getName().startsWith(type)) {
logger.debug("Executing script {}", script.getName());
final List<String> scriptCommands = SQLScriptUtil.getCommandsFromScript(script, databaseType);
for (String command : scriptCommands) {
logger.debug(command);
final PreparedStatement statement;
if (databaseType == DatabaseType.SQLSERVER || databaseType == DatabaseType.SQLSERVER2008) {
statement = connection.prepareStatement(
SQLCommandUtil.preprocessCommandSqlServer(command, dataSourceProperties));
} else {
statement = connection.prepareStatement(command);
}
statement.execute();
statement.close();
}
}
}
connection.commit();
} catch (SQLException ex) {
connection.rollback();
throw new RuntimeException(ex.getMessage(), ex);
} finally {
connection.close();
}
}
/**
* Starts and persists a basic simple process using current database entities.
* @param processId Process identifier. This identifier is also used to generate KieBase
* (process with this identifier is part of generated KieBase).
*/
public void startAndPersistSomeProcess(final String processId) {
testIsInitialized();
final StatefulKnowledgeSession session;
final KieBase kbase = createKieBase(processId);
session = JPAKnowledgeService.newStatefulKnowledgeSession(kbase, null, environment);
session.startProcess(processId);
}
public void createSomeTask() {
testIsInitialized();
TaskImpl task = new TaskImpl();
InternalI18NText name = (InternalI18NText) TaskModelProvider.getFactory().newI18NText();
name.setText("Some Task");
List<I18NText> names = new ArrayList<I18NText>();
names.add(name);
task.setNames(names);
InternalTaskData taskData = (InternalTaskData) TaskModelProvider.getFactory().newTaskData();
taskData.setWorkItemId(12);
taskData.setProcessInstanceId(1);
taskData.setProcessId("someprocess");
taskData.setDeploymentId("org.jbpm.test:someprocess:1.0");
taskData.setProcessSessionId(1);
task.setTaskData(taskData);
InternalPeopleAssignments peopleAssignments =
(InternalPeopleAssignments) TaskModelProvider.getFactory().newPeopleAssignments();
peopleAssignments.setPotentialOwners(new ArrayList<OrganizationalEntity>());
peopleAssignments.setBusinessAdministrators(new ArrayList<OrganizationalEntity>());
peopleAssignments.setExcludedOwners(new ArrayList<OrganizationalEntity>());
peopleAssignments.setRecipients(new ArrayList<OrganizationalEntity>());
peopleAssignments.setTaskStakeholders(new ArrayList<OrganizationalEntity>());
InternalOrganizationalEntity jdoe =
(InternalOrganizationalEntity) TaskModelProvider.getFactory().newUser();
jdoe.setId("jdoe");
peopleAssignments.getPotentialOwners().add(jdoe);
peopleAssignments.getBusinessAdministrators().add(jdoe);
task.setPeopleAssignments(peopleAssignments);
final boolean txOwner = transactionManager.begin();
try {
EntityManager em = entityManagerFactory.createEntityManager();
em.persist(jdoe);
em.persist(task);
transactionManager.commit(txOwner);
} catch (Exception ex) {
ex.printStackTrace();
transactionManager.rollback(txOwner);
throw new RuntimeException(ex.getMessage(), ex);
}
}
/**
* Loads persisted session from database.
* @param sessionId Unique identifier of the session.
* @param processIdForKieBase Process identifier for KieBase generation. A KieBase is generated for
* loaded session and this KieBase contains process with this identifier.
* @return Session that is stored in database.
*/
public StatefulKnowledgeSession loadPersistedSession(final Long sessionId, final String processIdForKieBase) {
testIsInitialized();
return JPAKnowledgeService.loadStatefulKnowledgeSession(sessionId, createKieBase(processIdForKieBase),
null, environment);
}
/**
* Persists a process and a session using entites from jBPM 6.0. Persists
* process and session using their database identifiers so be aware that you can
* rewrite some of your data. This method should be used only to populate inital data for tests.
* @param sessionId Unique identifier of the session.
* @param processId Identifier of the process (name).
* @param processInstanceId Unique identifier of the process.
*/
public void persistOldProcessAndSession(final Integer sessionId, final String processId,
final Long processInstanceId) {
testIsInitialized();
final boolean txOwner = transactionManager.begin();
final EntityManager entityManager = entityManagerFactory.createEntityManager();
try {
entityManager.merge(getOldProcessInstanceInfo(processId, processInstanceId));
entityManager.merge(getOldSessionInfo(sessionId));
entityManager.flush();
entityManager.close();
transactionManager.commit(txOwner);
} catch (Exception ex) {
ex.printStackTrace();
transactionManager.rollback(txOwner);
throw new RuntimeException(ex.getMessage(), ex);
}
}
/**
* Reads stored processes count from database.
* @return Stored processes count.
*/
public int getStoredProcessesCount() {
return getStoredEntitiesCount("ProcessInstanceInfo");
}
/**
* Reads stored sessions count from database.
* @return Stored sessions count.
*/
public int getStoredSessionsCount() {
return getStoredEntitiesCount("SessionInfo");
}
/**
* Reads stored entities count from database.
* @param entityClassName Class name of entity.
* @return Stored entities count.
*/
private int getStoredEntitiesCount(final String entityClassName) {
testIsInitialized();
final boolean txOwner = transactionManager.begin();
final EntityManager entityManager = entityManagerFactory.createEntityManager();
try {
final List entitiesList = entityManager.createQuery("SELECT p FROM " + entityClassName + " p")
.getResultList();
if (entitiesList == null) {
return 0;
} else {
return entitiesList.size();
}
} catch (Exception ex) {
ex.printStackTrace();
transactionManager.rollback(txOwner);
throw new RuntimeException(ex.getMessage(), ex);
} finally {
entityManager.close();
transactionManager.commit(txOwner);
}
}
/**
* Checks if this persistence context is initialized.
*/
private void testIsInitialized() {
if (context == null) {
throw new IllegalStateException("TestContext is not initialized! Call TestContext.init() before using it.");
}
}
/**
* Creates very basic KieBase that contains processes with specified processIds.
* @param processIds ProcessIds of processes that are contained within resulting KieBase.
* @return Basic KieBase.
*/
private KieBase createKieBase(final String... processIds) {
final KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
for (String processId : processIds) {
((KnowledgeBaseImpl) kbase).addProcess(ProcessCreatorForHelp.newSimpleEventProcess(processId, "test"));
}
return kbase;
}
/**
* Returns ProcessInstanceInfo entity for jBPM 6.0 version.
* @param processId Process identifier (name).
* @param processInstanceId Unique identifier of the process. Database identifier.
* @return ProcessInstanceInfo entity for jBPM 6.0 version filled with default data.
* @throws ParseException
*/
private ProcessInstanceInfo getOldProcessInstanceInfo(final String processId, final Long processInstanceId)
throws ParseException {
final DateFormat dateFormat = getDateFormat();
final ProcessInstanceInfo result = new ProcessInstanceInfo();
result.setProcessInstanceId(processInstanceId);
result.setEventTypes(getOldProcessEventTypes());
result.setLastModificationDate(dateFormat.parse("2015-08-25 13:43:25.760"));
result.setLastReadDate(dateFormat.parse("2015-08-25 13:43:25.210"));
result.setProcessId(processId);
result.setStartDate(dateFormat.parse("2015-08-25 13:43:25.190"));
result.setState(1);
result.setVersion(2);
result.setProcessInstanceByteArray(
TestsUtil.hexStringToByteArray(
"ACED00057769000852756C65466C6F770A0608061004180052550A0852756C65466C6F7710011A0E6D696E696D616C50726F63657373200128023A0A0801100222020805280160006A0E5F6A62706D2D756E697175652D3072120A0E5F6A62706D2D756E697175652D311001800101"));
return result;
}
/**
* Return default process event types.
* @return Default process event types.
*/
private Set<String> getOldProcessEventTypes() {
final Set<String> resultSet = new HashSet<String>(1);
resultSet.add("test");
return resultSet;
}
/**
* Returns SessionInfo entity for jBPM 6.0 version.
* @param sessionId Unique identifier of the session. Database identifier.
* @return SessionInfo entity for jBPM 6.0 version filled with default data.
* @throws ParseException
*/
private SessionInfo getOldSessionInfo(final Integer sessionId) throws ParseException {
final DateFormat dateFormat = getDateFormat();
final SessionInfo result = new SessionInfo();
result.setId(sessionId);
result.setLastModificationDate(dateFormat.parse("2015-08-25 13:43:25.248"));
result.setStartDate(dateFormat.parse("2015-08-25 13:43:24.858"));
result.setVersion(2);
result.setData(
TestsUtil.hexStringToByteArray(
"ACED0005777C0A060806100418005272080010001A6818002000320608011000180042231A190A044D41494E10001801200028FFFFFFFFFFFFFFFFFF01400022060A044D41494E52350A0744454641554C54222A0A266F72672E64726F6F6C732E636F72652E726574656F6F2E496E697469616C46616374496D706C100022026800"));
return result;
}
private DateFormat getDateFormat() {
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
}
}