cancel
Showing results for 
Search instead for 
Did you mean: 

Expression does not work for time cycle in timer event

sensisdev
Champ in-the-making
Champ in-the-making
Hi I am using Activiti 5.10

I have read the Activiti doco and it says the expression can be used for time cycle in timer event:
e,g.
     <timerEventDefinition>
      <timeDuration>${duration}</timeDuration>
    </timerEventDefinition>

My scenario is we want to read the time cycle from our configuration service instead of hard coding it.

My Activiti looks like this:

    <startEvent id="timerstartyelpendofdayreport" name="Timer Start Yelp End Of Day Report">
      <timerEventDefinition>
        <timeCycle>${configurationRetriever.getTimeCycle()}</timeCycle>
      </timerEventDefinition>
    </startEvent>

The configurationRetriever.getTimeCycle() will return cron expression string.

The problem is the activiti process can not be deployed and I have got the following error:

org.activiti.engine.ActivitiException: Failed to parse cron expression: ${configurationRetriever.getTimeCycle()}
    at org.activiti.engine.impl.calendar.CycleBusinessCalendar.resolveDuedate(CycleBusinessCalendar.java:37)
    at org.activiti.engine.impl.jobexecutor.TimerDeclarationImpl.prepareTimerEntity(TimerDeclarationImpl.java:128)
    at org.activiti.engine.impl.bpmn.deployer.BpmnDeployer.addTimerDeclarations(BpmnDeployer.java:182)
    at org.activiti.engine.impl.bpmn.deployer.BpmnDeployer.deploy(BpmnDeployer.java:149)
    at org.activiti.engine.impl.persistence.deploy.DeploymentCache.deploy(DeploymentCache.java:38)
    at org.activiti.engine.impl.persistence.entity.DeploymentManager.insertDeployment(DeploymentManager.java:42)
    at org.activiti.engine.impl.cmd.DeployCmd.execute(DeployCmd.java:59)
    at org.activiti.engine.impl.cmd.DeployCmd.execute(DeployCmd.java:31)
    at org.activiti.engine.impl.interceptor.CommandExecutorImpl.execute(CommandExecutorImpl.java:24)
    at org.activiti.engine.impl.interceptor.CommandContextInterceptor.execute(CommandContextInterceptor.java:42)
    at org.activiti.spring.SpringTransactionInterceptor$1.doInTransaction(SpringTransactionInterceptor.java:42)
    at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:130)
    at org.activiti.spring.SpringTransactionInterceptor.execute(SpringTransactionInterceptor.java:40)
    at org.activiti.engine.impl.interceptor.LogInterceptor.execute(LogInterceptor.java:33)
    at org.activiti.engine.impl.RepositoryServiceImpl.deploy(RepositoryServiceImpl.java:54)
    at org.activiti.engine.impl.repository.DeploymentBuilderImpl.deploy(DeploymentBuilderImpl.java:106)
    at au.com.sensis.fabric.vendormanagement.process.yelp.Vendor20630FulfillmentEndOfDayReportTest.deploy(Vendor20630FulfillmentEndOfDayReportTest.java:76)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:27)
    at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:74)
    at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:31)
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:82)
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:240)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:180)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:157)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:76)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:195)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:63)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)
Caused by: java.text.ParseException: Unexpected character: $
    at org.activiti.engine.impl.calendar.CronExpression.storeExpressionVals(CronExpression.java:582)
    at org.activiti.engine.impl.calendar.CronExpression.buildExpression(CronExpression.java:360)
    at org.activiti.engine.impl.calendar.CronExpression.<init>(CronExpression.java:277)
    at org.activiti.engine.impl.calendar.CycleBusinessCalendar.resolveDuedate(CycleBusinessCalendar.java:32)
    … 48 more

Could anyone please give me a hand?

Many thanks

Sean
9 REPLIES 9

frederikherema1
Star Contributor
Star Contributor
The following code is responsible for getting the actual time-cycle out of the expression (org.activiti.engine.impl.jobexecutor.TimerDeclarationImpl):

if (executionEntity == null) {
      dueDateString = description.getExpressionText();
    }
    else {
      Object dueDateValue = description.getValue(executionEntity);
      if (dueDateValue instanceof String) {
        dueDateString = (String)dueDateValue;
      }
      else if (dueDateValue instanceof Date) {
        duedate = (Date)dueDateValue;
      }
      else {
        throw new ActivitiException("Timer '"+executionEntity.getActivityId()+"' was not configured with a valid duration/time, either hand in a java.util.Date or a String in format 'yyyy-MM-dd'T'hh:mm:ss'");
      }
    }

When used on a start-event, there is not yet an execution available, so the expression for the timeCycle is not evaluated, but rather just used as-is. This has been an issue with using expressions in start-forms as well, so I think this can be resolved in a similar way.
I've created a JIRA for this: https://jira.codehaus.org/browse/ACT-1415

sensisdev
Champ in-the-making
Champ in-the-making
Hi

Thank you very much and we will wait for Activiti 5.11 then.

Many thanks

Sean

pondermuse1
Champ in-the-making
Champ in-the-making
Hi,

I have just started learning about BPMN 2.0 with activiti-5.14 and have a process with a timer start event where I would like to make the timeCycle value configurable. If I make the value an expression like ${timeCycle}, how would I then go about setting the value at runtime?

Many thanks,
PM.

pondermuse1
Champ in-the-making
Champ in-the-making
Hello again,

I am still trying to figure out how a timer start event can be made configurable. Is this actually now possible in activiti version 5.14? If it isn't, are there any alternative methods to achieve this?

Thanks again,
PM.

martin_grofcik
Confirmed Champ
Confirmed Champ
Hi,

there is already testcase in the 5.15-SNAPSHOT for expressions in TimerStartEvent (I think it should work in 5.14 too).


    <process id="startTimerEventExample" name="Timer start event example">

        <startEvent id="theStart">
            <timerEventDefinition>
                <timeDate>${'2036-11-14T11:12:22'}</timeDate>
            </timerEventDefinition>
        </startEvent>   

        <sequenceFlow id="flow1" sourceRef="theStart" targetRef="receive"/>

        <receiveTask id="receive"/>

        <sequenceFlow id="flow2" sourceRef="receive" targetRef="theEnd"/>

        <endEvent id="theEnd"/>

    </process>

More info StartTimerEventTestRegards
Martin

pondermuse1
Champ in-the-making
Champ in-the-making
Hello martin,

Thanks for your reply. Indeed I can see from the test and other docs that it is possible to have variable expressions for <timeXXX> values. However, I was looking for more of a practical running example showing how the expression value could be changed (at runtime) and therefore triggering the engine to update the timer job's ACT_RU_JOB table row parameters, etc.

An example use case scenario being one where the scheduled task is executing every 5 minutes while timeCycle value is "R/PT5M", and some time later the task starts executing every 10 minutes when timeCycle value is updated to "R/PT10M".

Unfortunately I am not yet too familiarized with activiti's different APIs so I am not too sure how I could make this trigger take place in practice. i.e. Once the expression value changes, should I use the APIs to update the timer job's parameters in ACT_RU_JOB directly? Should I maybe use the APIs to delete the current job and then deploy a new one? Should I use the APIs to delete the model and then re-deploy the model? Or something else entirely?

Thanks again,
PM.

jbarrez
Star Contributor
Star Contributor
You would need to update the job fields and save it back.

That is not exposed in the current API, so you must create a custom Command, that updates the Job and saves it using the JobEntityManager, and pass that to the managementService.executeJob() method.

njames
Champ in-the-making
Champ in-the-making
Hi,
I agree that Martin's test case works, in that a constant expression is evaluated. However, I am trying to use the startEvent timerDefinitionExpression as the very first poster describes, and it doesn't work. Is this expected? I was expecting to have access to my Spring beans. I am using 5.15.1
<code>
    <startEvent id="timerstartevent1" name="Timer start">
      <timerEventDefinition>
        <timeCycle>${fooService.getStartTimeCycle()}</timeCycle>
      </timerEventDefinition>
    </startEvent>
</code>
Note that I am using the @Deployment annotation on my unit test, so the Spring environment is built and running before the deployment happens.

frederikherema1
Star Contributor
Star Contributor
What do you mean by "it doesn't work"? Any stacktraces?

Long time ago, I added a fix, so that the expressions get resolved even of there is no execution-context alive yet (eg. on a start-event, when the execution is about to start but doesn't exist yet) TimerDeclarationImpl:


// ACT-1415: timer-declaration on start-event may contain expressions NOT
    // evaluating variables but other context, evaluating should happen nevertheless
    VariableScope scopeForExpression = executionEntity;
    if(scopeForExpression == null) {
      scopeForExpression = NoExecutionVariableScope.getSharedInstance();
    }

In case the fooService is configured in the "beans" map (see user guide for details on bean-resolution in expressions depending on env), it will be usable. The value returned can be either a Date or a String…