[JBoss JIRA] Commented: (JBPM-626) CommandExecutorThread thread safety issues
by Tom Baeyens (JIRA)
[ http://jira.jboss.com/jira/browse/JBPM-626?page=comments#action_12374982 ]
Tom Baeyens commented on JBPM-626:
----------------------------------
1) is not a problem as the command executor should *not* see the message until the transaction commits. and that is after the jbpmContext.save(processInstance). if somehow the command executor 'sees' a message before the message-producing transaction commits, then that is the problem.
> CommandExecutorThread thread safety issues
> ------------------------------------------
>
> Key: JBPM-626
> URL: http://jira.jboss.com/jira/browse/JBPM-626
> Project: JBoss jBPM
> Issue Type: Bug
> Components: Core Engine
> Affects Versions: jBPM 3.1, jBPM jPDL 3.2.1, jBPM jPDL 3.2, jBPM 3.1.4, jBPM 3.1.3, jBPM 3.1.2, jBPM 3.1.1
> Environment: Windows XP Pro
> Database: DB2/400 v5r3, HSQLDB
> Reporter: Mark Shotton
> Assigned To: Tom Baeyens
>
> There appears to be a couple of thread safety problems with the behaviour of the CommandExecutorThread:
> (1) Consider the following code:
> JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
> try {
> GraphSession graphSession = jbpmContext.getGraphSession();
>
> // Find the process definition in the database
> ProcessDefinition processDefinition =
> graphSession.findLatestProcessDefinition(processDefinitionName);
>
> // Create an execution of the process definition.
> ProcessInstance processInstance = new ProcessInstance(processDefinition);
>
> // Check that the process instance is in the start state
> Token token = processInstance.getRootToken();
>
> // Start the process execution
> token.signal();
> ?
> jbpmContext.save(processInstance);
>
> } finally {
> // Tear down the pojo persistence context.
> jbpmContext.close();
> }
> If this code is executed while the token is in node ?node 1?, the token.signal() method causes a node-enter event to fire on the next asynchronous node ?node 2? and a message to be stored in the JBPM_MESSAGE table. This message identifies the token as being in ?node 2?. However the node associated with the token in the JBPM_TOKEN table is still in ?node 1? until the jbpmContext.close() method flushes the process instance to the database.
> So the JBPM_MESSAGE.NODE for the token is ?node 2? but the JBPM_TOKEN.NODE for the same token is ?node 1?.
> This means that on line 53 of org.jbpm.command.ExecuteNodeCommand execute(), an exception is thrown:
> public void execute() {
> if (! node.equals(token.getNode())) {
> throw new JbpmException("couldn't continue execution for '"+token+"', token moved");
> }
> and an exception is added to the message so that it is not re-tried.
> As a work-around, I modified the org.jbpm.msg.command.CommandExecutorThread handleProcessingException method in a pretty ugly way; I just leave the message in the table to be re-tried if the exception message contains the String ?token moved?:
> void handleProcessingException(MessageProcessingException e) {
> JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext(jbpmContextName);
> try {
> // get the message session from the context
> DbMessageService dbMessageSessionImpl = (DbMessageService) jbpmContext.getServices().getMessageService();
> MessagingSession messageSession = dbMessageSessionImpl.getMessagingSession();
> // get the problematic command message from the exception
> Message message = e.message;
> // MWS 01/04/06 Workaround for async continuations
> //
> // Will get this exception if the JBPM_MESSAGE.NODE is not the same as
> // the node associated with the JBPM_MESSAGE.TOKEN (i.e. its JBPM_TOKEN.NODE)
> // in the JBPM_MESSAGE table (see org.jbpm.command.ExecuteNodeCommand execute());
> // however this can be caused by a token.signal()
> // persisting the JBPM_MESSAGE (e.g. from a node-enter on an async node)
> // before the token state is persisted by a subsequent call to
> // jbpmContext.close(); e.g.
> //
> // try {
> // ...
> // token.signal() //Writes to JBPM_MESSAGE if async node entered
> // jbpmContext.save();
> // } finally {
> // jbpmContext.close //Updates JBPM.TOKEN in DB
> //
> // i.e. the above is not thread-safe
> //
> // As a workaround, leave the message to be processed again
> // on the following invocation of the CommandExecutorThread
> //
> if (e.getCause().getMessage() != null) {
> if (e.getCause().getMessage().indexOf("token moved")!=-1) {
> try {
> Thread.sleep(2000);
> } catch(InterruptedException ie) {
> //Do nothing
> }
> return;
> }
> }
>
> // remove the problematic message from the queue
> dbMessageSessionImpl.receiveByIdNoWait(message.getId());
>
> message = Message.createCopy(message);
> // update the message with the stack trace
> StringWriter sw = new StringWriter();
> e.printStackTrace(new PrintWriter(sw));
> message.setException(sw.toString());
> // update the message with the jbpm-error-queue destination
> message.setDestination(errorDestination);
>
> // resend
> messageSession.save(message);
> } finally {
> jbpmContext.close();
> }
> }
> (2) There also appears to be a problem with the interaction of the SchedulerThread and the CommandExecutorThread. For a node ?node 1? that is marked asynchronous and has a timer that fires on node-enter with an associated action that executes immediately, the ActionHandler on the timer action may progress the token from asynchronous node ?node 1? to node ?node 2? and update the token in the JBPM_TOKEN table. However the update of the JPBM_TOKEN row may occur before the CommandExecutorThread has consumed the message in the JBPM_MESSAGE table that is stored on entry into ?node 1? and details that the token is currently in ?node 1?.
> So the JBPM_MESSAGE.NODE for the token is ?node 1? but the JBPM_TOKEN.NODE for the same token is ?node 2?. This means that on line 53 of org.jbpm.command.ExecuteNodeCommand execute(), an exception is thrown (as described in (1)):
> public void execute() {
> if (! node.equals(token.getNode())) {
> throw new JbpmException("couldn't continue execution for '"+token+"', token moved");
> }
> If node 2 is also asynchronous, the situation can arise where the same token ID appears in the JBPM_MESSAGE table twice, with each identifying a different node; ?node 1? and ?node 2?.
> As a work-around, in the ActionHandler for the timer, I wait for any messages related to the current node to be consumed by the CommandExecutorThread before progressing the token (I make the assumption that the JBPM_MESSAGE table can only contain one message per token):
> JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
> try {
> graphSession = jbpmContext.getGraphSession();
>
> //Load the processInstance from the database
> processInstance = graphSession.loadProcessInstance(getProcessInstanceId());
> ?
> MessagingSession messagingSession = jbpmContext.getMessagingSession();
> for (;;) {
> java.util.Iterator iterator = messagingSession.getMessageIterator("CMD_EXECUTOR");
> boolean foundToken = false;
> while(iterator.hasNext()) {
> Message message = (Message) iterator.next();
> if (message.getToken() == token) {
> foundToken = true;
> }
> }
> if (foundToken) {
> try {
> System.out.println("Found message for token: " + token.getId());
> System.out.println("Waiting for CommandExecutorThread to consume message");
> Thread.sleep(500);
> } catch (InterruptedException e) {
> //Do nothing
> }
> } else {
> break;
> }
> }
>
> ?
>
> //Progress over the 'credit_approved' transition
> taskInstance.end("credit_approved");
> jbpmContext.save(processInstance);
> } finally {
> jbpmContext.close();
> }
> Note that I open a new jbpmContext rather than getting this from the ExecutionContext because the code above executes in a new thread spawned by the ActionHandler. However, this work-around occasionally produces the following exception, so I?m obviously doing something wrong (I haven?t investigated this in detail as yet):
> Found message for token: 6
> Waiting for CommandExecutorThread to consume message
> org.jbpm.persistence.JbpmPersistenceException: couldn't commit hibernate session
> at org.jbpm.persistence.db.DbPersistenceService.close(DbPersistenceService.java:171)
> at org.jbpm.svc.Services.close(Services.java:211)
> at org.jbpm.JbpmContext.close(JbpmContext.java:138)
> at com.mdsuk.bpm.webshop.actions.ReceiveCreditReferenceAsyncActionHandler.asyncExecute(ReceiveCreditReferenceAsyncActionHandler.java:103)
> at com.mdsuk.bpm.webshop.actions.AsyncActionHandler.run(AsyncActionHandler.java:24)
> at java.lang.Thread.run(Unknown Source)
> Caused by: org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [org.jbpm.graph.exe.Token#6]
> at org.hibernate.persister.entity.AbstractEntityPersister.check(AbstractEntityPersister.java:1635)
> at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2208)
> at org.hibernate.persister.entity.AbstractEntityPersister.updateOrInsert(AbstractEntityPersister.java:2118)
> at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2374)
> at org.hibernate.action.EntityUpdateAction.execute(EntityUpdateAction.java:84)
> at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:243)
> at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:227)
> at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:141)
> at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:296)
> at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:27)
> at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:980)
> at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:353)
--
This message is automatically generated by JIRA.
-
If you think it was sent incorrectly contact one of the administrators: http://jira.jboss.com/jira/secure/Administrators.jspa
-
For more information on JIRA, see: http://www.atlassian.com/software/jira
18 years, 10 months
[JBoss JIRA] Updated: (JBPM-642) Signaling RootToken on Fork Node
by Tom Baeyens (JIRA)
[ http://jira.jboss.com/jira/browse/JBPM-642?page=all ]
Tom Baeyens updated JBPM-642:
-----------------------------
Fix Version/s: jBPM jPDL 3.2.2
> Signaling RootToken on Fork Node
> ---------------------------------
>
> Key: JBPM-642
> URL: http://jira.jboss.com/jira/browse/JBPM-642
> Project: JBoss jBPM
> Issue Type: Feature Request
> Components: Core Engine
> Reporter: Enric Cecilla
> Assigned To: Tom Baeyens
> Fix For: jBPM jPDL 3.2.2
>
>
> Giving the following processdefinition:
> *start->fork->a->join->end
> ->b ^
> When RootToken is on Fork node trying to signal again the RootToken actually moves the token to one branch state, then RootToken can be signalled til it is on the Join node. At that moment it can be signalled again and it leaves the Join node.
> pi.signal();
> assertEquals( "fork", pi.getRootToken().getNode().getName() ); //true
> pi.signal();
> assertEquals( "b", pi.getRootToken().getNode().getName() ); //true
> pi.signal();
> assertEquals( "join", pi.getRootToken().getNode().getName() ); //true
> Does it makes any sense being able to signal a RootToken on Fork node which has children on every branch? From my point of view signalling on that condition should throw and Exception. Only child tokens should be allowed to signal since "real" tasks are on child tokens
--
This message is automatically generated by JIRA.
-
If you think it was sent incorrectly contact one of the administrators: http://jira.jboss.com/jira/secure/Administrators.jspa
-
For more information on JIRA, see: http://www.atlassian.com/software/jira
18 years, 10 months
[JBoss JIRA] Closed: (JBPM-539) events on cancel, suspend, resume
by Tom Baeyens (JIRA)
[ http://jira.jboss.com/jira/browse/JBPM-539?page=all ]
Tom Baeyens closed JBPM-539.
----------------------------
Fix Version/s: (was: jBPM jPDL 3.2.2)
Resolution: Rejected
i am too scared of the performance impact that this will have. cause the event propagation mandates that events have to bubble up to the process definition. only in a few deployments, this feature will be used. i don't think it's worth introducing the minor performance penalty that all the other have to pay for this as well.
so we will not add it now. i'll keep it in mind for when we can do more architectural changes like in 4.0
> events on cancel, suspend, resume
> ---------------------------------
>
> Key: JBPM-539
> URL: http://jira.jboss.com/jira/browse/JBPM-539
> Project: JBoss jBPM
> Issue Type: Feature Request
> Reporter: James Depoorter
> Assigned To: Tom Baeyens
>
> Events on cancel, suspend and resume should be available.
> Preferably a "beforeXYZ" and "afterXYZ"
--
This message is automatically generated by JIRA.
-
If you think it was sent incorrectly contact one of the administrators: http://jira.jboss.com/jira/secure/Administrators.jspa
-
For more information on JIRA, see: http://www.atlassian.com/software/jira
18 years, 10 months
[JBoss JIRA] Created: (JBPM-835) check out jeff's bug reports
by Tom Baeyens (JIRA)
check out jeff's bug reports
----------------------------
Key: JBPM-835
URL: http://jira.jboss.com/jira/browse/JBPM-835
Project: JBoss jBPM
Issue Type: Task
Components: Core Engine
Reporter: Tom Baeyens
Assigned To: Tom Baeyens
Fix For: jBPM jPDL 3.2
When I created a project in Eclipse with jbpm-jpdl-3.2.Beta2 as the source there was a number of compilation issues. These I could resolve as follows:
1) jms example - remove or comment out the offending code. This example uses APIs that do not exist in the product.
2) org.apache.tools.ant.BuildException in src/identity/LoadIdentitiesTask required ant.jar to be added to lib
3) my rules examples require drools-compiler-3.0.5 and drools-core-3.0.5 to be added to lib
4) org.jbpm.jcr.jackrabbit.JackrabbitJcrServiceFactory requires jackrabbit-core-1.0.1 to be added to lib
--
This message is automatically generated by JIRA.
-
If you think it was sent incorrectly contact one of the administrators: http://jira.jboss.com/jira/secure/Administrators.jspa
-
For more information on JIRA, see: http://www.atlassian.com/software/jira
18 years, 10 months
[JBoss JIRA] Created: (JBPM-982) oracle : one single sequence for all jbpm tables
by Adrian Dimulescu (JIRA)
oracle : one single sequence for all jbpm tables
------------------------------------------------
Key: JBPM-982
URL: http://jira.jboss.com/jira/browse/JBPM-982
Project: JBoss jBPM
Issue Type: Bug
Components: Core Engine
Affects Versions: jBPM jPDL 3.2
Environment: jBPM persisted in Oracle database
Reporter: Adrian Dimulescu
Assigned To: Tom Baeyens
Fix For: jBPM jPDL 3.2.1
In order to generate table ids on Oracle, the hibernate-generated DDL specifies one single sequence :
create sequence hibernate_sequence;
That means that successive values for, say, deployed processes JBPM_TASK can be as far away from each other as 1 from 15000 (given that the same sequence generator is used for the lines in the JBPM_LOG table too).
The IDs do have business meaning, at least as far as the JBPM console is concerned : the ID's are shown there in order to identify processes, tasks etc. That may be confusing for users as they may wonder whatever happened to the tasks between 19 (last he solved) and 57 (the next one).
Ronald van Kuijk says: jbpm 3.2.1 (don't know about 3.2) has a real business key. That could/should be used in the next console.
--
This message is automatically generated by JIRA.
-
If you think it was sent incorrectly contact one of the administrators: http://jira.jboss.com/jira/secure/Administrators.jspa
-
For more information on JIRA, see: http://www.atlassian.com/software/jira
18 years, 10 months
[JBoss JIRA] Created: (JBPM-996) Add ESB Service Node to jpdl and jpdl-designer
by Jeff DeLong (JIRA)
Add ESB Service Node to jpdl and jpdl-designer
----------------------------------------------
Key: JBPM-996
URL: http://jira.jboss.com/jira/browse/JBPM-996
Project: JBoss jBPM
Issue Type: Feature Request
Components: Core Engine
Environment: The ESB has an ESBActionHandler that can be manually configured at each node representing an ESB service in a jPDL processdefinition. A new node type of service-node, that is supported through the jpdl designer, would make it easier for the user to add these sevices, as the designer would know what properties are required for the user to provide.
A nice thing about the ESB from a jBPM perspective (and an ESB Service Node) is that it gives jBPM access to practically any type of service through the same node-type, e.g., web services, rule services, jms queues, file transfer services, etc. So if jBPM has a service node, it eliminates the need to create lots of other specialized node types in jBPM, and less of a need for customers to create custom action handlers.
Reporter: Jeff DeLong
Assigned To: Tom Baeyens
--
This message is automatically generated by JIRA.
-
If you think it was sent incorrectly contact one of the administrators: http://jira.jboss.com/jira/secure/Administrators.jspa
-
For more information on JIRA, see: http://www.atlassian.com/software/jira
18 years, 10 months
[JBoss JIRA] Created: (JBPORTAL-1672) IdentityServiceControllerImpl.java : error message enhancement, (to help find error in config files)
by Antoine Herzog (JIRA)
IdentityServiceControllerImpl.java : error message enhancement, (to help find error in config files)
----------------------------------------------------------------------------------------------------
Key: JBPORTAL-1672
URL: http://jira.jboss.com/jira/browse/JBPORTAL-1672
Project: JBoss Portal
Issue Type: Feature Request
Security Level: Public (Everyone can see)
Components: Portal Identity
Affects Versions: 2.6.1 Final
Reporter: Antoine Herzog
Fix For: 2.6.2 Final
In file : IdentityServiceControllerImpl.java
One error message is not very helpfull.
Line : 170
//check if defaults contains all information
if (module.getType() == null ||
module.getImplementation() == null ||
//module.getJNDIName() == null ||
module.getServiceName() == null ||
module.getConfig() == null)
{
throw new IdentityException("Default module configuration must be complete");
}
This could help better, showing what is missing (and in what Type, which implementation...) :
if (module.getType() == null
|| module.getImplementation() == null
||
// module.getJNDIName() == null ||
module.getServiceName() == null
|| module.getConfig() == null) {
throw new IdentityException(
"Default module configuration must be complete."
+ "module=[" + module + "]" + ", "
+ "module.getType()=[" + module.getType()
+ "]" + ", "
+ "module.getImplementation()=["
+ module.getImplementation() + "]" + ", "
+ "module.getServiceName()=["
+ module.getServiceName() + "]" + ", "
+ "module.getConfig()=["
+ module.getConfig() + "]");
}
Also :
may be the same kind of things on some other errors.
a log (trace level) to tell which files are used (to make sure if they are changed), and which type and implementation are worked : it will help understanding when an exception happens.
I guess this will be helpfull to people changing the configuration.
--
This message is automatically generated by JIRA.
-
If you think it was sent incorrectly contact one of the administrators: http://jira.jboss.com/jira/secure/Administrators.jspa
-
For more information on JIRA, see: http://www.atlassian.com/software/jira
18 years, 10 months