La idea de este post es mostrar que detalles hay que tener en cuenta a la hora de crear tareas dinámicas, algo muy útil cuando se presenta un caso donde no podemos saber de antemano cuantas tareas se deberán instanciar, o cuando esta cantidad de tareas nos la dicta algún variable del proceso. (También tenemos que optar por esta opción cuando necesitamos tener mas de una tarea de la cual tenemos una sola definición)

Task Process Example

Task Process Example

En este simple proceso, que carece de significado real (debido a que el modelado, raramente debería ser de la manera planteada), pero útil para demostrar la practica, vamos a ver como podemos crear en el nodo Tomar Curso una cantidad X de tareas “Tomar Curso” dependiendo de cuantos alumnos atiendan al dictado.

Para realizar esto tendríamos que a la hora de iniciar el proceso, en cualquier paso antes de llegar al nodo Tomar Curso asignar una nueva variable en el contexto que indique la cantidad de alumnos que van a asistir al curso. Esto lo hacemos de la siguiente manera.

executionContext.getContextInstance().setVariable(“cantStudent”, 4);

Donde la cantidad 4, podría ser calculada mediante la llamada a un servicio de reservas, o algo similar.

Luego a nivel jPDL vemos como seria la definición del Task-Node para que no nos cree la tarea definida dentro de el automáticamente cuando la ejecución del proceso llegue a este nodo:

<task-node name="Tomar Curso" create-tasks="false">
  <task name="Tomar Curso" swimlane="student"></task>
  <event type="node-enter">
    <action name="Create Dinamic Tasks" class="com.sample.action.CreateDinamicTasksActionHandler"></action>
  </event>
  <transition to="end"></transition>
</task-node>

Dos cosas importantes tenemos que notar en jPDL:

  • create-tasks=”false”: se encarga de que la tarea (task name=”Tomar Curso”) no sea instanciada cuando el flujo de ejecución llegue al task-node
  • La acción en el evento node-enter: esta acción sera la encargada de crear automáticamente las tareas especificadas dentro del nodo task-node.

Sin duda  el comportamiento  del proceso que definimos sera el siguiente:

  • El proceso comienza su ejecución mediante un signal sobre el nodo de arranque
  • La ejecución continua hasta el nodo Tomar Curso. Ya que la ejecución no se detiene en el nodo Dictar Curso, porque he decidido que el nodo Dictar Curso solo debe crear la tarea para el instructor y debe dar la posibilidad de que los alumnos puedan ver sus tareas creadas antes de que se termine de dictar el curso. Para esto utilice la siguiente propiedad del nodo task-node:
    <task-node name="Dictar Curso" signal="unsynchronized">
            <task name="Dictar Curso" swimlane="instructor"></task>
            <transition to="Tomar Curso"></transition>
     </task-node>
  • Cuando la ejecución llega al nodo Tomar Curso, este por definición no crea las tareas (TaskInstances) que tiene definidas dentro, sino que delega esta acción al evento que especifico en el evento node-enter. Esta clase delegada contiene lo siguiente:
    public void execute(ExecutionContext context) throws Exception {
            TaskMgmtInstance tmi=context.getTaskMgmtInstance();
            TaskNode tomarCurso=(TaskNode)context.getNode();
            Task tomarCursoTask=tomarCurso.getTask("Tomar Curso");
            int cantStudent = Integer.parseInt(context.getContextInstance().getVariable("cantStudent").toString());
            for(int i=0; i < cantStudent;i++){
                tmi.createTaskInstance(tomarCursoTask, context.getToken());
            }
        }

    Lo que podemos ver a simple vista es que obtenemos el nodo donde estamos parados, a este le pedimos las tareas (por nombre) que tiene y a esta tareas la instanciamos haciendo uso del método createTaskInstance de la clase TaskMgmtInstance. Como el nodo task-node que contiene a estas tareas no le he cambiado la opción por defecto para hacer signal, va a esperar que se completen las tareas instanciadas dinámicamente (en este caso 4 iguales).

  • Cuando las cuatro tareas se finalizan el proceso llega con su ejecución al nodo end.

Espero que haya servido de ayuda para introducir un poco la API de Task MGMT. Cualquier duda comenten!

Este post tiene como objetivo profundizar sobre la customizacion de almacenamiento de variables de procesos.

(Documentación oficial: http://docs.jboss.com/jbpm/v3.2/userguide/html/context.html#customizingvariablepersistence)

La primer pregunta que nos surge es, cuando necesitamos customizar como se almancenan las variables en jBPM?

A decir verdad, es una buena pregunta. Vamos a empezar haciendo referencia a la documentación oficial en cuanto a las variables que dicen estar soportadas por jBPM para su almacenamiento automático:

  • java.lang.String
  • java.lang.Boolean
  • java.lang.Character
  • java.lang.Float
  • java.lang.Double
  • java.lang.Long
  • java.lang.Byte
  • java.lang.Short
  • java.lang.Integer
  • java.util.Date
  • byte[]
  • java.io.Serializable
  • classes that are persistable with hibernate

Supuestamente estos tipos de variable son fácilmente persistibles por jBPM y no debemos cambiar ninguna configuración para que las variables simplemente se persistan.

De los tipos basicos de datos (String, Boolean, Float, Double, Long, Byte, Short, Integer, Date y byte[]) podemos pensar que hibernate los resuelve directamente fijandose en los mapeos de los tipos de datos especificos para cada base de datos en particular. Y de los dos ultimos tipos (java.io.Serializable y hibernate classes) podemos pensar que vamos a necesitar alguna configuracion especial.
Pero no, ya que el motor de jBPM  para todos los tipos de variable va a revisar el archivo jbpm.varmappings.xml y transformara los objetos de usuario (todos los de la lista anterior) a objetos de tipo VariableInstance, que son objetos que actualmente se encuentran mapeados con Hibernate (mediante un hbm).
Para esta transformación se necesitan dos cosas importantes, los matchers y los converters. (JbpmTypeMatcher, Converter dos interfaces importantes en el paquete: org.jbpm.context.exe)
Estas dos interfaces (JbpmTypeMatcher, Converter) deben ser implementadas en el caso que necesitemos algun tipo nuevo de VariableInstance para nuestro caso particular. Como vemos en la documentacion oficial, los tipos de VariableInstance que tenemos por defecto son los siguientes:
  • DateInstance (with one java.lang.Date field that is mapped to a Types.TIMESTAMP in the database)
  • DoubleInstance (with one java.lang.Double field that is mapped to a Types.DOUBLE in the database)
  • StringInstance (with one java.lang.String field that is mapped to a Types.VARCHAR in the database)
  • LongInstance (with one java.lang.Long field that is mapped to a Types.BIGINT in the database)
  • HibernateLongInstance (this is used for hibernatable types with a long id field. One java.lang.Object field is mapped as a reference to a hibernate entity in the database)
  • HibernateStringInstance (this is used for hibernatable types with a string id field. One java.lang.Object field is mapped as a reference to a hibernate entity in the database)
  • ByteArrayInstance que misteriosamente no aparece en la documentacion
Por lo tanto si empezamos a ver como es la secuencia de funcionamiento, llegamos a la conclusion de que cuando llamamos al metodo setVariable, el objeto de usuario va a ser opcionalmente convertido con el converter apropiado segun el matcher que coincida en el archivo jbpm.varmappings.xml. Cuando nostros estemos customizando este archivo, tenemos que tener cuidado porque el mismo se revisa en orden secuencial, por lo tanto, hay que ver que nuestro tipo custom no matchee con ningun otro matcher antes que el nuestro.
Toda esta interaccion de matchers y converters ocurre dentro de la clase VariableInstance y el metodo createVariableInstance que es invocado cada vez que se llama el metodo setVariable mensionado anteriormente.
public static VariableInstance createVariableInstance(Object value) {
VariableInstance variableInstance = null;

Iterator iter = JbpmType.getJbpmTypes().iterator();
while ( (iter.hasNext())
&& (variableInstance==null) ){
JbpmType jbpmType = (JbpmType) iter.next();

if (jbpmType.matches(value)) {
variableInstance = jbpmType.newVariableInstance();
}
}

if (variableInstance==null) {
variableInstance = new UnpersistableInstance();
}

return variableInstance;
}

Otro clase que juega un papel importante aca es JbpmType:
public JbpmType(JbpmTypeMatcher jbpmTypeMatcher, Converter converter, Class variableInstanceClass) {
this.jbpmTypeMatcher = jbpmTypeMatcher;
this.converter = converter;
this.variableInstanceClass = variableInstanceClass;
}
Ya que podemos observar como se crea engloba al matcher, al converter y al tipo de la clase que estamos por crear el VariableInstance.
Para no extender mucho mas el post, termino dando tres aclaraciones que considero importantes:
  • Vamos a tener que crear un nuevo VariableInstance cuando necesitemos persistir algun objeto de dominio (negocio) con algun mapeo poco comun. Espero tener tiempo y poder postear un ejemplo de eso, asi queda claro que seria un mapeo poco comun.
  • Para nuestros objetos de dominio que tengamos o decidamos mapear con hibernate de la forma mas comun (hbms), siempre y cuando agreguemos los hbms al archivo hibernate.cfg.xml, los objetos seran automaticamente persistidos por hibernate.
  • Para nuestros objetos que no esten mapeados con hibernate pero esten marcados como serializables, estos seran transformados como indica el matcher en jbpm.varmappings.xml a ByteArrayInstance y se guardaran en forma binaria en la base de datos.

La idea del siguiente post es mostrar como cambiamos de fuente de datos de la configuración por defecto de jBPM. Esta tarea es bastante sencilla pero hay que tener un par de cosas en cuenta. Sino podemos pasar un rato largo sin saber que nos esta faltando.

Una de estas cosas básicas a tener en cuenta es que jBPM por defecto utiliza una fuente de datos llamada JbpmDS (por defecto), por lo tanto, la fuente de datos que vamos a crear va a llevar este nombre.

También tenemos que asegurarnos de que no tenemos otra fuente de datos con este nombre ya deployada, este comentario viene porque si estamos usando la suite jBPM, esta usa por defecto una base de datos hypersonic para funcionar. Y la configuración de esta fuente de datos que ya viene configurada en la suite se encuentra en /server/jbpm/deploy/jbpm-ds.xml.

En el caso de que estemos usando la suite jBPM deberemos hacer undeploy de este data source (podemos hacerlo  borrando el archivo o renombrándolo a jbpm-ds.xml.bak). Y luego hacer deploy de nuestra configuración de data source.

Teniendo estas cosas en claro vamos con los pasos necesarios para cambiar la fuente de datos.

1) Con JBoss Application Server abajo debemos agregar el JDBC driver correspondiente para el vendor de base de datos a la que nos queramos conectar. En este caso estoy usando postgreSQL 8.2 por eso el jar que agrego al directorio /server/jbpm/lib/ es: postgresql-8.2-508.jdbc3.jar que se puede descargar de la pagina oficial de postgreSQL. (http://jdbc.postgresql.org/download.html). Esto lamentablemente no lo podemos hacer cuando el server esta corriendo ya que este jar en el directorio lib no seria encontrado hasta que reiniciemos.

2) Buscamos en los ejemplos de JCA (Java Connector Architecture) un data source apropiado para el vendor que estamos usando. En este caso como el vendor es postgreSQL escogemos el archivo: server/docs/examples/jca/postgres-ds.xml. En este caso el directorio de inicio “server” hace referencia al directorio server de la distribución jBPM suite. En caso de que estemos usando un JBoss Application Server instalado por nosotros el directorio “server” vendría siendo la carpeta raíz del Application Server.

Si analizamos este archivo postgres-ds.xml vemos lo siguiente:

<datasources>
  <local-tx-datasource>
    <jndi-name>PostgresDS</jndi-name>
    <connection-url>jdbc:postgresql://[servername]:[port]/[database name]</connection-url>
    <driver-class>org.postgresql.Driver</driver-class>
    <user-name>x</user-name>
    <password>y</password>
        <!-- sql to call when connection is created.  Can be anything, select 1 is valid for PostgreSQL
        <new-connection-sql>select 1</new-connection-sql>
        -->

        <!-- sql to call on an existing pooled connection when it is obtained from pool.  Can be anything, select 1 is valid for PostgreSQL
        <check-valid-connection-sql>select 1</check-valid-connection-sql>
        -->

      <!-- corresponding type-mapping in the standardjbosscmp-jdbc.xml (optional) -->
      <metadata>
         <type-mapping>PostgreSQL 7.2</type-mapping>
      </metadata>
  </local-tx-datasource>

</datasources>

Como podemos apreciar esto es un template que nosotros vamos a tener que completar con los datos de nuestra fuente de datos. (Recordar cambiar el nombre del data source a JbpmDS, case sensitive)

Algo que llama la atención en este archivo de template es la versión de type mapping que tiene asignada por defecto “PostgreSQL 7.2″ y el comentario que aparece arriba de que nos fijemos en el archivo standardjbosscmp-jdbc.xml para ver los mapeos que podemos asignar acá. Este archivo (standardjbosscmp-jdbc.xml) se encuentra en el directorio server/jbpm/conf y en este caso si buscamos dentro de todo este archivo “PostgreSQL” vemos que tenemos 3 mapeos para este vendor de base de datos. Por eso en este caso vamos a cambiar el tipo de mapeo a “PostgreSQL 8.0″ para adecuarnos a los mapeos correspondientes a la versión de base de datos que estamos utilizando.

3) Copiamos el archivo postgres-ds.xml al directorio deploy de nuestro JBoss AS y le cambiamos el nombre a jbpm-ds.xml (o a otro nombre que nos guste mas que cumpla con el formato *-ds.xml, para que el DataSource deployer lo reconozca y realise el deploy correspondiente). En este paso llenamos todos los datos correspondientes.

4) Una cosa muy importante a tener en cuenta es que si estamos cambiando de data source el dialecto que va a usar jBPM (en realidad hibernate) para comunicarse con la base de datos va a cambiar y por esto vamos a tener que cambiar las configuraciones de jBPM. Si estamos usando la Suite jBPM esto lo tenemos que hacer en:

server/jbpm/deploy/jbpm-console.war/WEB-INF/classes/hibernate.cfg.xml

como veran para esto (en la Suite jBPM) debemos descomprimir el archivo jbpm-console.war y convertirlo en un directorio (para hacer un exploded package), todo esto para editar ese archivo. En donde cambiamos la siguiente linea:

<!-- hibernate dialect -->
<property name="hibernate.dialect">org.hibernate.dialect.HSQLDialect</property>

Por:

<!-- hibernate dialect -->
<property name="hibernate.dialect">org.hibernate.dialect.PostgreSQLDialect</property>

5) No hay que olvidar que antes de hacer deploys de nuestros procesos tenemos que tener creado el schema propio de jBPM, cosa que se puede hacer utilizando los scripts de creación de schemas que vienen con la suite jBPM.

6) Una vez que tenemos todo esto hecho , podemor arrancar el JBoss AS con ./run.sh -c jbpm en el directorio bin del mismo. Y comenzar a deployar nuestros procesos en este nuevo Data Source!

Cualquier comentario sera bienvenido!

La idea de este post es tener una seria de afirmaciones basicas sobre jBPM que ayuden a aclarar ciertas dudas a la hora de implementar o modelar procesos con jBPM. La siguiente no es una lista estatica, ni pretende tener la verdad absoluta, sino que se ira modificando con la experiencia. Espero que sirva de algo, aca les va:

  • jBPM es un framework de desarrollo (recordemos que termina siendo un jar que contiene clases para conformar un BPMS  – Business Process Management System) y no un producto para usuarios finales. Por esto mismo no hay que caer en la mentira de que jBPM es una herramienta solo para analistas que dibujan procesos y estos ya funcionan.
  • Cuando usamos el framework jBPM no hay ninguna instancia de servidor de jBPM corriendo. Pensar de esta manera lleva a conceptos erroneos sobre el framework. En el caso de que estemos usando jBPM con persistencia, en este caso si tenemos una instancia de Base de Datos con un schema propio de jBPM corriendo, con la cual el framework jBPM va a mantener conversaciones stateless.
  • WebConsole es una aplicacion web que funciona out-of-the-box para probar los procesos que modelamos y ver si se comportan correctamente. Lamentablemente WebConsole no es una aplicacion que puede ser puesta en produccion debido a sus comportamientos erroneos, su falta de QA y su falta de seguridad (opinion personal y respaldada por lo que dicen los foros oficiales de jBPM)
  • GPD (Graphic Process Designer, plugin para eclipse) ayuda pero esconde varias cosas y deja afuera a muchos usuarios de otros IDEs que no sean eclipse. Mejor aprendamos a escribir XML.
  • Entre jbpm-jpdl.jar y jbpm-enterprise.ear, la diferencia es el empaquetado, las configuraciones (jbpm.cfg.xml, hibernate.cfg.xml) y algunas clases que van a ser fundamentales en entornos Java EE

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!

En este post vamos a analizar el Nodo tipo Node, que es el mas genérico de los nodos en jPDL.
Vamos a analizar el código de la clase node para encontrar las características mas importantes, con el fin de empezar a conocer el comportamiento del mismo y el comportamiento base para todos los demas tipos de nodos ya que todos extienden de el.

Vamos a empezar por lo primero. La definicion de la clase Node

public class Node extends GraphElement implements Parsable

Esto es importante ya que todas las subclases de Node estaran regidas por esta herencia. Si vemos la interfaz Parsable propone lo siguiente:

public interface Parsable {
  void read(Element element, JpdlXmlReader jpdlReader);
  void write(Element element);
}

Como podemos ver se definen dos metodos que Node va a tener que implementar read y write, estos metodos seran los encargados de saber leer jPDL y escribir jPDL en caso de ser necesario. Esto sirve para que el framework pueda leer jPDL(que es un xml) y pueda levantarlo a objetos y por otro lado que nosotros podamos dibujar con GPD (Graphic Process Designer) y de objetos pueda escribir el jPDL correspondiente

Y la clase de la cual extiende Node llamada GraphElement es la encargada de definir en si el comportamiento de los nodos en cuanto a Eventos, Excepciones, todos los metodos para poder navegar en la jerarquia de GraphElement (y asi obtener sus hijos y sus padres), y ultimo pero no menos importante nos da la implementacion del metodo executeAction, que es el metodo que se encarga de ejecutar la siguiente linea:

action.execute(executionContext);

Continuamos viendo la estructura principal de su metodo execute:

public void execute(ExecutionContext executionContext) {
    if (action!=null) {
      try {
           executeAction(action, executionContext);
      } catch ...
    } else {
      leave(executionContext);
    }
}

Como podemos ver, esta implementación base de el método execute nos lleva a la conclusión de que los nodos de tipos Node, no se comportan como wait states y continúan el flujo de ejecución si no especificamos ninguna acción dentro de la definición del nodo. En caso de que especifiquemos una acción, esta pasa a ser la responsable de continuar con la ejecucion del flujo del proceso. Por esta razón en la acción debemos decidir si el comportamiento de la acción es síncrono, el ActionHandler va a tener que llamar al metodo context.leaveNode() para continuar la ejecución del proceso. En el caso de que tengamos un comportamiento asíncrono, el ActionHandler ejecutara la lógica asignada y se quedara en un wait state esperando que alguien externo continué la ejecución del proceso.

Como veiamos en el metodo execute, si hemos asignado una accion, cuando se ejecute el metodo execute esta accion se ejecutara por consecuencia. Esta accion es de tipo Action, la cual implementa ActionHandler. Por lo tanto la clase delegada que nosotros tenemos que implementar y especificar explicitamente en jPDL debe implementar la interfaz ActionHandler.

Un ejemplo de jPDL del nodo tipo Node:

<node name="Node">
        <action name="Node Action " class="com.wordpress.salaboy.handlers.NodeActionHandler.java"></action>
        <transition to="next node"></transition>
</node>

Espero que esto haya servido como pantallaso del nodo Node, seguramente seguire agregando informacion sobre este nodo a medida que vaya considerando necesario.

La idea de este post y los siguientes a ser repasar brevemente la funcionalidad de los distintos tipos de nodos, tanto en su funcionalidad como en su sintaxis XML, con la idea de poseer una fuente de conocimiento sobre cada tipo de nodo. Este post especialmente esta dedicado al nodo de tipo <start-state> como indica el titulo.

<start-state>

Este nodo se caracteriza por ser el nodo inicial de nuestros procesos. Al ser el primer nodo tiene algunas características especiales para cumplir cumplir con su funcionalidad.

Las características mas obvias, pero que no hay que dejar de lado y siempre tener en cuenta son:

  • No posee transiciones que llegan a el
  • Solo puede haber un solo <start-state> por proceso

Con respecto a la primer característica podemos ver la implementación exacta de la clase StartState.java que extiende su funcionalidad de la clase Node (como absolutamente todos los nodos) y sobre escribe los siguientes dos métodos para cumplir esta funcionalidad:

public Transition addArrivingTransition(Transition t) {
    throw new UnsupportedOperationException( "illegal operation : its not possible to add a transition that is arriving in a start state" );
  }

  public void setArrivingTransitions(Map arrivingTransitions) {
    if ( (arrivingTransitions!=null)
         && (arrivingTransitions.size()>0)) {
      throw new UnsupportedOperationException( "illegal operation : its not possible to set a non-empty map in the arriving transitions of a start state" );
    }
  }

Otra característica que sale a la luz cuando vemos la implementación de la clase StartState, es que esta clase sobre escribe el método execute que contiene la logica a realizar por el nodo de la siguiente manera:

public void execute(ExecutionContext executionContext) {}

En otras palabras el nodo start-state no se ejecuta. Solo sirve para indicar que el proceso acaba de ser creado y todavía no comienza su ejecución. Esto quiere decir que cuando con la API nosotros creamos un nuevo Objeto ProcessInstance, se crea un RootToken que apunta al start-state del proceso.

A nivel jPDL podemos ver una definición simple de un nodo <start-state>

<start-state name="start">
  <transition to="next node"></transition>
</start-state>

o también una mas compleja que incluye la definición de una tarea dentro del nodo <start-state>

<start-state name="start">
        <task name="Start Task" priority="high">
            <assignment actor-id="salaboy"></assignment>
            <controller>
                <variable access="read,write" name="variable1" mapped-name="variable1"></variable>
            </controller>
        </task>
        <transition to="next node"></transition>
</start-state>

Con respecto a la tarea, ya voy a describir un poco mas en un post dedicado solo a eso. Pero lo importante aca, es el significado que puede tener esta tarea dentro del proceso. Por lo general se suele poner una tarea en el <start-state> con el fin de poder capturar quien fue el actor que inicio el proceso. Tambien probablemente lo mejor seria que posteara un ejemplo de implementacion de esto mas adelante.

Como ultimo detalle y no menor, esta bueno tener bien en claro que eventos soporta este nodo tan particular. Ya que el nodo <start-state> no tiene transiciones que llegan a el, los eventos como por ejemplo, node-enter en este caso no estan soportados. Por esto viendo en la implementacion, vemos que los unicos soportados son los siguientes:

public static final String[] supportedEventTypes = new String[]{
    Event.EVENTTYPE_NODE_LEAVE,
    Event.EVENTTYPE_AFTER_SIGNAL
};

Espero que se entienda y si alguien tiene algun comentario para agregar, bienvenido sea.

La idea de este post es presentar un ejemplo de proceso de negocio que nos clarifique la idea del modelado y de las situaciones que comunmente se modelan con jBPM. Para esto se presenta el siguiente proceso de negocio simplificado para el cual se tratara de explicar el comportamiento y la lógica relacionada con cada uno de sus nodos.

Extracción ATM: (traducido seria, retirar dinero de un cajero automático)

Extracción ATM

Extracción ATM

Como vemos en la imagen, en el proceso se describen varios nodos, de distintos tipos que nos permiten especificar un comportamiento particular para cada uno de ellos.

La primer gran diferencia, debido a la cantidad de nodos de tipo tarea y nodos de tipo node, es entre las actividades (o tareas) que son realizadas por personas, y las actividades que realizan sistemas automaticos o procedimientos programados (programas, software, etc).

Por lo tanto es fundamental identificar que actividades las realizan personas y que actividades las realizan sistemas. En este caso de dominio es bastante fácil identificar esta división, pero hay casos donde los dominios son complejos y el lenguaje de dominio también, donde un analista o el encargado de modelado puede llegar a confundirse. En estos casos, es importantísimo profundizar el relevamiento y obtener estos datos, para poder modelar correctamente el proceso.

Esta diferencia entre Tareas y Nodos es importante a nivel jBPM debido a que su comportamiento interno es muy distinto, y por lo tanto nuestro proceso a nivel global se comportara distinto si modelamos un nodo como Tarea o como Nodo (de tipo Node).

Para poder demarcar bien las diferencias de comportamiento veamos las siguientes definiciones simplificadas:

Nodos de Tipo Tarea: van a representar a un actor (persona, ser humano) interactuando con el proceso. Al tratarse de una persona y su interacción con el proceso, fácilmente salta a la vista que el comportamiento del nodo va a ser diferente debido a que una persona no se comporta como una llamada a un método (WebService, función, etc). Sino que el comportamiento y la interacción de la persona con el proceso se caracteriza por ser mucho mas asíncrona. Esto quiere decir, que el “proceso” deberá quedarse esperando (estado conocido como wait state) a que el actor interactúe con el proceso hasta el momento en que el mismo o algún procedimiento que el active cierre (termine formalmente) la tarea.

Nodos de Tipo Nodo: son los nodos mas genéricos, que nos permiten especificar cualquier tipo de lógica y comportamiento dentro de ellos. Nosotros podríamos crear dentro de estos nodos lógica asíncrona o síncrona, pero lo mas normal es que estos nodos sean utilizados para interactuar con lógica de carácter síncrono, como seria la ejecución de un método o la llamada a un Web Service.

Estos dos tipos de nodos, al ser tan distintos utilizan distintas “porciones” de las API que nos provee jBPM. El nodo tipo Tarea hace uso de una sección particular de las API que esta totalmente dedicada a la interacción y administración de todo lo relacionado con las tareas humanas. Muy probablemente escriba un post sobre esto en los siguientes días.

Por el lado del nodo del tipo Nodo, tenemos que tener en cuenta que nos deja especificarle una acción (action handler) que contiene código Java a ejecutar. Dentro de este Action Handler podemos hacer cualquier tipo de llamada a cualquier sistema externo, lo que causara que el proceso se mantenga en el nodo mientras este “código” se ejecuta.

Con respecto al nodo Tipo Decisión no creo que haga falta aclarar mucho, solamente que hay que recordar que esta decisión se puede tomar automáticamente mediante el uso de un Decisión Handler (codigo Java) o a partir de expresiones que evaluen los datos particulares del proceso y en base a eso se decida por donde continuar la ejecucion.

Entonces para ejemplificar un poco vamos a ver el proceso que veiamos en la imagen anterior.

Veamos particularmente el primer nodo (luego del nodo start – al cual le dedicare otro post), el nodo “Ingresar Tarjeta”, este nodo es de tipo Task (tarea) ya que es una actividad que va a realizar una persona cuando se encuentre frente al cajero. Cuando pasara esto? no sabemos. Lo que si sabemos es que en algún momento esto va a ocurrir y por eso el “motor” de procesos se encargo de crear una tarea, que tiene que ser llevada a cabo para que el proceso pueda continuar. Simplemente la lógica de esta tarea seria mostrarle una pantalla al usuario de que debe ingresar la tarjeta en la ranura correspondiente y cuando el usuario ingrese la tarjeta y el lector de la tarjeta recupere el numero y los datos correspondientes de la misma, la tarea finalice y continúe el flujo de ejecución del proceso al siguiente nodo.

En el siguiente nodo, llamado “Reconocer Tarjeta”, es de tipo Node y acá deberíamos tener escrito en Java (dentro de un Action Handler) la lógica para ir a una fuente de datos y recuperar todos los datos asociados a esa tarjeta. Esto seria ir a una base de datos directamente y buscar los datos del duenio de la tarjeta mediante el numero recuperado en la tarea anterior, o consumir un Web Service externo al proceso que realice esta funcionalidad.

Espero haber sido claro y haber dejado un poco marcada la diferencia entre estos dos grandes tipos de nodos. Estaría bueno que si alguien tiene alguna duda sobre algun otro nodo, o sobre que logica deberia llevar asociada algun nodo e particular, lo comente y discutamos sobre eso.

Saludos y espero que sirva de algo…

En este post vamos a hablar un poco sobre la estructura del empaquetado que nos provee JBoss de su framework jBPM. La idea principal es tener en claro lo que contiene esta suite, la cual tiene como objetivo tener todas las cosas necesarias para armar un entorno de desarrollo rápido para proyectos que usen jBPM.

Para encontrar las instrucciones de como instalar esta suite dirigirse al siguiente POST.

Empezamos por el directorio config dentro de nuestra instalación de la suite de jBPM:

Directorio Config

Dentro de este directorio vamos a encontrar dos archivos claves:

  • hibernate.cfg.xml: es el archivo que nos permite configurar la fuente de datos con la que va a hablar el framework para realizar todas las tareas de persistencia. Este archivo incluye también todos los mapeos de hibernate necesarios para que los objetos que manejara el framework puedan persistirse.
  • jbpm.cfg.xml: este archivo por lo general viene sin contenido ya que el contenido se saca de las configuraciones por defecto que se encuentran en: org/jbpm/default.jbpm.cfg.xml. Por lo tanto este archivo nos permite sobre escribir las configuraciones por defecto, dejandonos configurar que tipo y quien nos va a administrar las transacciones, quien va a ser responsable de la persistencia, quien va a ser responsable del logging, de la autenticacion, etc.

En el directorio db encontramos los siguiente:

Directorio db

En este directorio como vemos, encontramos todos los scripts de generación de esquemas de base de datos, que nos permitirán generar el esquema en cualquier base de datos soportada por el framework de persistencia Hibernate.

En el directorio deploy encontramos los siguientes archivos:

Directorio deploy

En este directorio encontramos distintas distribuciones de jbpm previamente compiladas y listas para su distribución. Seguramente en posts futuros voy a estar hablando del empaquetado jbpm-enterprise.ear y sobre el empaquetado jbpm-console.war. Ambos empaquetados cumplen con la tarea de empaquetar al framework juntos con configuraciones y características especiales, para correr en distintos entornos y con distintas aplicaciones.

En el directorio designer encontramos:

Directorio designer

En este directorio básicamente lo que encontramos es un directorio eclipse, que posee internamente la estructura de un plugin para el IDE eclipse, y la idea principal es que descomprimamos una distribución de eclipse sobre este directorio con el fin de tener un eclipse con los plugins de jBPM instalados. Si ustedes ya poseen un eclipse instalado y desean agregarle los plugins de jBPM deberán copiar el contenido de los directorios: plugins y features a su instalación de eclipse.

En el directorio server encontramos:

Directorio server

Este directorio es uno de los mas importantes ya que contiene un servidor JBoss 4.0.3 ya configurado con todo lo necesario para correr jbpm y además con jbpm-console.war ya deployado. Este servidor consta de una sola configuración llamada jbpm. Por lo tanto si queremos arrancarlo como a cualquier otra instalación de JBoss tendremos que desde el directorio bin/ ejecutar la siguiente linea:

./run.sh -c jbpm

En el directorio src encontramos:

Directorio src

Es importante tener en claro la estructura de este directorio ya que contiene tres directorios importantes a nivel de fuentes de componentes:

  • enterprise: este directorio contiene todas las clases extras que se empaquetan en jbpm-enterprise.ear. Por lo tanto si optamos por este empaquetado tenemos que tener en claro que clases extra contiene.
  • identity: este directorio contiene los fuentes del modulo de identidades propuesto. Este modulo de identidades, es un modulo simple que se propone con el fin de contar con una implementación simple de un esquema de usuarios y roles. Este modulo puede ser extendido modificando los fuentes en este directorio o directamente remplazado con otro esquema y otra implementación de identidades.
  • jpdl: este directorio contiene el core del framework, es decir todas las clases que implementan las funcionalidades de jBPM. Dentro de este directorio se encuentran las clases que implementan y exponen todas las APIs con las que nosotros vamos a interactuar.

En el directorio raiz encontramos los siguientes archivos sueltos:

Directorio raiz

Los archivos importantes acá son los dos jars:

  • jbpm-identity.jar: este jar contiene la implementación básica de identidades compilada y lista para ser usada como biblioteca en cualquier proyecto standalone o enterprise.
  • jbpm-jpdl.jar: de la misma manera este jar contiene toda la API compilada para que cualquier aplicación utilice jBPM.

Cambiando un poco el enfoque del post anterior voy a dedicarme a escribir recomendaciones y algunas sugerencias sobre como podemos utilizar las APIs de jBPM para realizar las funcionalidades mas comunes a la hora de utiliza este framework.

Para empezar con algo sencillo pero muy útil, recomiendo el uso de dos métodos muy cómodos cuando estamos trabajando en la etapa de desarrollo y necesitamos tener en claro que registros se crean dentro del schema de jBPM.

Con los siguientes métodos podemos realizar programaticamente la destrucción de todas las tablas del schema de jBPM y luego podemos crearlas de vuelta:

JbpmConfiguration conf=JbpmConfiguration.parseResource("/jbpm.cfg.xml");
//Destruimos el schema programaticamente
conf.dropSchema();
//Creamos nuevamente el schema programaticamente
conf.createSchema();

Con esto obtenemos como resultado el schema limpio para poder instanciar nuestros procesos.

Vale la pena aclarar que estos métodos (dropSchema y createSchema) actúan sobre el datasource configurado dentro del archivo hibernate.cfg.xml, el cual se levanta cuando creamos la configuración a partir del recurso jbpm.cfg.xml.

Otra aclaracion importante es que no podemos usar estos metodos si estamos trabajando con la distribucion Enterprise de jBPM, ya que cuando querramos ejecutar alguno de estos metodos dentro de una transaccion empresarial, el contenedor nos reclamara sobre que no podemos forzar un commit (el commit que se genera por crear o destruir las tablas de base de datos) dentro de una transaccion administrada por el contenedor.