/* * JBoss, Home of Professional Open Source * Copyright 2005, JBoss Inc., and individual contributors as indicated * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jbpm.scheduler.exe; import java.util.ArrayList; import java.util.Calendar; import java.util.List; import org.jbpm.AbstractJbpmTestCase; import org.jbpm.JbpmConfiguration; import org.jbpm.JbpmContext; import org.jbpm.JbpmException; import org.jbpm.graph.def.ActionHandler; import org.jbpm.graph.def.ProcessDefinition; import org.jbpm.graph.exe.ExecutionContext; import org.jbpm.graph.exe.ProcessInstance; import org.jbpm.graph.exe.Token; import org.jbpm.job.Timer; import org.jbpm.scheduler.SchedulerService; import org.jbpm.svc.Service; import org.jbpm.svc.ServiceFactory; public class SchedulerTest extends AbstractJbpmTestCase { public static class TestSchedulerServiceFactory implements ServiceFactory { private static final long serialVersionUID = 1L; public Service openService() { return new TestSchedulerService(); } public void close() { } } public static class TestSchedulerService implements SchedulerService { List createdTimers = new ArrayList(); List cancelledTimersByName = new ArrayList(); List cancelledTimersByProcessInstance = new ArrayList(); private static final long serialVersionUID = 1L; public void createTimer(Timer timer) { createdTimers.add(timer); } public void deleteTimer(Timer timer) { cancelledTimersByName.add(new Object[] { timer.getName(), timer.getToken() }); } public void deleteTimersByName(String timerName, Token token) { cancelledTimersByName.add(new Object[] { timerName, token }); } public void deleteTimersByProcessInstance(ProcessInstance processInstance) { cancelledTimersByProcessInstance.add(processInstance); } public void close() { } } private static JbpmConfiguration jbpmConfiguration = JbpmConfiguration.parseXmlString("<jbpm-configuration>" + " <jbpm-context>" + " <service name='scheduler' factory='" + TestSchedulerServiceFactory.class.getName() + "' />" + " </jbpm-context>" + "</jbpm-configuration>"); public void testTimerCreation() { ProcessDefinition processDefinition = ProcessDefinition.parseXmlString("<process-definition>" + " <start-state>" + " <transition to='catch crooks' />" + " </start-state>" + " <state name='catch crooks'>" + " <timer name='reminder' " + " duedate='3 business hours' " + " transition='time-out-transition' >" + " <action class='the-remainder-action-class-name' />" + " </timer>" + " </state>" + "</process-definition>"); JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext(); try { TestSchedulerService testSchedulerService = (TestSchedulerService) jbpmContext.getServices() .getSchedulerService(); ProcessInstance processInstance = new ProcessInstance(processDefinition); processInstance.signal(); assertEquals(1, testSchedulerService.createdTimers.size()); Timer scheduledTimer = (Timer) testSchedulerService.createdTimers.get(0); assertEquals("reminder", scheduledTimer.getName()); assertEquals(processDefinition.getNode("catch crooks"), scheduledTimer.getGraphElement()); // System.out.println("due date: "+scheduledTimer.getDueDate()); assertNotNull(scheduledTimer.getDueDate()); assertEquals("the-remainder-action-class-name", scheduledTimer.getAction() .getActionDelegation() .getClassName()); assertSame(processInstance.getRootToken(), scheduledTimer.getToken()); assertEquals("time-out-transition", scheduledTimer.getTransitionName()); } finally { jbpmContext.close(); } } public void testTimerCreationRepeat() { ProcessDefinition processDefinition = ProcessDefinition.parseXmlString("<process-definition>" + " <start-state>" + " <transition to='catch crooks' />" + " </start-state>" + " <state name='catch crooks'>" + " <timer name='reminder' " + " duedate='3 business hours' " + " repeat='10 business minutes' >" + " <action class='the-remainder-action-class-name' />" + " </timer>" + " </state>" + "</process-definition>"); JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext(); try { TestSchedulerService testSchedulerService = (TestSchedulerService) jbpmContext.getServices() .getSchedulerService(); ProcessInstance processInstance = new ProcessInstance(processDefinition); processInstance.signal(); assertEquals(1, testSchedulerService.createdTimers.size()); Timer scheduledTimer = (Timer) testSchedulerService.createdTimers.get(0); assertEquals("reminder", scheduledTimer.getName()); assertEquals(processDefinition.getNode("catch crooks"), scheduledTimer.getGraphElement()); // System.out.println("due date: "+scheduledTimer.getDueDate()); assertNotNull(scheduledTimer.getDueDate()); assertEquals("10 business minutes", scheduledTimer.getRepeat()); assertEquals("the-remainder-action-class-name", scheduledTimer.getAction() .getActionDelegation() .getClassName()); assertSame(processInstance.getRootToken(), scheduledTimer.getToken()); } finally { jbpmContext.close(); } } public void testCreateTimerAction() { ProcessDefinition processDefinition = ProcessDefinition.parseXmlString("<process-definition>" + " <start-state>" + " <transition to='catch crooks' />" + " </start-state>" + " <state name='catch crooks'>" + " <event type='node-enter'>" + " <create-timer name='reminder' " + " duedate='3 business hours' " + " transition='time-out-transition' >" + " <action class='the-remainder-action-class-name' />" + " </create-timer>" + " </event>" + " <transition to='end'/>" + " </state>" + " <end-state name='end'/>" + "</process-definition>"); JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext(); try { TestSchedulerService testSchedulerService = (TestSchedulerService) jbpmContext.getServices() .getSchedulerService(); ProcessInstance processInstance = new ProcessInstance(processDefinition); processInstance.signal(); assertEquals(1, testSchedulerService.createdTimers.size()); Timer scheduledTimer = (Timer) testSchedulerService.createdTimers.get(0); assertEquals("reminder", scheduledTimer.getName()); assertEquals(processDefinition.getNode("catch crooks"), scheduledTimer.getGraphElement()); // System.out.println("due date: "+scheduledTimer.getDueDate()); assertNotNull(scheduledTimer.getDueDate()); assertEquals("the-remainder-action-class-name", scheduledTimer.getAction() .getActionDelegation() .getClassName()); assertSame(processInstance.getRootToken(), scheduledTimer.getToken()); assertEquals("time-out-transition", scheduledTimer.getTransitionName()); // while we are at it, i might as well check if the cancel timer is not executed ;) processInstance.signal(); assertEquals(0, testSchedulerService.cancelledTimersByName.size()); } finally { jbpmContext.close(); } } public void testCreateTimerActionRepeat() { ProcessDefinition processDefinition = ProcessDefinition.parseXmlString("<process-definition>" + " <start-state>" + " <transition to='catch crooks' />" + " </start-state>" + " <state name='catch crooks'>" + " <event type='node-enter'>" + " <create-timer name='reminder' " + " duedate='3 business hours' " + " repeat='10 business minutes'>" + " <action class='the-remainder-action-class-name' />" + " </create-timer>" + " </event>" + " <transition to='end'/>" + " </state>" + " <end-state name='end'/>" + "</process-definition>"); JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext(); try { TestSchedulerService testSchedulerService = (TestSchedulerService) jbpmContext.getServices() .getSchedulerService(); ProcessInstance processInstance = new ProcessInstance(processDefinition); processInstance.signal(); assertEquals(1, testSchedulerService.createdTimers.size()); Timer scheduledTimer = (Timer) testSchedulerService.createdTimers.get(0); assertEquals("reminder", scheduledTimer.getName()); assertEquals(processDefinition.getNode("catch crooks"), scheduledTimer.getGraphElement()); // System.out.println("due date: "+scheduledTimer.getDueDate()); assertNotNull(scheduledTimer.getDueDate()); assertEquals("10 business minutes", scheduledTimer.getRepeat()); assertEquals("the-remainder-action-class-name", scheduledTimer.getAction() .getActionDelegation() .getClassName()); assertSame(processInstance.getRootToken(), scheduledTimer.getToken()); // while we are at it, i might as well check if the cancel timer is not executed ;) processInstance.signal(); assertEquals(0, testSchedulerService.cancelledTimersByName.size()); } finally { jbpmContext.close(); } } public void testTimerCancelAction() { ProcessDefinition processDefinition = ProcessDefinition.parseXmlString("<process-definition>" + " <start-state>" + " <transition to='catch crooks' />" + " </start-state>" + " <state name='catch crooks'>" + " <timer name='reminder' " + " duedate='3 business hours' " + " repeat='10 business minutes'" + " transition='time-out-transition' >" + " <action class='the-remainder-action-class-name' />" + " </timer>" + " <transition to='end'/>" + " </state>" + " <end-state name='end'/>" + "</process-definition>"); JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext(); try { TestSchedulerService testSchedulerService = (TestSchedulerService) jbpmContext.getServices() .getSchedulerService(); ProcessInstance processInstance = new ProcessInstance(processDefinition); processInstance.signal(); processInstance.signal(); List cancelledTimerNames = testSchedulerService.cancelledTimersByName; assertEquals(1, cancelledTimerNames.size()); Object[] cancelledTimer = (Object[]) cancelledTimerNames.get(0); assertEquals("reminder", cancelledTimer[0]); assertSame(processInstance.getRootToken(), cancelledTimer[1]); } finally { jbpmContext.close(); } } public static class TimerCreateAction implements ActionHandler { private static final long serialVersionUID = 1L; static Timer timer; public void execute(ExecutionContext executionContext) throws Exception { timer = executionContext.getTimer(); } } public void testTimerEvent() { ProcessDefinition processDefinition = ProcessDefinition.parseXmlString("<process-definition>" + " <start-state>" + " <transition to='catch crooks' />" + " </start-state>" + " <state name='catch crooks'>" + " <event type='timer-create'>" + " <action class='org.jbpm.scheduler.exe.SchedulerTest$TimerCreateAction' />" + " </event>" + " <timer name='reminder' " + " duedate='2 seconds' >" + " <action class='the-timer-create-event-class-name' />" + " </timer>" + " <transition to='end'/>" + " </state>" + " <end-state name='end'/>" + "</process-definition>"); JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext(); try { ProcessInstance processInstance = new ProcessInstance(processDefinition); assertNull(TimerCreateAction.timer); processInstance.signal(); assertNotNull(TimerCreateAction.timer); assertEquals("the-timer-create-event-class-name", TimerCreateAction.timer.getAction() .getActionDelegation() .getClassName()); } finally { jbpmContext.close(); } } public void testUnavailableSchedulerService() { ProcessDefinition processDefinition = ProcessDefinition.parseXmlString("<process-definition>" + " <start-state>" + " <transition to='catch crooks' />" + " </start-state>" + " <state name='catch crooks'>" + " <timer name='reminder' " + " duedate='2 seconds' >" + " <action class='the-timer-create-event-class-name' />" + " </timer>" + " </state>" + "</process-definition>"); try { new ProcessInstance(processDefinition).signal(); fail("expected exception"); } catch (JbpmException e) { // OK } } public void testTaskTimerExecution() { ProcessDefinition processDefinition = ProcessDefinition.parseXmlString("<process-definition>" + " <start-state>" + " <transition to='timed task' />" + " </start-state>" + " <task-node name='timed task'>" + " <task name='find the hole in the market'>" + " <timer duedate='23 business seconds'>" + " <action class='geftem-eu-shuppe-oender-ze-konte'/>" + " </timer>" + " </task>" + " </task-node>" + "</process-definition>"); JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext(); try { TestSchedulerService testSchedulerService = (TestSchedulerService) jbpmContext.getServices() .getSchedulerService(); ProcessInstance processInstance = new ProcessInstance(processDefinition); processInstance.signal(); List scheduledTimers = testSchedulerService.createdTimers; assertEquals(1, scheduledTimers.size()); } finally { jbpmContext.close(); } } public static class TimerCustomizingAction implements ActionHandler { private static final long serialVersionUID = 1L; static boolean isCustomized = false; public void execute(ExecutionContext executionContext) throws Exception { assertNotNull(executionContext.getTimer()); assertEquals("reminder", executionContext.getTimer().getName()); isCustomized = true; } } public void testTaskTimerActionExecution() { ProcessDefinition processDefinition = ProcessDefinition.parseXmlString("<process-definition>" + " <start-state>" + " <transition to='timed task' />" + " </start-state>" + " <task-node name='timed task'>" + " <task name='find the hole in the market'>" + " <event type='timer-create'>" + " <action class='org.jbpm.scheduler.exe.SchedulerTest$TimerCustomizingAction' />" + " </event>" + " <timer name='reminder' duedate='23 business seconds'>" + " <action class='geftem-eu-shuppe-oender-ze-konte'/>" + " </timer>" + " </task>" + " </task-node>" + "</process-definition>"); JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext(); try { ProcessInstance processInstance = new ProcessInstance(processDefinition); processInstance.signal(); assertTrue(TimerCustomizingAction.isCustomized); } finally { jbpmContext.close(); } } public void testTimerELCreation() { ProcessDefinition processDefinition = ProcessDefinition.parseXmlString("<process-definition>" + " <start-state>" + " <transition to='get old' />" + " </start-state>" + " <state name='get old'>" + " <timer name='pension' " + " duedate='#{dateOfPension}' " + " transition='time-out-transition' >" + " <action class='the-remainder-action-class-name' />" + " </timer>" + " </state>" + "</process-definition>"); JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext(); try { TestSchedulerService testSchedulerService = (TestSchedulerService) jbpmContext.getServices() .getSchedulerService(); ProcessInstance processInstance = new ProcessInstance(processDefinition); Calendar dateOfPension = Calendar.getInstance(); dateOfPension.set(2036, 1, 12, 2, 10, 0); dateOfPension.clear(Calendar.MILLISECOND); processInstance.getContextInstance() .setVariable("dateOfPension", dateOfPension.getTime()); processInstance.signal(); assertEquals(1, testSchedulerService.createdTimers.size()); Timer scheduledTimer = (Timer) testSchedulerService.createdTimers.get(0); assertEquals("pension", scheduledTimer.getName()); assertEquals(processDefinition.getNode("get old"), scheduledTimer.getGraphElement()); Calendar dateOfPensionTest = Calendar.getInstance(); dateOfPensionTest.clear(Calendar.MILLISECOND); dateOfPensionTest.set(2036, 1, 12, 2, 10, 0); assertEquals(dateOfPensionTest.getTime(), scheduledTimer.getDueDate()); assertNotNull(scheduledTimer.getDueDate()); assertEquals("the-remainder-action-class-name", scheduledTimer.getAction() .getActionDelegation() .getClassName()); assertSame(processInstance.getRootToken(), scheduledTimer.getToken()); assertEquals("time-out-transition", scheduledTimer.getTransitionName()); } finally { jbpmContext.close(); } } public void testTimerELPlusCreation() { ProcessDefinition processDefinition = ProcessDefinition.parseXmlString("<process-definition>" + " <start-state>" + " <transition to='get old' />" + " </start-state>" + " <state name='get old'>" + " <timer name='pension' " + " duedate='#{dateOfBirth} + 65 years' " + " transition='time-out-transition' >" + " <action class='the-remainder-action-class-name' />" + " </timer>" + " </state>" + "</process-definition>"); JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext(); try { TestSchedulerService testSchedulerService = (TestSchedulerService) jbpmContext.getServices() .getSchedulerService(); ProcessInstance processInstance = new ProcessInstance(processDefinition); Calendar dateOfBirth = Calendar.getInstance(); dateOfBirth.set(1971, 1, 12, 2, 10, 0); dateOfBirth.clear(Calendar.MILLISECOND); processInstance.getContextInstance().setVariable("dateOfBirth", dateOfBirth.getTime()); processInstance.signal(); assertEquals(1, testSchedulerService.createdTimers.size()); Timer scheduledTimer = (Timer) testSchedulerService.createdTimers.get(0); assertEquals("pension", scheduledTimer.getName()); assertEquals(processDefinition.getNode("get old"), scheduledTimer.getGraphElement()); assertNotNull(scheduledTimer.getDueDate()); Calendar dateOfPension = Calendar.getInstance(); dateOfPension.set(2036, 1, 12, 2, 10, 0); dateOfPension.clear(Calendar.MILLISECOND); assertEquals(dateOfPension.getTime(), scheduledTimer.getDueDate()); assertEquals("the-remainder-action-class-name", scheduledTimer.getAction() .getActionDelegation() .getClassName()); assertSame(processInstance.getRootToken(), scheduledTimer.getToken()); assertEquals("time-out-transition", scheduledTimer.getTransitionName()); } finally { jbpmContext.close(); } } public void testTimerELMinusCreation() { ProcessDefinition processDefinition = ProcessDefinition.parseXmlString("<process-definition>" + " <start-state>" + " <transition to='get old' />" + " </start-state>" + " <state name='get old'>" + " <timer name='pensionReminder' " + " duedate='#{dateOfPension} - 1 year' " + " transition='time-out-transition' >" + " <action class='the-remainder-action-class-name' />" + " </timer>" + " </state>" + "</process-definition>"); JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext(); try { TestSchedulerService testSchedulerService = (TestSchedulerService) jbpmContext.getServices() .getSchedulerService(); ProcessInstance processInstance = new ProcessInstance(processDefinition); Calendar dateOfPension = Calendar.getInstance(); dateOfPension.set(2036, 1, 12, 2, 10, 0); dateOfPension.clear(Calendar.MILLISECOND); processInstance.getContextInstance() .setVariable("dateOfPension", dateOfPension.getTime()); processInstance.signal(); assertEquals(1, testSchedulerService.createdTimers.size()); Timer scheduledTimer = (Timer) testSchedulerService.createdTimers.get(0); assertEquals("pensionReminder", scheduledTimer.getName()); assertEquals(processDefinition.getNode("get old"), scheduledTimer.getGraphElement()); assertNotNull(scheduledTimer.getDueDate()); Calendar dateOfPensionReminder = Calendar.getInstance(); dateOfPensionReminder.set(2035, 1, 12, 2, 10, 0); dateOfPensionReminder.clear(Calendar.MILLISECOND); assertEquals(dateOfPensionReminder.getTime(), scheduledTimer.getDueDate()); assertEquals("the-remainder-action-class-name", scheduledTimer.getAction() .getActionDelegation() .getClassName()); assertSame(processInstance.getRootToken(), scheduledTimer.getToken()); assertEquals("time-out-transition", scheduledTimer.getTransitionName()); } finally { jbpmContext.close(); } } public void testTimerELCalendarCreation() { ProcessDefinition processDefinition = ProcessDefinition.parseXmlString("<process-definition>" + " <start-state>" + " <transition to='get old' />" + " </start-state>" + " <state name='get old'>" + " <timer name='pension' " + " duedate='#{dateOfPension}' " + " transition='time-out-transition' >" + " <action class='the-remainder-action-class-name' />" + " </timer>" + " </state>" + "</process-definition>"); JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext(); try { TestSchedulerService testSchedulerService = (TestSchedulerService) jbpmContext.getServices() .getSchedulerService(); ProcessInstance processInstance = new ProcessInstance(processDefinition); Calendar dateOfPension = Calendar.getInstance(); dateOfPension.set(2036, 1, 12, 2, 10, 0); dateOfPension.clear(Calendar.MILLISECOND); processInstance.getContextInstance().setVariable("dateOfPension", dateOfPension); processInstance.signal(); assertEquals(1, testSchedulerService.createdTimers.size()); Timer scheduledTimer = (Timer) testSchedulerService.createdTimers.get(0); assertEquals("pension", scheduledTimer.getName()); assertEquals(processDefinition.getNode("get old"), scheduledTimer.getGraphElement()); Calendar dateOfPensionTest = Calendar.getInstance(); dateOfPensionTest.clear(Calendar.MILLISECOND); dateOfPensionTest.set(2036, 1, 12, 2, 10, 0); assertEquals(dateOfPensionTest.getTime(), scheduledTimer.getDueDate()); assertNotNull(scheduledTimer.getDueDate()); assertEquals("the-remainder-action-class-name", scheduledTimer.getAction() .getActionDelegation() .getClassName()); assertSame(processInstance.getRootToken(), scheduledTimer.getToken()); assertEquals("time-out-transition", scheduledTimer.getTransitionName()); } finally { jbpmContext.close(); } } public void testTimerELUnsupportedFormatCreation() { ProcessDefinition processDefinition = ProcessDefinition.parseXmlString("<process-definition>" + " <start-state>" + " <transition to='get old' />" + " </start-state>" + " <state name='get old'>" + " <timer name='pension' " + " duedate='#{dateOfPension}' " + " transition='time-out-transition' >" + " <action class='the-remainder-action-class-name' />" + " </timer>" + " </state>" + "</process-definition>"); JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext(); try { ProcessInstance processInstance = new ProcessInstance(processDefinition); processInstance.getContextInstance().setVariable("dateOfPension", "Today"); processInstance.signal(); } catch (JbpmException je) { assert je.getMessage().indexOf("date") != -1 : je; } finally { jbpmContext.close(); } jbpmContext = jbpmConfiguration.createJbpmContext(); try { ProcessInstance processInstance = new ProcessInstance(processDefinition); processInstance.getContextInstance().setVariable("dateOfPension", "2036-02-12"); processInstance.signal(); } catch (JbpmException je) { assert je.getMessage().indexOf("date") != -1 : je; } finally { jbpmContext.close(); } } public void testTimerErrorCreation() { ProcessDefinition processDefinition = ProcessDefinition.parseXmlString("<process-definition>" + " <start-state>" + " <transition to='get old' />" + " </start-state>" + " <state name='get old'>" + " <timer name='pension' " + " duedate='1 demo' " + " transition='time-out-transition' >" + " <action class='the-remainder-action-class-name' />" + " </timer>" + " </state>" + "</process-definition>"); JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext(); try { ProcessInstance processInstance = new ProcessInstance(processDefinition); processInstance.signal(); } catch (JbpmException je) { Throwable cause = je.getCause(); assertSame(IllegalArgumentException.class, cause.getClass()); assert cause.getMessage().indexOf("demo") != -1 : cause; } finally { jbpmContext.close(); } } }