Correct, signalling the an execution from an eventlistener is not the correct approach. But why make it so 'compact'? You could use a state with a timer on a transition. In that event you can check external things or even just have a java activity after it. After that use a decision node that either loops back to the wait-state with the timer, contiues normally or does something special when the maximum number of retries is reached.
I tried as you described, meaning I set timer on transition for wait state after timer is fired it goes to java activity which is responsible for gathering some information and then it goes to decision point which based on process variables take decision what to do.
It works but not for all scenarios. It throws an exception when decision point forwards flow to wait state again.
wait state-1 -> java -> decision -> wait state-1
2010-02-10 13:08:48,204 WARN [org.hibernate.util.JDBCExceptionReporter] (http-127.0.0.1-8080-1) SQL Error: -104, SQLState: 23000 2010-02-10 13:08:48,204 ERROR [org.hibernate.util.JDBCExceptionReporter] (http-127.0.0.1-8080-1) Violation of unique constraint $$: duplicate value(s) for column(s) $$: SYS_CT_613 in statement [update JBPM4_EXECUTION set DBVERSION_=?, ACTIVITYNAME_=?, PROCDEFID_=?, HASVARS_=?, NAME_=?, KEY_=?, ID_=?, STATE_=?, SUSPHISTSTATE_=?, PRIORITY_=?, HISACTINST_=?, PARENT_=?, INSTANCE_=?, SUPEREXEC_=?, SUBPROCINST_=? where DBID_=? and DBVERSION_=?] 2010-02-10 13:08:48,204 ERROR [org.hibernate.event.def.AbstractFlushingEventListener] (http-127.0.0.1-8080-1) Could not synchronize database state with session org.hibernate.exception.ConstraintViolationException: could not update: [org.jbpm.pvm.internal.model.ExecutionImpl#260128] at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:94) at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:66) . . . Caused by: java.sql.SQLException: Violation of unique constraint $$: duplicate value(s) for column(s) $$: SYS_CT_613 in statement [update JBPM4_EXECUTION set DBVERSION_=?, ACTIVITYNAME_=?, PROCDEFID_=?, HASVARS_=?, NAME_=?, KEY_=?, ID_=?, STATE_=?, SUSPHISTSTATE_=?, PRIORITY_=?, HISACTINST_=?, PARENT_=?, INSTANCE_=?, SUPEREXEC_=?, SUBPROCINST_=? where DBID_=? and DBVERSION_=?] at org.hsqldb.jdbc.Util.throwError(Unknown Source) at org.hsqldb.jdbc.jdbcPreparedStatement.executeUpdate(Unknown Source) at org.jboss.resource.adapter.jdbc.CachedPreparedStatement.executeUpdate(CachedPreparedStatement.java:96) at org.jboss.resource.adapter.jdbc.WrappedPreparedStatement.executeUpdate(WrappedPreparedStatement.java:365) at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2435)
but this scenario works perfectly fine:
wait state-1 -> java -> decision -> wait state-2
Same problem happens as unit test and on JBoss AS and regardless if signaling is made by a timer or manually.
Any ideas what could be wrong?
Small update - manage to do it by setting java activity attribute continue to async. With such setup it works ok for both unit tests and JBoss AS.
Question is what about default setup? Why it causes contraint exception while returning to the node that was previously visited?
I have a slight idea why, but if you can provide a unittest, I'm willing to investigate a little more
Ronald, did you have time to look into that issue? Or could you share your thoughts about it?
It seems to have to do with a state with a timer that triggers leaving the state and in one way or another looping back to the state with the timer in one transaction (ending one timer and creating a new one).... I've not created a minimal testcase that demonstrates this (no time). If you could do that and file a jira issue with mentioning this I would be greatful.
What adding async does is demarcating the transaction, so if the state is async, the transaction for leaving the state is separated from the transaction entering the state again.
I looked into the source code just to see if I can figure it out a bit, sometimes it's good to "get your hands dirty"
Seems like the issue is in
It assigns the same id for two timers which causes constraint exception.
Question is how it should be fixed?
Simple solution is to keep the state/number of loops for the same node and process instance using variables but I am not sure if that is the right thing to do - is more like jBPM user solution than developer.
Any comments on this? I think it is rather important to have it done or am I the only one with use case like this?!
I would be happy to help with this if there will be such a need.
I haven't made use of "state" activities. Looking at the code it looks like a "state" activity will end the current transaction. Transaction demarcation is very important, especially if you are interacting with an external system. You don't want to hold a transaction open while waiting for an external operation to complete. You should let JBPM handle the timeout for you (and possibly have 1 transition for normal work and 1 transition for timeout). If the operation completes before the timeout, the job in the timeout queue needs to be deleted. If you go and add another timeout in the same transaction this might cause a problem. What table/constraint is being violated?
I do not get your remark about 2 seperate transitions. And regarding the 'timeout', he does let that handle by jBPM.
The table where the problem is caused is shown in the error he posts above, the timer table and the only thing that is not allowed to be duplicate there is the ID. This was already determined.
His transactions are already short, and yes it is an external system but if it has to be queried, it has to be queried, no other solution then. Using a async nodes does not solve this.
So I'm not quite sure what you propose.
the whole solution was developed to prevent of keeping transaction while waiting for response from external system. The idea is that first step sends request to external system and then goes into the state for a specified period of time (end of transaction). When timer expires the flow is moved to check node to verify if the response is already available. If so, move on to the rest of the process, if not go back to wait state with new timer.
There are no manual creation of timers whatsoever.
I have modified a bit DatabaseIdComposer class to verify if that helps. The solution is rather simple but it works. It records all generated ids as process variable (a map) that keeps count of the executions for given id. If one was already created it appends an index, for instance:
That solved the problem for my case but I cannot be sure it does not violate anything else and what bothers me is that the process variable is kept for entire life cycle of that process instance.
If I get a chance I will try to look at your code a little more closely. For some reason I wasn't able to load the jpdl into the eclipse editor. Much easier to see the transitions on a diagram than in xml.
I proposed 2 transitions because I was assuming that the external system was an active participant and that it could trigger completion before the timer expired. As this is not the case, no need for 2 transitions.
The one exception you included in your post included a uniqueness constraint violation. Can you paste the definition of the constraint (or key) mentioned?