/* 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.spring.test.autodeployment; import java.net.URISyntaxException; import java.util.HashSet; import java.util.List; import java.util.Set; import org.activiti.engine.RepositoryService; import org.activiti.engine.impl.test.PvmTestCase; import org.activiti.engine.impl.util.IoUtil; import org.activiti.engine.repository.Deployment; import org.activiti.engine.repository.DeploymentQuery; import org.activiti.engine.repository.ProcessDefinition; import org.activiti.engine.repository.ProcessDefinitionQuery; import org.springframework.context.ApplicationContext; import org.springframework.context.support.AbstractXmlApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * @author Tom Baeyens * @author Joram Barrez */ public class SpringAutoDeployTest extends PvmTestCase { protected static final String CTX_PATH = "org/activiti/spring/test/autodeployment/SpringAutoDeployTest-context.xml"; protected static final String CTX_NO_DROP_PATH = "org/activiti/spring/test/autodeployment/SpringAutoDeployTest-no-drop-context.xml"; protected static final String CTX_CREATE_DROP_CLEAN_DB = "org/activiti/spring/test/autodeployment/SpringAutoDeployTest-create-drop-clean-db-context.xml"; protected ApplicationContext applicationContext; protected RepositoryService repositoryService; protected void createAppContext(String path) { this.applicationContext = new ClassPathXmlApplicationContext(path); this.repositoryService = applicationContext.getBean(RepositoryService.class); } protected void tearDown() throws Exception { removeAllDeployments(); this.applicationContext = null; this.repositoryService = null; super.tearDown(); } public void testBasicActivitiSpringIntegration() { createAppContext("org/activiti/spring/test/autodeployment/SpringAutoDeployTest-context.xml"); List<ProcessDefinition> processDefinitions = repositoryService .createProcessDefinitionQuery() .list(); Set<String> processDefinitionKeys = new HashSet<String>(); for (ProcessDefinition processDefinition: processDefinitions) { processDefinitionKeys.add(processDefinition.getKey()); } Set<String> expectedProcessDefinitionKeys = new HashSet<String>(); expectedProcessDefinitionKeys.add("a"); expectedProcessDefinitionKeys.add("b"); expectedProcessDefinitionKeys.add("c"); assertEquals(expectedProcessDefinitionKeys, processDefinitionKeys); } public void testNoRedeploymentForSpringContainerRestart() throws Exception { createAppContext(CTX_PATH); DeploymentQuery deploymentQuery = repositoryService.createDeploymentQuery(); assertEquals(1, deploymentQuery.count()); ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery(); assertEquals(3, processDefinitionQuery.count()); // Creating a new app context with same resources doesn't lead to more deployments new ClassPathXmlApplicationContext(CTX_NO_DROP_PATH); assertEquals(1, deploymentQuery.count()); assertEquals(3, processDefinitionQuery.count()); } // Updating the bpmn20 file should lead to a new deployment when restarting the Spring container public void testResourceRedeploymentAfterProcessDefinitionChange() throws Exception { createAppContext(CTX_PATH); assertEquals(1, repositoryService.createDeploymentQuery().count()); ((AbstractXmlApplicationContext)applicationContext).destroy(); String filePath = "org/activiti/spring/test/autodeployment/autodeploy.a.bpmn20.xml"; String originalBpmnFileContent = IoUtil.readFileAsString(filePath); String updatedBpmnFileContent = originalBpmnFileContent.replace("flow1", "fromStartToEndFlow"); assertTrue(updatedBpmnFileContent.length() > originalBpmnFileContent.length()); IoUtil.writeStringToFile(updatedBpmnFileContent, filePath); // Classic produced/consumer problem here: // The file is already written in Java, but not yet completely persisted by the OS // Constructing the new app context reads the same file which is sometimes not yet fully written to disk waitUntilFileIsWritten(filePath, updatedBpmnFileContent.length()); try { applicationContext = new ClassPathXmlApplicationContext(CTX_NO_DROP_PATH); repositoryService = (RepositoryService) applicationContext.getBean("repositoryService"); } finally { // Reset file content such that future test are not seeing something funny IoUtil.writeStringToFile(originalBpmnFileContent, filePath); } // Assertions come AFTER the file write! Otherwise the process file is messed up if the assertions fail. assertEquals(2, repositoryService.createDeploymentQuery().count()); assertEquals(6, repositoryService.createProcessDefinitionQuery().count()); } public void testAutoDeployWithCreateDropOnCleanDb() { createAppContext(CTX_CREATE_DROP_CLEAN_DB); assertEquals(1, repositoryService.createDeploymentQuery().count()); assertEquals(3, repositoryService.createProcessDefinitionQuery().count()); } // --Helper methods ---------------------------------------------------------- private void removeAllDeployments() { for (Deployment deployment : repositoryService.createDeploymentQuery().list()) { repositoryService.deleteDeployment(deployment.getId(), true); } } private boolean waitUntilFileIsWritten(String filePath, int expectedBytes) throws URISyntaxException { while (IoUtil.getFile(filePath).length() != (long) expectedBytes) { try { wait(100L); } catch (InterruptedException e) { fail(e.getMessage()); } } return true; } }