### Eclipse Workspace Patch 1.0 #P jbpm4 Index: modules/pvm/src/main/resources/jbpm.execution.hbm.xml =================================================================== --- modules/pvm/src/main/resources/jbpm.execution.hbm.xml (revision 6285) +++ modules/pvm/src/main/resources/jbpm.execution.hbm.xml (working copy) @@ -247,6 +247,7 @@ + Index: modules/api/src/main/java/org/jbpm/api/job/Timer.java =================================================================== --- modules/api/src/main/java/org/jbpm/api/job/Timer.java (revision 6285) +++ modules/api/src/main/java/org/jbpm/api/job/Timer.java (working copy) @@ -38,5 +38,8 @@ /** indicates the delay for repeating this timer after successful execution */ String getRepeat(); + + /** indicates the cron expression used for this timer*/ + String getCronExpression(); } \ No newline at end of file Index: modules/jpdl/src/main/java/org/jbpm/jpdl/internal/xml/JpdlParser.java =================================================================== --- modules/jpdl/src/main/java/org/jbpm/jpdl/internal/xml/JpdlParser.java (revision 6400) +++ modules/jpdl/src/main/java/org/jbpm/jpdl/internal/xml/JpdlParser.java (working copy) @@ -40,6 +40,8 @@ import org.jbpm.jpdl.internal.activity.JpdlBinding; import org.jbpm.jpdl.internal.activity.MailListener; import org.jbpm.jpdl.internal.model.JpdlProcessDefinition; +import org.jbpm.pvm.internal.cal.CronExpression; +import org.jbpm.pvm.internal.cal.Duration; import org.jbpm.pvm.internal.el.Expression; import org.jbpm.pvm.internal.email.impl.MailProducerImpl; import org.jbpm.pvm.internal.email.impl.MailTemplate; @@ -361,8 +363,13 @@ String duedatetime = XmlUtil.attribute(timerElement, "duedatetime"); if (duedate!=null) { - timerDefinition.setDueDateDescription(duedate); - + if (Duration.isValidExpression(duedate)) { + timerDefinition.setDueDateDescription(duedate); + } else if (CronExpression.isValidExpression(duedate)) { + timerDefinition.setCronExpression(duedate); + }else { + timerDefinition.setDueDateDescription(duedate); + } } else if (duedatetime!=null) { String dueDateTimeFormatText = (String) EnvironmentImpl.getFromCurrent("jbpm.duedatetime.format", false); if (dueDateTimeFormatText==null) { Index: modules/test-db/src/test/java/org/jbpm/test/timer/TimerCrontabTest.java =================================================================== --- modules/test-db/src/test/java/org/jbpm/test/timer/TimerCrontabTest.java (revision 0) +++ modules/test-db/src/test/java/org/jbpm/test/timer/TimerCrontabTest.java (revision 0) @@ -0,0 +1,206 @@ +package org.jbpm.test.timer; + +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.HashMap; +import java.util.List; + +import org.jbpm.api.ProcessInstance; +import org.jbpm.api.job.Job; +import org.jbpm.api.job.Timer; +import org.jbpm.test.JbpmTestCase; + + +public class TimerCrontabTest extends JbpmTestCase { + + /* + * Uncomment this to test with job executor being active + public void testTimerCrontabWithJobExecutor() { + + int currentHour = Calendar.getInstance().get(Calendar.HOUR_OF_DAY); + + deployJpdlXmlString( + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + ""); + + + HashMap vars = new HashMap(); + vars.put("index", 0); + + ProcessInstance pi = executionService.startProcessInstanceByKey("TimerCrontab", vars); + + try { + Thread.sleep(120000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + pi = executionService.findProcessInstanceById(pi.getId()); + String executionId = pi.findActiveExecutionIn("state1").getId(); + executionService.signalExecutionById(executionId); + } + */ + + public void testTransitionTimerCrontab() { + + Calendar cl = Calendar.getInstance(); + int currentHour = cl.get(Calendar.HOUR_OF_DAY); + cl.add(Calendar.MINUTE, 1); + cl.set(Calendar.SECOND, 0); + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm"); + + deployJpdlXmlString( + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + ""); + + + HashMap vars = new HashMap(); + vars.put("index", 0); + + ProcessInstance pi = executionService.startProcessInstanceByKey("TimerCrontab", vars); + + List jobs = managementService.createJobQuery().timers().list(); + assertEquals(1, jobs.size()); + assertEquals("0 * "+ currentHour +" * * ?", ((Timer) jobs.get(0)).getCronExpression()); + + assertEquals(sdf.format(cl.getTime()), sdf.format(((Timer) jobs.get(0)).getDuedate())); + + + managementService.executeJob(jobs.get(0).getId()); + + jobs = managementService.createJobQuery().timers().list(); + assertEquals(1, jobs.size()); + + assertEquals(sdf.format(cl.getTime()), sdf.format(((Timer) jobs.get(0)).getDuedate())); + + managementService.executeJob(jobs.get(0).getId()); + + jobs = managementService.createJobQuery().timers().list(); + assertEquals(1, jobs.size()); + + assertEquals(sdf.format(cl.getTime()), sdf.format(((Timer) jobs.get(0)).getDuedate())); + + managementService.executeJob(jobs.get(0).getId()); + + assertProcessInstanceEnded(pi); + + pi = executionService.findProcessInstanceById(pi.getId()); + assertNull(pi); + } + + public void testEventTimerCrontab() { + + Calendar cl = Calendar.getInstance(); + int currentHour = cl.get(Calendar.HOUR_OF_DAY); + cl.add(Calendar.MINUTE, 1); + cl.set(Calendar.SECOND, 0); + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm"); + + deployJpdlXmlString( + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + ""); + + + HashMap vars = new HashMap(); + vars.put("index", 0); + + ProcessInstance pi = executionService.startProcessInstanceByKey("TimerCrontab", vars); + + List jobs = managementService.createJobQuery().timers().list(); + assertEquals(1, jobs.size()); + assertEquals("0 * "+ currentHour +" * * ?", ((Timer) jobs.get(0)).getCronExpression()); + + assertEquals(sdf.format(cl.getTime()), sdf.format(((Timer) jobs.get(0)).getDuedate())); + + + managementService.executeJob(jobs.get(0).getId()); + + jobs = managementService.createJobQuery().timers().list(); + assertEquals(1, jobs.size()); + + assertEquals(sdf.format(cl.getTime()), sdf.format(((Timer) jobs.get(0)).getDuedate())); + + managementService.executeJob(jobs.get(0).getId()); + + jobs = managementService.createJobQuery().timers().list(); + assertEquals(1, jobs.size()); + + assertEquals(sdf.format(cl.getTime()), sdf.format(((Timer) jobs.get(0)).getDuedate())); + + managementService.executeJob(jobs.get(0).getId()); + + pi = executionService.findProcessInstanceById(pi.getId()); + String executionId = pi.findActiveExecutionIn("state1").getId(); + executionService.signalExecutionById(executionId); + + assertProcessInstanceEnded(pi); + + pi = executionService.findProcessInstanceById(pi.getId()); + assertNull(pi); + + } + +} + Index: modules/pvm/src/main/java/org/jbpm/pvm/internal/model/TimerDefinitionImpl.java =================================================================== --- modules/pvm/src/main/java/org/jbpm/pvm/internal/model/TimerDefinitionImpl.java (revision 6285) +++ modules/pvm/src/main/java/org/jbpm/pvm/internal/model/TimerDefinitionImpl.java (working copy) @@ -53,7 +53,7 @@ return repeat; } public void setRepeat(String repeat) { - if (cronExpression != null) { + if (cronExpression != null && repeat != null) { throw new JbpmException("Can't use 'repeat' and 'cronExpression' together for same timer"); } this.repeat = repeat; Index: modules/pvm/src/main/java/org/jbpm/pvm/internal/model/ScopeInstanceImpl.java =================================================================== --- modules/pvm/src/main/java/org/jbpm/pvm/internal/model/ScopeInstanceImpl.java (revision 6404) +++ modules/pvm/src/main/java/org/jbpm/pvm/internal/model/ScopeInstanceImpl.java (working copy) @@ -306,7 +306,9 @@ if (timer.getDuedate() == null && timerDefinition.getCronExpression() != null) { try { - timer.setDuedate(new CronExpression(timerDefinition.getCronExpression()).getNextValidTimeAfter(Clock.getTime())); + CronExpression cronExpr = new CronExpression(timerDefinition.getCronExpression()); + timer.setDuedate(cronExpr.getNextValidTimeAfter(Clock.getTime())); + timer.setCronExpression(cronExpr.getCronExpressionString()); } catch (ParseException pe) { throw new JbpmException("Can't parse cron expression " + timerDefinition.getCronExpression(), pe); } Index: modules/pvm/src/main/java/org/jbpm/pvm/internal/job/TimerImpl.java =================================================================== --- modules/pvm/src/main/java/org/jbpm/pvm/internal/job/TimerImpl.java (revision 6360) +++ modules/pvm/src/main/java/org/jbpm/pvm/internal/job/TimerImpl.java (working copy) @@ -28,6 +28,7 @@ import org.jbpm.api.cmd.Environment; import org.jbpm.api.job.Timer; import org.jbpm.internal.log.Log; +import org.jbpm.pvm.internal.cal.CronExpression; import org.jbpm.pvm.internal.cal.Duration; import org.jbpm.pvm.internal.env.EnvironmentImpl; import org.jbpm.pvm.internal.id.DbidGenerator; @@ -58,7 +59,9 @@ protected String signalName; protected String eventName; protected String repeat; + protected String cronExpression; + public static final String EVENT_TIMER = "timer"; public TimerImpl() { @@ -91,14 +94,18 @@ } if (eventName!=null) { + ObservableElement eventSource = execution.getActivity(); + if (log.isDebugEnabled()) log.debug("firing event "+eventName+" into "+eventSource); execution.fire(eventName, eventSource); } boolean deleteThisJob = true; // if there is no repeat on this timer - if (repeat==null) { + // nor cron expression is used + // or execution is ended meaning that it was signaled to continue + if (repeat==null && cronExpression == null || (execution.isEnded())) { // delete the job if (log.isDebugEnabled()) log.debug("deleting " + this); DbSession dbSession = environment.get(DbSession.class); @@ -111,10 +118,16 @@ deleteThisJob = false; // suppose that it took the timer runner thread a very long time to execute the timers // then the repeat action duedate could already have passed - do { - setDueDateDescription(repeat); - } while (duedate.getTime() <= Clock.getTime().getTime()); - + if (repeat != null) { + do { + setDueDateDescription(repeat); + } while (duedate.getTime() <= Clock.getTime().getTime()); + } else if (cronExpression != null) { + + do { + this.setDuedate(new CronExpression(cronExpression).getNextValidTimeAfter(new Date(Clock.getTime().getTime()+3600))); + } while (duedate.getTime() <= Clock.getTime().getTime()); + } if (log.isDebugEnabled()) log.debug("rescheduled "+this+" for "+formatDueDate(duedate)); // release the lock on the timer @@ -188,4 +201,10 @@ public void setRepeat(String repeat) { this.repeat = repeat; } + public String getCronExpression() { + return cronExpression; + } + public void setCronExpression(String cronExpression) { + this.cronExpression = cronExpression; + } } Index: modules/test-db/src/test/java/org/jbpm/test/timer/Listener.java =================================================================== --- modules/test-db/src/test/java/org/jbpm/test/timer/Listener.java (revision 0) +++ modules/test-db/src/test/java/org/jbpm/test/timer/Listener.java (revision 0) @@ -0,0 +1,22 @@ +package org.jbpm.test.timer; + +import org.jbpm.api.listener.EventListener; +import org.jbpm.api.listener.EventListenerExecution; + +public class Listener implements EventListener { + + private static final long serialVersionUID = 1L; + + public Listener() { + } + +// @Override + public void notify(EventListenerExecution execution) throws Exception { + Integer index = (Integer) execution.getVariable("index"); + + index++; + System.out.println("Index is " + index); + execution.setVariable("index", index); + } + +}