/* 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.test.api.event; import java.util.Calendar; import java.util.Date; import java.util.List; import org.activiti.engine.delegate.event.ActivitiEntityEvent; import org.activiti.engine.delegate.event.ActivitiEvent; import org.activiti.engine.delegate.event.ActivitiEventType; import org.activiti.engine.impl.test.PluggableActivitiTestCase; import org.activiti.engine.impl.util.DefaultClockImpl; import org.activiti.engine.runtime.Clock; import org.activiti.engine.runtime.Job; import org.activiti.engine.runtime.ProcessInstance; import org.activiti.engine.task.Task; import org.activiti.engine.test.Deployment; /** * Test case for all {@link ActivitiEvent}s related to jobs. * * @author Frederik Heremans */ public class JobEventsTest extends PluggableActivitiTestCase { private TestActivitiEntityEventListener listener; /** * Test create, update and delete events of jobs entities. */ @Deployment public void testJobEntityEvents() throws Exception { ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("testJobEvents"); Job theJob = managementService.createJobQuery().processInstanceId(processInstance.getId()).singleResult(); assertNotNull(theJob); // Check if create-event has been dispatched assertEquals(2, listener.getEventsReceived().size()); ActivitiEvent event = listener.getEventsReceived().get(0); assertEquals(ActivitiEventType.ENTITY_CREATED, event.getType()); checkEventContext(event, theJob, false); event = listener.getEventsReceived().get(1); assertEquals(ActivitiEventType.ENTITY_INITIALIZED, event.getType()); checkEventContext(event, theJob, false); listener.clearEventsReceived(); // Update the job-entity. Check if update event is dispatched with update job entity managementService.setJobRetries(theJob.getId(), 5); assertEquals(1, listener.getEventsReceived().size()); event = listener.getEventsReceived().get(0); assertEquals(ActivitiEventType.ENTITY_UPDATED, event.getType()); Job updatedJob = (Job) ((ActivitiEntityEvent) event).getEntity(); assertEquals(5, updatedJob.getRetries()); checkEventContext(event, theJob, true); listener.clearEventsReceived(); // Force timer to fire Calendar tomorrow = Calendar.getInstance(); tomorrow.add(Calendar.DAY_OF_YEAR, 1); processEngineConfiguration.getClock().setCurrentTime(tomorrow.getTime()); waitForJobExecutorToProcessAllJobs(2000, 100); // Check delete-event has been dispatched assertEquals(3, listener.getEventsReceived().size()); // First, a timer fired event has been dispatched event = listener.getEventsReceived().get(0); assertEquals(ActivitiEventType.TIMER_FIRED, event.getType()); checkEventContext(event, theJob, true); // Next, a delete event has been dispatched event = listener.getEventsReceived().get(1); assertEquals(ActivitiEventType.ENTITY_DELETED, event.getType()); checkEventContext(event, theJob, true); // Finally, a complete event has been dispatched event = listener.getEventsReceived().get(2); assertEquals(ActivitiEventType.JOB_EXECUTION_SUCCESS, event.getType()); checkEventContext(event, theJob, true); } /** * Timer repetition */ @Deployment public void testRepetitionJobEntityEvents() throws Exception { Clock previousClock = processEngineConfiguration.getClock(); Clock testClock = new DefaultClockImpl(); processEngineConfiguration.setClock(testClock); Date now = new Date(); testClock.setCurrentTime(now); ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("testRepetitionJobEvents"); Job theJob = managementService.createJobQuery().processInstanceId(processInstance.getId()).singleResult(); assertNotNull(theJob); // Check if create-event has been dispatched assertEquals(2, listener.getEventsReceived().size()); ActivitiEvent event = listener.getEventsReceived().get(0); assertEquals(ActivitiEventType.ENTITY_CREATED, event.getType()); checkEventContext(event, theJob, false); event = listener.getEventsReceived().get(1); assertEquals(ActivitiEventType.ENTITY_INITIALIZED, event.getType()); checkEventContext(event, theJob, false); listener.clearEventsReceived(); try { waitForJobExecutorToProcessAllJobs(2000, 100); fail("a new job must be prepared because there are 2 repeats"); }catch (Exception ex){ //expected exception because a new job is prepared } testClock.setCurrentTime(new Date(now.getTime() + 10000L)); try { waitForJobExecutorToProcessAllJobs(2000, 100); fail("a new job must be prepared because there are 2 repeats"); }catch (Exception ex){ //expected exception because a new job is prepared } testClock.setCurrentTime(new Date(now.getTime() + 20000L)); try { waitForJobExecutorToProcessAllJobs(2000, 100); }catch (Exception ex){ fail("There must be no jobs remaining"); } testClock.setCurrentTime(new Date(now.getTime() + 30000L)); try { waitForJobExecutorToProcessAllJobs(2000, 100); }catch (Exception ex){ fail("There must be no jobs remaining"); } // count timer fired events int timerFiredCount = 0; List<ActivitiEvent> eventsReceived = listener.getEventsReceived(); for (ActivitiEvent eventReceived : eventsReceived) { if (ActivitiEventType.TIMER_FIRED.equals(eventReceived.getType())) { timerFiredCount++; } } listener.clearEventsReceived(); processEngineConfiguration.setClock(previousClock); assertEquals(2, timerFiredCount); } @Deployment public void testJobCanceledEventOnBoundaryEvent() throws Exception { Clock testClock = new DefaultClockImpl(); processEngineConfiguration.setClock(testClock); testClock.setCurrentTime(new Date()); runtimeService.startProcessInstanceByKey("testTimerCancelledEvent"); listener.clearEventsReceived(); Task task = taskService.createTaskQuery().singleResult(); taskService.complete(task.getId()); checkEventCount(1, ActivitiEventType.JOB_CANCELED); } @Deployment(resources = "org/activiti/engine/test/api/event/JobEventsTest.testJobCanceledEventOnBoundaryEvent.bpmn20.xml") public void testJobCanceledEventByManagementService() throws Exception { // GIVEN processEngineConfiguration.getClock().setCurrentTime(new Date()); ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("testTimerCancelledEvent"); listener.clearEventsReceived(); Job job = managementService.createJobQuery().processInstanceId(processInstance.getId()).singleResult(); // WHEN managementService.deleteJob(job.getId()); // THEN checkEventCount(1, ActivitiEventType.JOB_CANCELED); } public void testJobCanceledEventOnProcessRedeploy() throws Exception { // GIVEN // deploy process definition String deployment1 = repositoryService.createDeployment().addClasspathResource("org/activiti/engine/test/api/event/JobEventsTest.testTimerFiredForTimerStart.bpmn20.xml").deploy().getId(); listener.clearEventsReceived(); // WHEN String deployment2 = repositoryService.createDeployment().addClasspathResource("org/activiti/engine/test/api/event/JobEventsTest.testTimerFiredForTimerStart.bpmn20.xml").deploy().getId(); // THEN checkEventCount(1, ActivitiEventType.JOB_CANCELED); repositoryService.deleteDeployment(deployment2); repositoryService.deleteDeployment(deployment1); } private void checkEventCount(int expectedCount, ActivitiEventType eventType) {// count timer cancelled events int timerCancelledCount = 0; List<ActivitiEvent> eventsReceived = listener.getEventsReceived(); for (ActivitiEvent eventReceived : eventsReceived) { if (eventType.equals(eventReceived.getType())) { timerCancelledCount++; } } assertEquals(eventType.name() + " event was expected "+ expectedCount+" times.", expectedCount, timerCancelledCount); } /** /** * Test TIMER_FIRED event for timer start bpmn event. */ @Deployment public void testTimerFiredForTimerStart() throws Exception { // there should be one job after process definition deployment // Force timer to start the process Calendar tomorrow = Calendar.getInstance(); tomorrow.add(Calendar.DAY_OF_YEAR, 1); processEngineConfiguration.getClock().setCurrentTime(tomorrow.getTime()); waitForJobExecutorToProcessAllJobs(2000, 100); // Check Timer fired event has been dispatched assertEquals(3, listener.getEventsReceived().size()); assertEquals(ActivitiEventType.TIMER_FIRED, listener.getEventsReceived().get(0).getType()); checkEventCount(0, ActivitiEventType.JOB_CANCELED); } /** * Test TIMER_FIRED event for intermediate timer bpmn event. */ @Deployment public void testTimerFiredForIntermediateTimer() throws Exception { runtimeService.startProcessInstanceByKey("testTimerFiredForIntermediateTimer"); // Force timer to start the process Calendar tomorrow = Calendar.getInstance(); tomorrow.add(Calendar.DAY_OF_YEAR, 1); processEngineConfiguration.getClock().setCurrentTime(tomorrow.getTime()); waitForJobExecutorToProcessAllJobs(2000, 100); checkEventCount(0, ActivitiEventType.JOB_CANCELED); checkEventCount(1, ActivitiEventType.TIMER_FIRED); } /** * Test create, update and delete events of jobs entities. */ @Deployment public void testJobEntityEventsException() throws Exception { ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("testJobEvents"); Job theJob = managementService.createJobQuery().processInstanceId(processInstance.getId()).singleResult(); assertNotNull(theJob); // Set retries to 1, to prevent multiple chains of events being thrown managementService.setJobRetries(theJob.getId(), 1); listener.clearEventsReceived(); // Force timer to fire Calendar tomorrow = Calendar.getInstance(); tomorrow.add(Calendar.DAY_OF_YEAR, 1); processEngineConfiguration.getClock().setCurrentTime(tomorrow.getTime()); try { managementService.executeJob(theJob.getId()); fail("Expected exception"); } catch (Exception e) { // exception expected } // Check delete-event has been dispatched assertEquals(5, listener.getEventsReceived().size()); // First, the timer was fired ActivitiEvent event = listener.getEventsReceived().get(0); assertEquals(ActivitiEventType.TIMER_FIRED, event.getType()); checkEventContext(event, theJob, true); // Second, the job-entity was deleted, as the job was executed event = listener.getEventsReceived().get(1); assertEquals(ActivitiEventType.ENTITY_DELETED, event.getType()); checkEventContext(event, theJob, true); // Next, a job failed event is dispatched event = listener.getEventsReceived().get(2); assertEquals(ActivitiEventType.JOB_EXECUTION_FAILURE, event.getType()); checkEventContext(event, theJob, true); // Finally, an update-event is received and the job count is decremented event = listener.getEventsReceived().get(3); assertEquals(ActivitiEventType.ENTITY_UPDATED, event.getType()); checkEventContext(event, theJob, true); event = listener.getEventsReceived().get(4); assertEquals(ActivitiEventType.JOB_RETRIES_DECREMENTED, event.getType()); assertEquals(0, ((Job) ((ActivitiEntityEvent) event).getEntity()).getRetries()); checkEventContext(event, theJob, true); } protected void checkEventContext(ActivitiEvent event, Job entity, boolean scopeExecutionExpected) { assertEquals(entity.getProcessInstanceId(), event.getProcessInstanceId()); assertEquals(entity.getProcessDefinitionId(), event.getProcessDefinitionId()); if(scopeExecutionExpected) { assertEquals(entity.getExecutionId(), event.getExecutionId()); } else { assertEquals(entity.getProcessInstanceId(), event.getExecutionId()); } assertTrue(event instanceof ActivitiEntityEvent); ActivitiEntityEvent entityEvent = (ActivitiEntityEvent) event; assertTrue(entityEvent.getEntity() instanceof Job); assertEquals(entity.getId(), ((Job) entityEvent.getEntity()).getId()); } @Override protected void setUp() throws Exception { super.setUp(); listener = new TestActivitiEntityEventListener(Job.class); processEngineConfiguration.getEventDispatcher().addEventListener(listener); } @Override protected void tearDown() throws Exception { super.tearDown(); if(listener != null) { processEngineConfiguration.getEventDispatcher().removeEventListener(listener); } } }