Jugando con jBPM #6 – Decision Node vs Fork/Join Nodes

La idea de este post es remarcar la diferencia que poseen estos dos tipos de nodos que son bastantes distintos pero a simple vista ambos tienen mas de una transición de salida y pueden llegarnos a confundir a la hora de modelar.

Empezamos por el mas simple y el que tiene que quedar claro ya que no tiene muchos usos distintos.

Decision Node:

Este nodo tiene una tarea bastante sencilla, su funcionalidad principal es la de escoger entre 2 o mas ramas alternativas para continuar la ejecución del proceso. Esto significa que el token de ejecución que llega a este nodo (mas acerca del token en otros posts) se propaga por solo una transición de salida del nodo decisión.

La transición por la cual se continuara la ejecución del proceso puede ser elegida de varias formas, que se eligen en tiempo de modelado. Las dos grandes posibilidades que podemos especificar son:

  • Expresiones del tipo JSF (Ej: #{objeto.propiedad < 5})
  • DecisionHandler (clase delegada que implementa la interfaz DecisionHandler y que tiene la lógica en Java para decidir que transición tomar)

Por lo tanto en jPDL podemos ver a un decision Node de las dos siguientes formas:

    <decision name="Para donde voy?" expression="#{variable == true}">
        <transition to="Nodo 1" name="true"></transition>
        <transition to="Nodo 2" name="false"></transition>
    </decision>
o

    <decision name="Para donde voy?">
	<handler class="com.wordpress.salaboy.handlers.MyDelegationHandler"></handler>
	<transition to="Nodo 1" name="Nodo 1"></transition>
        <transition to="Nodo 2" name="Nodo 2"></transition>
    </decision>

Como podemos ver, o delegamos la decision a una clase o lo realizamos con una expresion JSF.

En caso de que optemos por la clase delegada, esta debera implementar la interfaz DelegationHandler que define el siguiente metodo a implementar:

String decide(ExecutionContext executionContext) throws Exception;

La implementación de este método debe retornar el nombre de la transición a tomar.

Fork/Join Nodes:

Estos nodos son un poco mas complicado en su implementación y en su conceptualización ya que para entenderlo por lo general se necesita ver un ejemplo claro de su funcionamiento. En este post voy a hablar un poco mas internamente de su funcionamiento para contrastarlo con el decision node y en el post siguiente voy a presentar un ejemplo concreto del mismo.

Estos nodos se utilizan por lo general en conjunción ya que uno crea varias ramas concurrentes de ejecucion del proceso (y para cada una de estas crea un sub token de ejecución que propaga por cada una de las transiciones salientes del nodo fork, al mismo tiempo) y el otro nodo juntara todas estas ramas para volver a unificar el camino de ejecución del proceso.

Con esta funcionalidad que brinda el nodo fork, podemos modelar casos donde hay tareas que pueden ocurrir al mismo tiempo, es decir, que no tienen que ejecutarse en un orden secuencia (primero una y después otra). Esto muchas veces nos permite optimizar los tiempos del proceso, ya que muchas veces el proceso fue pensado de manera secuencial y a la hora de modelarlo nos damos cuenta de que podemos ejecutar varias tareas en paralelo.

El ejemplo mas clásico de jBPM para mostrar esto, es el que se encuentra en la documentación oficial:

Ejemplo de Proceso
Ejemplo de Proceso

Donde claramente se ve que las actividades “send item” y “receive money” pueden ocurrir en paralelo sin depender una de otra.

Para aclarar un poco mas como es la ejecución de este proceso de ejemplo, podemos decir que cuando la ejecución del proceso llega al nodo fork, este se encarga de crear dos sub token (los tokens en general representan programaticamente los caminos de ejecución) los cuales va a propagar por las ramas shipping y billing al mismo tiempo. Estos dos subtoken llegaran a las actividades “send item” y “receive money” y estas dos actividades se podrán ejecutar en cualquier orden.

Cuando alguna de las dos se ejecuten, la rama actual propagara la ejecución a la siguiente tarea en la rama. Cuando alguna rama llegue a la ultima tarea y el token de esa rama se propague hasta el nodo Join, se dice que la ejecución de esa rama queda esperando a que las demas ramas finalicen su ejecución en el nodo Join. Este comportamiento esta especificado en la implementación del nodo Join, es decir, el nodo Join tiene la tarea de esperar a que la ejecución de todas las ramas finalice para poder propagar el token padre que estaba apuntando al nodo Fork correspondiente.

Sobre esto me gustaria que surgieran dudas y preguntas asi podemos dejar un post bien completo con este tema que no es menor y por ahi confunde mucho a los que recien empiezan.

Espero sus comentarios!

Advertisements

30 thoughts on “Jugando con jBPM #6 – Decision Node vs Fork/Join Nodes”

  1. listo ya comprendi la diferencia entre estos nodos gracias a este post… ahora mi pregunta… mirando el ejemplo del post “Jugando con jBPM #1 ” me doy cuenta de que utilizas un nodo de tipo “state” lo utilizas en el ejemplo como “bandeja de entrada”, y de el salen otros flujos del proceso… ahora el nodo state tiene un comportamiento como el nodo decision, o como el fork… cual es la razon de ser del nodo state??

    Like

  2. Excelente pregunta, para responderte rápido te comento que nodo State es otro de los grandes nodos genericos, pero a diferencia del nodo tipo Node, este no ejecuta su logica y propaga la ejecucion del flujo, sino que este se comporta como un wait state. Lo que significa que cuando la ejecución del proceso llega a este nodo, toda la información del proceso se persiste y se espera que el usuario le indique al proceso que tiene que continuar. Es una especie de estado donde decimos que la aplicación ya hizo lo que tenia que hacer y algún evento externo (por ahí la idea de mencionar que la interacción es de un usuario no tiene que ser confundida con una tarea humana específicamente) continuara la ejecución del proceso. A decir verdad no hay mucho mas que poner de este nodo, sino mas que aclarar su funcionamiento.
    Este tipo de nodo por lo general es usado cuando tenemos una lógica de proceso que debe ejecutarse pero no debe continuar el flujo, sino que alguien o algo debe indicarle al proceso que debe continuar.

    Espero haber respondido tu pregunta! gracias por tus comentarios! cualquier duda por favor comentame!

    Like

  3. Esta muy bueno su blog.

    Si durante el proceso se hace un fork y uno de los subtokens llega al end-state, mientras el otro se encuentra en ejecución, que pasa con el proceso?
    Claramente sería un error de modelamiento, pero que podría llegar a pasar?

    Like

  4. Dependiendo del caso, podria ser un error de modelado o un requisito. En el caso mas normal cuando usamos un node fork, la idea es que todas las ramas que creamos se sincronicen en un nodo de tipo Join. (Estas son las implementaciones por defecto, si quisiéramos hacer otras cosas deberíamos implementar nuestro propio tipo de Fork/Join). Tratando de responder a tu pregunta, lo que sucede es que el token del proceso padre va a quedar parado para siempre en el nodo Fork ya que uno de sus subtokens nunca va a llegar al nodo Join correspondiente.
    Es una respuesta bastante a simple vista.. pero tendrías que analizar tu situación particular.
    Aclare un poco tus dudas?
    Muchas gracias por tu comentario!

    Like

  5. Hello Mauricio.

    if i am in “receive money state”, is there an api’s method that tell me which is the parent of this state or other info about “send item state” ?

    Thanks a lot.
    best regards.

    Like

  6. I think that you can write or look for a named query (in hibernate.queries.hbm.xml) that would do that for you, but at the moment there is no method or query that give you that information. This is because the only relationship between the tokens is the parent.

    Like

  7. Hola, muy buen blog. Trabajo con JBPM hace un año y ahora he tenido un problema con los fork, tal vez sea un bug, o tal vez estoy haciendo algo equivocado.

    Necesito controlar el tiempo de duracion de todo un proceso y enviar alarmas (mail) cada cierto tiempo hasta que el proceso finalice en su totalidad. La forma en la cual hago esto es con un fork al inicio, en uno de los caminos hay un estate con un timer, luego una decision que evalua si por el otro camino ya se finalizó el proceso verificando una variable, sino ha finalizado envia el correo y vuelve al estate.

    En conclusion se ejecuta un nodo de espera en paralelo hasta que finalice el proceso, enviando la alarma cada cierto tiempo.

    El problema es que el token nunca se sincroniza, al llegar al join el timer no detiene su ejecucion y el proceso “se enloquese”.

    Esto es un bug? Existe otra forma de realizar esto?

    Muchas gracias.

    Like

  8. Primero que nada gracias por tus comentarios en mi blog.
    Vamos a la respuesta:
    1) Primero que nada, lo que me comentas es bastante extranio a nivel de modelado, no deberías usar un fork para hacer lo que estas haciendo. Ya que si bien son dos cosas que se realizan en paralelos, la creación de estos dos sub tokens van a causarte problemas en la ejecución de la rama que realmente tiene los pasos del proceso de negocio (probablemente tu proceso enloquece por esto)
    2) Por otro lado los timers (el tag timer) en jBPM estan pensados para avisar cuando una tarea o un nodo no se realiza en un cierto tiempo y cuando la ejecucion del proceso deja la tarea o el nodo, este timer se cancela automática.
    3) Si mal no recuerdo el tag timer es una abreviación de dos tag: create-timer y cancel-timer (deberías buscar en el documentación sobre estos tags ya que no los tengo frescos en la memoria). Pero ellos te darían la flexibilidad que andas buscando, ya que podías poner el create-timer en el start-state y el cancel-timer en tu nodo end-state del proceso.

    Espero haberte ayudado con tu problema, luego comenta me si con estos tags pudiste solucionar el problema.

    Saludos.

    Like

  9. Muchas gracias por tu respuesta.
    Estoy de acuerdo con que no es un uso comun para el fork, pero se requiere realizar esta tarea y debo adaptar lo que hay en JBPM para hecer esto. El problema de tener un timer que inicie en un start y finalice con el proceso (como comentas) es que no puedo enviar un correo cada cierto tiempo.

    El timer se requiere para enviar un correo cada cierto tiempo hasta que el proceso termine, como un bucle que se repite. Yo lo veo como una accion en paralelo con el proceso, tambien he probado colocarlo como un subproceso, pero genera el mismo problema.

    Parece ser un bug que se da al tener un timer en una ramificacion del fork, pero no sé como podria implementar esta funcionalidad sin usar fork.

    Gracias

    Like

  10. En la documentación oficial cuando habla de estos tags que te comentaba (create-timer y cancel-timer) menciona un atributo del tag que es repeat, supuestamente eso se usa, para que hasta que el timer no sea cancelado la acción que le especifiques se repita cada cierto tiempo especificado en este atributo. Te dejo el link de la documentación oficial y espero que esto resuelva tus problemas y te de un modelado mucho mas prolijo.

    Saludos
    Link: http://docs.jboss.org/jbpm/v3/userguide/jpdl.html#create.timer.element

    Like

  11. Hola Salaboy. Enhorabuena por tu blog y tus post sobre jBPM. Actualmente estoy evaluando motores de workflow para el desarrollo de mi tesis con el objetivo de conseguir un entorno ALM que permita certificar CMMI 2 una empresa que parte de una definición de una metodología en EPF. Viendo este post me surge una duda en cuanto a las decisiones. Siguiendo tu ejemplo:

    ¿Dónde le indicas que transición coger?¿es por orden en la declaración?

    Veo claro como hacerlo en el handler pero con la expresión no tengo claro donde lo especificas.

    Muchas gracias por adelantando 🙂

    Like

  12. Tienes razon mi ejemplo no esta claro y esta bastante mal a decir verdad.. ahora mismo lo voy a corregir agregando las dos posibilidades que puedes usar con las expresiones.
    Estas dos posibilidades son partiendo de los nombres, es decir en el ejemplo deberia ser de la siguiente manera:
    Dentro de la expresion deberia decir ${variable = true} y luego en los nombres de los nodos deberia decir true en uno y en el otro false.

    Esa es la primer opcioen, la segunda opcion son las condiciones dentro de las transiciones, donde no usamos expresiones sino donde dentro de cada transision ponemos una condicion y depende de si la condicion se cumple es si se toma la transision.

    Muchas gracias por tu pregunta, ahora trato de arreglar el post

    Like

  13. Hello Salaboy,

    I had a quick questions which I was hoping you could help me with. Is there some looping construct that I can get from the execution context, I have seen the while loop and the until loop, but don’t know how to incorporate it into my workflow.

    for example a part of my workflow is

    I have set the context variables in GetDeviceInfoHandler but need to loop the next step based on the variables that were set in the previous step.

    Thanks,
    Pravin

    Like

  14. Hola, quería saber si habría algún problema haciendo que desde varios task-node salga una transaccion a un fork. Al fork solo llegaria desde uno de ellos, ya que son varios caminos distintos.

    Un saludo.

    Like

  15. Hola Salaboy, tengo un problema con un fork con 3 estados en cada rama (A1, A2, A3 Y B1, B2, B3). Al hacer signal hasta el fork se me ejecutan el A1 y B1 a la vez. Al hacer otro signal, se me vuelve a ejecutar el A1 y luego sigue hasta A2 y A3 pero no se ejecutar la rama B concurrentemente con la A. Por otro lado no entiendo por que se me ejecuta el estado A1 dos veces. Me podrias poner un pequeño ejemplo en Java de como se utiliza un fork para que se ejecuten sus dos ramas concurrentemente con al menos dos estados en cada rama? Por cierto, lo he intentado con estados tal como tienes en el dibujo, y tambien usando subprocesos (process-state). El comportamiento es el mismo.

    Muchisimas gracias y enhorabuena por tu blog!

    Like

  16. Como estas? primero que nada gracias por tu comentario.
    Muchas personas tienen problemas con la concepción de la ejecución de ramas paralelas usando forks.
    Básicamente el concepto que tenes que tener, es que cuando el proceso llega al nodo Fork, se crean token hijos, uno por cada rama. Esto lo podes ver como si se crearan sub ejecuciones. Por lo tanto para avanzar cada una de las ramas, tendrías que ejecutar/llamar al método signal de cada una de las sub ejecuciones por separado.
    Por otro lado, como estas usando states, para pasar al siguiente nodo vas a tener que llamar a signal si o si.
    Lo mismo pasa con process-state. Fijate que si cambias los dos primeros nodos de ambas ramas a tipo Node, la ejecución se va a parar en los segundos nodos de cada rama.
    Espero que sea de ayuda, prometo hacer un post al respecto, pero no se para cuando lo podre tener listo. Ando corto de tiempo.
    Saludos

    Like

  17. Hola!
    Antes que nada te felicito pq son excelentes los ejemplos q has publicado.
    Tengo una consulta sobre como conseguir modelar un caso en que recibo una lista de elementos y por cada uno de ellos tengo q activar un subproceso (que puede ser distinto o no). Tengo un fork con 3 ramas y cada una llega a un subproceso distinto (y luego de juntan en un join).El problema es q este modelo solo me limita a q pueda ejecutar un tipo de subproceso a la vez. Necesitaria algo asi como un fork dinamico..

    desde ya mil gracias por la ayuda!

    Like

      1. Hola,
        Gracias por la respuesta! Es justo lo que estaba necesitando. Si no es problema, aprovecho para consultarte nuevamente. Luego de hacer un signal a un proceso principal (superprocess)que presenta transiciones a distintos subprocesos, de que manera seria posible volver a reactivarlos (un resume)?
        He visto en algunos casos que se busca la instancia del subproceso haciendo un childToken = superProcessInstance.findToken(“to-subproceso”) y luego un childToken.getSubProcessInstance().signal();
        En particular mi duda es como manejar la reactivación de subprocesos que se generan en forma dinámica (caso del fork dinamico).

        Nuevamente te agradezco por tu tiempo.
        Saludos

        Like

  18. Antes que nada, felicitaciones por el blog, nos está siendo de muchísima utilidad al empezar con JBMP.

    Mi pregunta es sobre el Nodo State, en el caso de la bandeja de entrada, al mismo tiempo q entendemos q el nodo state no tiene lógica, como quedaría el código de ese nodo para atender el evento leer mail o el evento logout ?
    Tendría q tener cierta “lógica” para saber que transición seguir? Tiene cierta “similitud” con el nodo decisión, pero estando a la espera de saber q ocurre para “decidir” seguir hacia una u otra transición.. gracias.

    Like

  19. Hola como estas? Gracias por leer el blog y dar feedback.
    Pasando a responder tus preguntas, me gustaria aclarar que el nodo state no deberia ser usado para interacciones con seres humanos como “leer mail” o “logout de una aplicacion”.
    Para ambos casos deberias usar Task Nodes. Que estan pensados estas interacciones. Con respecto a las transiciones de salidas de los nodos, puedes tranquilamente sacar distintas tranciones y luego usar condiciones en las mismas para decidir si continuar o no el flujo dependiendo de la salida de la tarea.
    Saludos

    Like

  20. Hi,

    I’m trying to make a dynamic fork.
    I have a list of user names (“user1,user2,user3”).
    I store this list into a variable (“ParticipantList”).
    This variable is populated in the first task of the process.

    I want to make something like a dynamic fork. For example, if I have 2 users I want to generate 2 taskinstances and delegate the instances to them (one user – one instance).

    Thank you in advance,

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s