poniedziałek, 7 września 2015

Improved signaling in jBPM 6.3

One of the very powerful features of BPMN2 is signaling. It is realized by throw (send signal) and catch (receive signal) constructs. Depending on which type of signal we need it can be used in different places in the process:

  • throw events
    • intermediate event
    • end event
  • catch events
    • start event
    • intermediate event
    • boundary event

It is powerful as is, but it has been enhanced in jBPM 6.3 in two areas:
  • introduction of signal scopes for throwing events
  • support for parameterized signal names - both throw and catch signal events

Signal scopes

Signals by default rely on process engine (ksession) signaling mechanism that until version 6.3 has been scoped only to the same ksession instance meaning it was not able to signal properly things outside of given ksession. This was especially visible when using strategy different than singleton e.g. per process instance. 
Version 6.3 is equipped with predefined scopes to eliminate this problem and further provide fine grained control over what is going to be signaled.

NOTE: signal scopes apply only to throw events.

  • process instance scope - is the lowest in the hierarchy of scopes that narrows down the signal to given process instance. That means only catch events within same process instance will be singled, nothing outside of process instance will be affected
  • default (ksession) scope - same as in previous versions (and thus called default) that signals only elements known to ksession - behavior will vary depending on what strategy is used 
    • singleton - will signal all instances available for this ksession
    • per request - will signal only currently processed process instance and those with start signal events
    • per process instance - same as per request - will signal only currently processed process instance and those with start signal events
  • project scope - will signal all active process instances of given deployment and start signal events (regardless of the strategy)
  • external scope - allows to signal both project scope way and cross deployments - for cross deployments it requires to have a process variable called 'SignalDeploymentId' that provides information about what deployment/project should be the target of the signal. It was done on purpose to provide deployment id as doing overall broadcast would have negative impact on performance in bigger environments

To illustrate this with an example let's consider few very simple processes:
  • starting up with those that will receive signals - here there is no difference
Intermediate catch signal event

Start signal event

  • next those that will throw events with different scopes

Process instance scoped signal

Default (ksession) scoped signal
Project  scoped signal

External scoped signal
Process instance, default and project does not require any additional configuration to work properly, though external does. This is because external signal uses work item handler as a backend to allow pluggable execution (out of the box jBPM comes with one that is based on JMS). It does support both queue and topic although it is configured with queue in jbpm console/kie workbench.
So to be able to use external signal one must register work item handler that can deal with the external signals. One that comes with jBPM can be easily registered via deployment descriptor (either on server level or project level)
Registered External Send Task work item handler for external scope signals

Some might ask why it is not registered there by default - and the reason is that jBPM supports multiple application servers and all of them deal with JMS differently - mainly they will have different JNDI names for queues and connection factories.
JMS based work item handler supports that configuration but requires to specify these JNDI look up names when registering handler.
As illustrated on above screenshot, when running on JBoss AS/EAP/Wildfly you can simply register it via mvel resolver with default (no arg) constructor and it will pick up the preconfigured queue (queue/KIE.SIGNAL) and connection factory (java:/JmsXA). For other cases you need to specify JNDI names as constructor arguments:

new org.jbpm.process.workitem.jms.
JMSSendTaskWorkItemHandler("jms/CF", "jms/Queue")

Since external signal support cross project signals it does even further than just broadcast. It allows to give what project needs to be signaled and even what process instance within that project. That is all controlled by process variables of the process that is going to throw a signal. Following are supported:
  • SignalProcessInstanceId - target process instance id
  • SignalDeploymentId - target deployment (project)

Both are optional and if not given engine will consider same deployment/project as the process that throws the signal, and broadcast in case of missing process instance id. When needed it does allow fine grained control even in cross project signaling.
declared SignalDeploymentId process variable for external scope signal

You can already give it a try yourself by cloning this repo and working with these two projects:

  • single-project - contains all process definitions that working with same project
  • external-project - contains process definition that uses external scope signal (includes a form to enter target deployment id)
But what are the results with these sample process??
  • When using process that signals only with process instance scope (process id: single-project.throw-pi-signal) it will only signal event based subprocess included in the same process definition nothing else
  • When using process that signals with default scope (process id: single-project.throw-default-signal) it will start a process (process id: single-project.start-with-signal) as it has signal start event (regardless of what strategy is used) but will not trigger process that waits in intermediate catch event for other strategies than singleton
  • When using process that signals with project scope (process id: single-project.throw-project-signal) it will start a process (process id: single-project.start-with-signal) as it has signal start event and will trigger process that waits in intermediate catch event (regardless of what strategy is used)
  • When using process that signals with external scope (process id: external-project.throw-external-signal) it will start a process (process id: single-project.start-with-signal) as it has signal start event and will trigger process that waits in intermediate catch event (regardless of what strategy is used) assuming the SignalDeploymentId was set to org.jbpm.test:single-project:1.0.0-SNAPSHOT on start of the process

Parameterized signal names

another enhancement on signals in jBPM 6.3 is to allow signal names to be parameterized. That means you don't have to hardcode signal names in process definition but simply refer to them by process variables. 
That gives extremely valuable approach to dynamically driven process definitions that allow to change the signal it throw or catches based on the state of process instance.

One of the use cases that is needed is when multi instance is used and we want individual instances to react to different signals.

Simply refer to it via variable expression as already supported in data input and outputs, user task assignments etc.

#{mysignalVariable}

then make sure that you define mysignalVariable variable in your process and it has a value before it enters the signal event node.

And that's it for now, stay tuned for more news about jBPM 6.3 that is almost out the door.

22 komentarze:

  1. Hi Maciej,

    Thanks for the detailed explanations. This is very helpful.

    Regarding signal scopes, Can I assume below signal scopes when using jBPM Services method

    Process Instance Scope / Default Scope - ProcessService.signalProcessInstance()
    Project Scope - ProcessService.signalEvent()

    Is this correct?

    Also, as there is no external scope avaiable, I am thinking of using the AsyncSignalEventCommand to send signals externally. Is this a correct approach?

    Please advise on this.

    Thanks,
    Anindya

    OdpowiedzUsuń
    Odpowiedzi
    1. scopes are mainly intended to be used from process diagrams rather from api. From api you have number of ways to find process instance that you would like to send signal to so it's less important.

      Process Instance Scope / Default Scope - ProcessService.signalProcessInstance()
      -- it is process instance scope as it will only signal that given instance

      Project Scope - ProcessService.signalEvent()
      -- yes, we can consider it as project scope as it will use underlying runtime manager to signal

      Also, as there is no external scope avaiable, I am thinking of using the AsyncSignalEventCommand to send signals externally. Is this a correct approach?
      -- again, external was for from within the process use case as from api you can use number of techniques to send signals over any medium

      hope this helps

      Usuń
  2. Thanks for the clarifications. It really helps as always. :)

    OdpowiedzUsuń
  3. Hello Maciej,
    did parameterized signal names is available in jBPM 6.1? I whant to send signal to multi-instance subprocess in process but this signal is receive by any working process with same multi-instance process with same reference signal name. I whant to add variable to signal ref name but with this variable process build fail ('stopUzgodnienia#{processId}' is not a valid value for 'NCName'). I use web designer to build process.

    OdpowiedzUsuń
    Odpowiedzi
    1. Ok. If parameterized signal names is not available in version 6.1 maybe something else help in my issue. Did version 6.1 have any scope at signal? How to send signal exacly to one reference signal? This reference signal is part of multi-instance subprocess (whant to send signal to all instance of subprocess). Process with this signals and mutli-instance subprocess is fired more than once at time. If you whant i describe this more (with screenshot) to your e-mail in Polish. Thanks for helping any way.

      Usuń
    2. actually there no much options prior to this feature. One thing you might want to try is to use Receive Task instead of signal catch events that are then backed by work item handler which you would have to write. That handler could maybe then distinguish what to listen to etc.

      Feel free to drop by on #jbpm channel at irc.freenode.net

      Usuń
  4. Hi Maciej
    when i want build&deploy external-project, i encounter this problem (java.lang.RuntimeException: [Error: could not resolve class: org.jbpm.process.workitem.jms. JMSSendTaskWorkItemHandler]
    [Near : {... new org.jbpm.process.workitem.jms. ....}]).

    i run jbpm 6.3 on tomcat and set "External Send Task" to "new org.jbpm.process.workitem.jms.
    JMSSendTaskWorkItemHandler("jms/CF", "jms/Queue")" as you mentioned.

    regards. Masoud

    OdpowiedzUsuń
    Odpowiedzi
    1. you can't use that on tomcat as tomcat (by default) does not have any JMS provider. So you have to first configure JMS provider such as ActiveMQ an then setup proper connection factory and queues which are then referenced by their JNDI names from JMSSendTaskWorkItemHandler constructor.

      Usuń
  5. Hi Maciej.

    Can I use process boundary signal capture to capture signals launched by other subprocesses different to the one with the boundary signal attached to cancel execution of the subprocess. ( The signal is not launched by the subprocess to whom the boundary signal is attached).

    We want to implement 4 eyes revision pattern using signals, but its not working with jbpm 6.4

    Regards.

    OdpowiedzUsuń
    Odpowiedzi
    1. not sure I fully understand the question, signals can be send between process instances though the receiving part must be active - either waiting in intermediate catch event or by using event subprocess with signal start event.

      Usuń
  6. Ten komentarz został usunięty przez autora.

    OdpowiedzUsuń
  7. Hi, Maciej,
    Thank you for your fast answer.

    We have just found that subprocess boundary event signal capture is not resolving variable values!.

    Let me explain:

    Our signal ref property value is 'document-approved-#{document-ref}'.

    For our document with ref: 123.

    Subproces is launching the signal properly translated to document-approved-123.
    But we have found that the boundary event signal listener is listening to
    'document-approved-#{document-ref}' instead of 'document-approved-123', so signal is never captured.

    It seems a bug. We have seen that error on org.jbpm.process.instance.event.DefaultSignalManager, line 52.

    We have seen that other intermediate capture signal event listeners are registering themselves properly with the expected signalRef ( 'document-approved-123' ) so it must be a problem of subprocess boundary signal event capture.


    We have solved our case using names without variable parts, but this bug should be fixed.

    Thanks in advance.

    OdpowiedzUsuń
    Odpowiedzi
    1. are we talking about event subprocesses? if so that should be fixed in 6.5 if I recall correctly.

      Usuń
  8. The bug occurs while registering boundary event signal capture on integrated subprocesses.
    Here you have a shot: https://ibb.co/bzyORF

    Regards.

    OdpowiedzUsuń
  9. ok, please file jira issue for further investigation

    OdpowiedzUsuń
  10. Thanks for such a clear explanation! I used to have a similar issue a while ago, but thanks to ax-dynamics.com/, I fixed it very quickly.
    Keep on writing, cheers!

    OdpowiedzUsuń
  11. Hi Maciej,

    I want to trigger a bpmn process on receipt of jms message and then do some more processing but i don't want to hold up the message listener. Should i use the signal option or rely on sub-process?

    OdpowiedzUsuń
    Odpowiedzi
    1. see this article that shows how you can easily mark one node to be async and that will result in releasing the current thread executing the process instance http://mswiderski.blogspot.com/2015/04/asynchronous-continuation-in-jbpm-63.html

      Usuń
  12. Hi Maciej,

    I am also facing the same issue, unable to build dynamic Signal Ref, i am using kie-server 7.1.0.
    Please help me

    Thanks
    Naveen D

    OdpowiedzUsuń
  13. Continuation...

    Steps 1. Start -> ScriptTask1 -> IntermediateCatchEvent -> ScriptTask2 -> End
    Steps 2. ScriptTask1: set pId value as follows
    Long pIdLong = kcontext.getProcessInstance().getId();
    String processInstanceId = String.valueOf(pIdLong);
    System.out.println("processInstanceId:"+processInstanceId );
    kcontext.setVariable("pId",processInstanceId);
    System.out.println(kcontext.getVariable("pId"));

    Steps 3. set Signal Definition in intermediateCatchEvent as follows


    But, when i try to get ....processes/instances/811/signals
    It is giving, Hi#{pId} instead of Hi811

    Thanks
    Naveen D

    OdpowiedzUsuń
  14. bpmn2:signalEventDefinition id="_Urk8YahUEeePH7W4jCwdUg" signalRef="Hi#{pId}"

    OdpowiedzUsuń