随笔 - 77, 文章 - 4, 评论 - 86, 引用 - 0



The Process Virtual Machine


by Tom Baeyens and Miguel Valdes Faura
May 8th, 2007.

Translated by Landy(landy.lxy@gmail.com)



There are many process languages for Business Process Management (BPM), workflow and orchestration. For simplicity we'll refer to that collection as workflow for short. There are two aspects to workflow; the process modelling aspect and the software implementation aspect. The biggest problem in workflow technologies today is that they don't handle the dual nature of those technologies properly. The main goal of this paper is to fix exactly that problem.


The Process Virtual Machine does not define a process language. Instead it acknowledges some process language might be better suited for a certain situation then another and hence, there will be multiple process languages coexisting. The Process Virtual Machine will define a common model that can be shared between all the graph based execution languages. It also includes a strategy on how process constructs can be seen as software components. This will enable support for multiple process languages and also it will show much clearer how process technology fits right into software development projects.


Component technology


The reason for the fragmentation in process languages is that there are many environments and features that can be handled by workflow. Up to now, the main focus was to build 'the best' process language. There is no sign yet that process languages are converging to each other in some way or another. So the new thing about Process Virtual Machine is the idea that different environments are best served with dedicated process languages.


While every developer knows the relational model that underpins relational databases, such a conceptual model is absent for workflow. The Process Virtual Machine will fix this missing piece by defining a component model for process constructs. That way, complete process languages can be built on top of the Process Virtual Machine as a set of process construct implementations. Developers that know the Process Virtual Machine will much better understand the current process engines that are currently available. Furthermore, we expect that the Process Virtual Machine will be the basis for all next generation workflow engines.


More and more, software development will be done in a mix of many languages. Aspects like a domain model or gluecode that binds frameworks to the domain language are best expressed in a general purpose programming language like Java. But a shift is taking place away from general purpose programming languages and leverage more frameworks for specific aspects of software development. Typically, the frameworks come with a library and Domain Specific Language (DSL). Those frameworks and languages address certain parts of software development in a more natural and easier way. Examples are process engines with process languages, rules engines with rules languages, object relational mapping frameworks with the mapping metadata, parsers based on grammars, inversion of control (IoC) containers with an object wiring language and web frameworks with their configuration files that specify navigation.


Many of the Domain Specific Languages (DSL) that we see today are graph based execution languages. For all of those, the Process Virtual Machine is the common foundation that can be see as the masterpiece to federate DSL graph based languages by reducing maintenance, design and implementation cost.


So process languages will be pluggable on top of the Process Virtual Machine.


Embeddable workflow


Just a few workflow automation projects can be realized only by using process languages. The BPM Suites that preach the "no code" approach only target that kind of projects where everything must be modelled as a process. We think that workflow, BPM and orchestration can ease the implementation of some aspects of a software development project. And a software development project usually combines many aspects, only some of which could be modelled as processes.


In this view, workflow technology is much more broadly applicable then it is used today. Because today's process engines are not embeddable enough and because of the current fragmentation and confusion, developers still write homegrown workflow engines for their own project.


The concept of embeddable workflow means that a process engines can be easily integrated into todays software development projects. This is in contrast with the traditional monolithic BPM server approach.


In software development in general, there is a clear trend towards the use of more Domain Specific Languages (DSL). Embeddable workflow really fits with that trend: Process languages just become another language that developers can use in their projects. Workflow is complementary to old fashion plain programming. The developer should be free to select the language best suited for the job.


The following aspects are crucial to make the workflow engine embeddable:


·         Persistence: The workflow engine itself should be decoupled from the actual persistence technology. First of all, persistence itself should be optional and in case it is required, different persistence technologies like e.g. JPA, Java serialization or XML should be pluggable. In case a relational database is used for persistence, the developer should have a choice to deploy the workflow engine's database tables separate or alongside the application's database tables or in a separate database. The latter can make the whole application much more manageable.


·         Services: The services that an engine might use such as a timer service or an asynchronous message service should be pluggable so that different implementations can be used in different environments like e.g. stardard Java and enterprise Java.


·         Transactions: If the workflow engine's can work with the same database connection as the application, there is no need for global or distrubuted transactions. That definitely reduces complexity. Same reasoning applies to sessions of the object relational mapper like e.g. Hibernate.


·         Libraries: Apart from the traditional monolithic deployment of a workflow engine, it often can be much more practical to deploy just the engine's libraries in the client application.


·         Testability: Executable processes are software so they should be tested the same way. Tests should take process executions through different test scenarios. This should of course be integrated with the rest of the client application infrastructure.


·         Binding: It should be easy to bind the process logic to the rest of the software development project. As we'll see in section Process modelling, an executable process is always a combination of the graphical diagram and technical details. Up to now, the focus of tooling is on editing the graphical diagram. But software development projects usually contain many languages like e.g. Java, process language and scripting languages. So we expect that tools will start to focus more that coherence between different artifacts in one software project. For example, a refactoring for renaming a Java class might span updates to Java classes and process definition files.




We already explained above that the Process Virtual Machine is a common foundation for graph based execution languages. The languages for which the Process Virtual Machine can be leveraged have three main characteristics:


·         Processes are represented graphically to facilitate communication between all stakeholders


·         The process expresses some kind of execution flow


·         Processes can potentially include wait states from the perspective of the process engine and be 'long running'

从流程引擎方面来看,流程可以潜在的包含等待状态;流程也可以是long running的。

Any aspect in software development that meets these criteria can be built on top of the Process Virtual Machine. It's not limited to workflow or BPM as we know it today. An example of such a non trivial aspect might be pageflow; a language to describe navigation between pages in a web application.




Here follow the basic principles of the Process Virtual Machine. After the basics, we'll cover a series of extensions that are needed to cover all features needed for real process languages.


A process is a graphical description of an execution flow. For example the procedure on processing expense notes is a process. It can be deployed in a process engine. One process can have many executions. E.g. my expense note of last Monday could have been handled by one execution of the expense note process. Another example of a process is shown in Figure 1.


Figure 1: An example process for an insurance claim


The basic structure of a process is made up of nodes and transitions. Transitions have a sense of direction and hence a process forms a directed graph. Nodes can also have a set of nested nodes. Figure 2 shows how transitions and nodes can be modelled in a UML class diagram.


UML class diagram of nodes and transitions
Figure 2: UML class diagram of nodes, transitions and the behavior


Each node in the process has a piece of Java code associated as its behaviour. The interface to associate Java code with a node is shown in Figure 3.


public interface Executable {

 void execute(Execution execution) throws Exception;


Figure 3: The Executable interface for specifying the behaviour of nodes


Now, let's look at the runtime data structure. An execution is a pointer that keeps track of the current position in the process graph as indicated in Figure 4.


Figure 4: An execution points to the current position in the process graph


When a new execution is started for a given process, the initial node will be positioned in the initial node of the process. After that, the execution is waiting for an external trigger. The method and class structure of Execution's is indicated in Figure 5.


Figure 5: UML class diagram of an Execution


An external trigger can be given with the proceed(String transitionName) method on the execution. Such an external trigger is very similar to the signal operation in finite state machines. The execution knows how to interpret the process graph. By calling the proceed method, the execution will take the specified (or the default) transition and arrives in the destination node of the transition. Then, the execution will update its node pointer and invoke the node's behaviour.

外部触发可以由执行的proceed(String transitionName)方法来发起。这样的一个外部触发与有限状态机中的signal操作非常类似。执行知道如何解释流程图。通过调用proceed方法,执行将沿着指定的(或默认的)转移,到达转移的目的节点。然后,执行会更新它的节点指针和调用节点的行为。

The node's behaviour has got access to the current state of the process through the execution that is passed in as a parameter. In the extensions that are described in detail in the full paper show how for example variables or external services will be available through the execution.


On the other hand, the node's behaviour has got control over the propagation of execution. This means that the executable implementation can just behave as a wait state, continue execution, create concurrent executions or update any information in the execution.


Let's look at two example node behaviour implementations:


A task node


The reason why task management and workflow are so closely related is because tasks for humans often translate to wait states for a software system. Processes can easily combine software operations with human tasks in the following way.


The first thing that is needed outside of the process execution engine is a task repository, a place where tasks for people are kept. On top of this component, there is a user interface that allows for people to see their task list and complete them.


Then you can imagine the following behaviour implementation of a task node. First, some external trigger has to be given (with the proceed method) so that the process starts executing and arrives in the task node. The node behaviour implementation will create a new task for a given person in the task list component. That task also includes a reference back to the execution. Then, the node behaviour returns without propagating the execution. This means that the execution will be positioned in the task node when the proceed invocation returns.


public class TaskNode implements Executable {

 String taskName;

 public void execute(Execution execution) {

    // find the responsible person for this new task

    User assignedUser = calculateUser(taskName, execution);

    // crete the task

    Task task = new Task(taskName, assignedUser, execution);


    // add the task to the repository

    TaskRepository taskRepository = execution.getContext().getTaskRepository();




Figure 6: Pseudo code for a task node implementation


The taskName member field shows how configuration information that is specified in the process definition file, should be injected into the behaviour object.


So the execution can then be persisted during while the system is waiting for the user to complete the task. After some time, when the user completes the task, the task management component will use the reference to the execution to provide a trigger. This is done with the proceed method. Then the execution resumes, leaves the task node and continues.


An email node


An email node is different from a task node in the sense that it is automatic. An email has to be send and then the execution should immediately be propagated over the default leaving transitions. That propagation of execution is done with the invocation of the proceed method at the end of EmailNode's behaviour implementation in Figure 7.


public class EmailNode implements Executable {

 String recipient;

 String subject;

 String text;

 public void execute(Execution execution) {

    // send the email

    sendEmail(recipient, subject, text, execution);


    // propagate the execution




Figure 7: Pseudo code for an email node implementation


Similarly as with the task node, the member fields recipientsubject and text are specified in the process definition file. The engine will inject the values into the member fields. Those values might be the result of runtime evaluation of expressions and also mappings might be applied.


Now, let's discuss the main features of the basic model that we have presented so far.


Graphical representation


The basic Process Virtual Machine concepts show how graphical diagrams can be made executable. Processes are made up of nodes and transitions, hence they have a direct relation with the graphical representation. On the other hand, the behaviour member field in the Node class defines the programming logic that is executed at runtime.


The graphical structure of the concepts defined in Process Virtual Machine cover most constructs of process languages. Even advanced conctructs like e.g. UML super states can be mapped directly to the nestedNodes relation as defined in Figure 2. Still, process languages can extend the basic structure with new concepts such as e.g. timers, data flow or conditions.

流程虚拟机中定义的概念的图形化结构覆盖了流程语言的大部分元素。甚至高级元素,如UML超状态可以直接映射到图二中定义的nestedNodes关系。流程语言也可以使用新概念来扩展基本结构,如timersdata flowconditions

Wait states


We also saw in the task node example that nodes can represent wait states. When an execution arrives in a node and the execution.proceed() method is not invoked, the originally proceed, invoked by external client will return. That means that the execution is now in a wait state and typically it is then waiting for an external trigger. Typically, that is the right time to persist an execution.

我们在任务节点的例子中看到了节点可以表示等待状态。当执行到达一个节点时,不调用execution.proceed()方法, 由外部客户端调用的初始的执行方法会返回。这意味着执行当前处于等待状态,典型场景是执行等待一个外部触发。通常,这是持久化只执行的合适时间。

With wait states, processes can now express execution flows that spans software operations (typically in a transaction) and wait states when the engine must wait for an external trigger. This is also known as long running processes.


Synchronous execution


When a client invokes the proceed method, the exection will start to interpret and execute the process in that thread. An execution typically starts by taking a transition and then executing the destination node. When the execution invokes the behaviour of the destination node, it passes itself as the parameter. The node behaviour can then in turn propagate the execution onward by calling the proceed method on the execution again. Note that this is still done in the thread of the client, which is still waiting for the original proceed invocation to return.


In other words, the execution is always interpreted in the thread of the client. When the the execution is not propagated any more, the nested proceed invocations start returning since the proceed should be the last operation in the behaviour's execute method. The proceed invocation will be blocking for the caller until a wait state is reached.


The default behaviour is to have synchronous executions because it limits the number of transactions and hence improves performance. But in case there are many automatic things to be done before the process execution reaches another wait state, Asynchronous continuations have to be considered.




The bulk of the interpretation of the process is delegated to the behaviour implementations. The only responsibility of the execution is to follow the transition to it's destination node and update the node pointer accordingly. That way, the execution's node pointer will always indicate the current state.


This is important because it means that the bulk of the behaviour of the engine is pluggable. The essence of interpreting a graph is baked in the component model. But all the rest is left up to the process construct implementations. That is why it is possible to build such diverse process languages on top of the same framework.


Another way of describing the responsibilities of the node implementations is the following: First, they can use any Java API to perform any kind of business logic like sending emails, creating tasks, calling web services and so on. To do this, the implementations have access to the contextual information about the process like process variablesservices from the environmentand configuration information that gets injected into the member variables. After the business logic, node implementations can propagate the control flow of the process execution. The basic scenarios are wait states and propagation of the incoming execution over one a leaving transition. But in more exotic scenarios, the node implementation can reorganise the complete runtime execution datastructure as well.

下面是描述节点实现的职责的另一种方式:首先,能够使用任意Java API来执行任意业务逻辑,如发电子邮件,创建任务,调用web服务等等。要实现这样,实现要求能够访问流程的上下文信息如流程变量,环境的服务和注入成员变量的配置信息。业务逻辑执行完之后,节点实现可以传播流程执行的控制流。基本的场景是等待状态和根据一个离开转移传播执行。但是,在更加特定的场景中,节点实现也可改变整个运行时执行数据结构。



The previous sections explained the basic operations of the Process Virtual Machine. In the next sections we'll describe a set of extensions to this basic model to show how advanced features can be build on top.




Process variables can contain contextual information associated with a single execution. For example, in a payraise process, the desired amount and the reason could be process variables.


Process variables can be added to the basic Process Virtual Machine by associating a set of key value pairs within an execution.


Processes or nodes can have variable declarations. Some process languages mandate variable declarations and others don't. So that is why the key value pairs should be dynamic, just like a HashMap in Java. The variable declarations could be used at runtime to initialize, destroy or to check for avaiability of a certain variable.


Process variables should be Plain Old Java Objects (POJO) because the component programming model is in Java. Process languages like BPEL only store xsd types or XML snippets. In that case, the process language itself is responsible of translating those datatypes into the desired POJO type.


Persistence is optional, so in general, it should be possible to store any POJO object in the process variables. Only when an execution is stored, transformations should be applied to map the POJO's to their persistable format.


The section about Concurrent paths of execution will introduce a tree of executions. That tree also defines a natural scoping structure for process variables.




An action is the crucial concept that will enable modelling freedom for the analyst, while the developer has to make the process executable.


An action is a piece of programming logic that is inserted invisibly in the process graph. For example, perform a certain database update when leaving this node. Or, delete a certain file when this transition is taken. Actions can also be described as listeners to process events.


The Executable interface that is shown above can be used to refer to actions.


An event is a point in the process where actions can be specified. The three most common events are entering a node, leaving a node and taking a transition. As an implementation detail, we mention that the Execution class can be enhanced with a fire method in the API. That way, clients, actions and node behaviour implementations can all fire events. Despite the goal of being hidden from the graphical diagram, in Figure 8, we indicate how the actions are related to the diagram just to serve this explanation.


Figure 8: Most common events


But it is good to use the combination of a process element and a plain event type string to identify an event. That way, much more event types can be defined and even users could define their own event type.


Events can also be propagated. The process can have a hierarchical structure with nested nodes. If a process language implements a construct like e.g. UML super states, events on a nested node can be propagated to the enclosing superstate. That propagation can continue recursively until the level of the process defintion itself. So super states or processes might listen to all events of a certain type that are fired inside. For example, invoke this action for every transition that is taken within this superstate.

事件也可以传播。流程可以带有内嵌的节点,组成分层结构。如果流程语言实现了一个构造如UML state,内嵌的节点上的事件可以传播到封闭的超状态。传播可以递归进行,直到流程定义级别为止。所以,超状态或流程可能监听所有触发的指定类型的事件。例如,为超状态中的每一次“获取转移”调用动作。

Concurrent paths of execution


One path of execution can point to one single location in the process graph. In some situations, there parts in the process that can progress independently. For instance, the shipping and billing part of a process. In those scenarios, multiple paths of execution are needed to keep track of the state for one complete process execution. To support this, the executions can be structured in a parent-child relation. A new execution for a given process acts as the main path of execution. To create concurrent paths of execution, child executions can be created. Typically, the parent execution will remain inactive in the fork (aka and-split) while the child executions are executing.


Several fork and join behaviours can be implemented in as node behaviours. The fork typically launches a new execution over each of its leaving transitions. Optionally, guard conditions might be added to prevent some of the concurrent executions from being created.


Note that process concurrency mostly doesn't require multithreaded calculations. Assume for a second that no actions are involved and that all nodes right after the fork (aka and split) behave as wait states, like illustrated in figure 9. In that case, one database transaction should contain the operations to inactivate the parent execution and to add the two new child executions each referencing their respective wait state.

注意流程并行通常不需要多线程执行。假定某一时刻没有执行任何动作,且fork(或and split)后面的所有节点都是等待状态,就像图9中的例子。在这种情况下,一个数据事务应该包含去激活父执行和增加两个新的子执行的操作。每个子执行应该引用各自的等待状态。

Figure 9: Concurrent paths of execution


Calculating the updates for that single transaction doesn't need to be done in two separate threads. In fact, those threads would have to be synchronized anyway because they operate on the same database transaction.


The main difference is in the join behaviour. We'll describe two examples of join implementations just to illustrate that any type of join behaviour can be implemented. The Process Virtual Machine itself doesn't include a fixed mechanism for process concurrency. Instead, the different forms of concurrency can just be implemented just like any other process construct.


As long as all paths leaving a fork arrive at the same join and no other paths can arrive at that join, a simple join implementation can be build that takes advantage of the hierarchical structure of the executions: When an execution arrives in a join, it first marks the incoming execution as deactivated and then checks if there are any active siblings left. If there are no active siblings left, the parent execution is reactivated, positioned in the fork and that execution is propagated over the default leaving transition.


Another approach is to count the number of executions that have arrived in a join. If that number just got the same as the number of arriving transitions, a new execution will be created, positioned in the join and propagated over the default transition. In this case the complete execution tree needs to be pruned for inactive executions after a join has been triggered.


This type of join implementation can handle unstructured combinations of forks and joins. I leave it up to the imagination of the reader to find the differences with the previously explained join behaviour. The most important point is that different types of fork and join behaviours can be implemented on top of the Process Virtual Machine.


Process composition


Process composition means that a node in one process can reference another process. When an execution arrives in such a process node, that execution will wait in that node until a complete execution of the referenced process is completed.


Figure 10: Process composition


This can be added to the process virtual machine by leveraging the hierarchical relation of the execution tree that was created for concurrent paths of execution. When an execution arrives in a process node, a child execution can be created for the referenced process like illustrated in Figure 10.


A check needs to be added when executions complete. In this case, executions that are completed need to check whether there is a parent execution available. If that is the case, that execution need to be propagated. The parent execution, one that originally arrived in the process node, will then leave that process node and continue over the default transition.


Asynchronous continuations


Up to now, all propagation of the execution was done synchronously. In some situations that might be problematic. For example suppose a process where a large pdf file has to be generated automatically after a user has completed a task. And most likely, the invocation ofexecution.proceed() will be done in the request-response cycle of the web application for the user. In that case, the web response is unnecessarily delayed with the generation of the pdf because that is done inside of the proceed method invocation. In general, this means that the client that is calling the Execution.proceed will be blocked until the process has reached a new wait state.


The Process Virtual Machine can be extended to handle these situations by using an asynchronous message queue. To see how that works, we first must define what the atomic operations are. Atomic operations cannot be interrupted. Here, we'll define two atomic operations: Executing a node and taking a transition. So execution of a node and of a transition cannot be interrupted. But in between those atomic operations, a thread can decide to stop the interpretation and instruct another thread to continue the interpretation asynchronously from that point forward.


In the node and transition elements, an extra configuration flag can be added. Let's call it the asynchronous flag. When a node has the asynchronous flag set, it will be executed asynchronously at runtime. Similar for transitions.


Remember that the execution is responsible for interpreting the graph, finding the destination node of a transition and executing the behaviour of that node. Now, when an execution arrives in an asynchronous node, the node will not be executed. But instead, a message is being sent over an asynchronous messaging system. The message contains a reference to the execution and the instruction to execute that node. After that, the Execution.proceed returns and the transaction, that contains both the process updates and the production of the asynchronous message, can be committed.


At the other end of the queue, there is a component called the job executor. That component will start a new transaction, fetch a message from the queue and perform the instructions in the message. In our scenario, this means that the node will be executed. When that is done, the transaction will be committed. That transaction included consumption of the message and the new process updates.


Figure 11: Asynchronous continuations


This can also be seen as a kind of transaction demarcation. Because by default the interpretation of the graph will continue until a wait state is reached. But with asynchronous continuations, it's possible to mark a position in the process where the current transaction should be committed and a new transaction should be started automatically.


The tradeoff for asynchronous continuations is exception handling versus response times. We already saw in the beginning of this section that response time can be significantly reduced by introducing asynchronous continuations. But on the other hand, the downside can be that the original caller is not aware of any problems that might arise further in the process. For example, a task node is followed by an email notification node and suppose that it is crucial that this notification is sent. When the email notification node is marked as asynchronous, the task completion will be faster, but that user might not get a notification when the mail server is down.


In case an exception occurs in the job executor, an automatic retry mechanism could be used. Most message queueing systems have a configurable number of retries before the message is sent to the dead letter queue. Administrators could monitor that dead letter queue or a timer could be added to notify the stakeholders of the process execution in case of asynchronous failure.


Process updates


A set of process updates describe modifications to a particular process. For example additions and removals of nodes, transitions or actions.


With process updates, the Process Virtual Machine can be extended for two new use cases: Process inheritence and per instance process customizations.


Suppose a situation where for each country there is a process on how banks must do efforts to detect suspecious stock transactions. And suppose that most that the basic layout of all those processes would be the same. There are only some minor details, that are different. In that case, it might be an option to describe a single default process and then define a kind of process inheritence. A specialized process might be defined as a reference to another process plus a set of updates. Of course, the execution's interpretation algorithm have to be modified to take these process modifications into account.


The second use case is updates per execution. Suppose that a user wants to monitor all progress for a specific execution. In that case, he could add an action on a process definition for the transition event. That means that the notification action will be executed for each transition that is taken in the whole process.


Another example of updates per execution is removing transitions for a given execution. Suppose that in the process, there is a distinct handling for urgent packets from the handling of normal packets. Now imagine that early in the process, a user can recognizes a client that always tries to get the fast delivery while he's not entitled to it. Then this might be implemented with process updates by letting the user remove the transition for urgent handling.


Figure 12: Process update for a single execution




While a process is being executed, history logs can be generated. Those history logs can contain information about when transitions were taken, when nodes are entered and when nodes are completed. Also the process variable updates and tasks can produce logs. Basically, the whole execution of a process can be logged.

One way to add configurability and flexibility is to define a loging service. The execution and the nodes can then generate log objects in the form of Java objects and pass them to the logging service. That way, log filtering and transformations can be applied centrally. Also different forms of storage can be configured in the logging service.



The most important use case for history logging is explained in more depth in section Business intelligence (BI).


If the logs keep complete track of all the updates that are done in a process, the logs can be used to generate compensating transactions. A compensating transaction means that a new transaction will undo the effects of previous transactions. A technique to do this could be to walk through the logs in reverse order and restore the begin state as indicated in the log entries.




Up to now, the Process Virtual Machine was explained in terms of objects, without any reference to persistence. This is possible because of Object Relational Mapping (ORM) technologies such asHibernate. ORM can translate between objects and database records. On the Java platform the database is accessed using JDBC. Object relational mappers can also make abstraction of the differences in the SQL dialects of databases.


The algorithms we have defined for process interpretation never use the database directly. Persistence of processes and executions can therefor be offered as a separate service. That way, process languages that don't need persistence, like e.g. pageflow are not required to use a database.


The data in a workflow database can be split into three main compartiments like illustrated in Figure 13: Static process definition information, runtime execution information and history logs.


Figure 13: The three parts of a workflow database


The object relational mapper will take a complete process object graph and translate that into records in the database. That way, the complete process structure can stored in the database. For example, the node objects in the process object graph will be inserted as records in the nodes table. Since process definition information normally doesn't change, it can be cached in memory. In jBPM's case that is done using the second level cache of Hibernate.


The runtime process execution information is typically updated in every transaction that includes a workflow operation because the execution's node pointer will have moved to a next node. In Figure 14, you can see the scenario of the workflow persistence operations in one transaction.


In this scenario, the starting situation is that an execution was already persisted in the database and now an external client wants to provide an external trigger to resume the process execution. So the execution record in the database has a foreign key column that references the node in the nodes table. To get started, the client needs the id of the execution.


In the first step, the client start a transaction and loads the execution object using the object relational mapper using the id.


Figure 14: The persistence scenario of a runtime workflow transaction


In the second step, the client will invoke some methods on the execution object. Typically this will be the proceed-method. In general, methods will be invoked on the runtime data structure. After those method invocations, the execution is pointing to a new node. This second step was executed without any consideration of persistence.


In the third step, the changes made to the execution object will be saved in the database. It's the task of the object relational mapper at this stage to analyse the execution java object structure and compare it with the data that was originally loaded from the database. The object relational mapper will then issue the nessecary insert, update and delete SQL statements to bring the database in sync with the execution object graph.


In the case where an execution just moves to the another node, the result will be a single update statement where only the foreign key column of the execution will have to be changed to the id of the node to which the execution is pointing after the method invocation.

Then, the transaction can be committed.


Another aspect of object relational mapping solutions that deserves attention in this context is optimistic locking. ORM solutions like Hibernate have a built-in mechanism for optimistic concurrency control. A version column is added to the database and each time an object is updated, the version number will be increased as well. But with each update, in the where clause an extra condition is added that specifies the version of the object that was originally loaded. If execution of that SQL statement returns that zero rows were updated, that transaction knows it was working with stale data and the transaction should be rolled back. For more about this, seethe hibernate reference manual.


But the result of all this optimistic locking is that process engines based on the Process Virtual Machine can scale by synchronizing on the database, using this light weight concurreny control mechanism. As long a they all are working on the same database and use this optimistic concurrency, all process updates comming from multiple systems will be synchronized.




To add timers to the Process Virtual Machine, a timer service is required that can schedule timers to be executed in the future. Timers must have the ability to contain some contextual information and reference the program logic that needs to be executed when the timer expires.


Let's look at the typical scenario where a wait state needs to be monitored with a timer. For example, if a user task is not completed within 2 days, notify the manager.


To implement this, the timer service as described above can addressed from actions on the node-enter and node-leave events as indicated in Figure 15. When the node enters, a timer is created with a given due date and it is scheduled with the timer service. Then the node will behave as a wait state until an external trigger is given.


Figure 15: A node with a timer


Suppose now that an external trigger is given before the due date of the timer. Then the execution will leave the node and the cancellation action will be executed. This action will cancel the timer that was created upon entering the node.

If on the other hand, the external trigger is not given, the execution will still be positioned in the node when the timer fires. In that case, the programming logic of the timer is executed. In our example that was the notification to the manager.





In this section about services, we'll discuss an implementation aspect of the Process Virtual Machine that is crucial for embeddabity of the engine inside of the client application.

Whenever the execution or the node behaviour implementation needs some kind of service, always consider using a self made interface. Examples of such services are the asynchronous message service and the timer service as discussed above. That way, different implementations can be provided in different environments. For example, in a Java enterprise environment, the asynchronous message service will probably need to be tied to JMS. Whereas in a standard environment this might be done using an in-memory queue.



A simple way to make all external services available to the execution and all the node behaviour implementations is to add a context property to the execution. The context can manage a set of named services and objects. The client knows in which environment it is running, so it constructs the context object and injects it in the execution before a method is invoked on it.


This mechanism can be given an extra dimension when an Inversion of Control (IoC) container is used for the context. In that case, transactional services can be lazy created on demand during execution of a process. The typical XML based configuration files of the IoC containers can be leveraged to specify which implementations for the services need to be used.




In this section we explain the essential features of process languages and how they relate to the Process Virtual Machine. Where the PVM basics and extensions explains a strategy on implementing a common foundation for process engines, this section describes more the essential features of BPM Systems and how the Process Virtual Machine relates to those features.


Process modeling


First we need to distinct between two categories of processes: descriptive process models and executable processes.

The purpose of descriptive process models is to describe business processes to other people. They support various kinds of notations like BPMN, Event-driven Process Chains (EPC) or UML acitivty diagrams. The main goal of these languages is to define precise semantics of the notational elements, resulting in an expressive graphical language. The modeller can use the graphical constructs in great freedom for the purpose of communicating the process to the reader. Apart from the expressive notation, they may also provide value in managing large sets of interrelated diagrams and models. These processes are not executable on a runtime environment.



The second catogory aims to make process models executable on a runtime environment aka process engine. In this case, executable process models are in fact software artifacts that specify the behaviour of a computer system. In that respect executable processes are exactly the same as a programs in a Object Oriented (OO) programming language like e.g. Java, even while the format and language are completely different. So in this case, the executable process as a whole is not so free any more. Because it has to specify a particular desired behaviour of a software system.


The Process Virtual Machine defines a model for the implementation of process engines. As for any process that is executable on a process engine, processes for the Process Virtual Machine are a combination of a graphical process elements and related technical details. So the graphical diagram of an executional process can be seen as a kind of projection that excludes those technical details.

The big value of this combination is that the process diagram can serve as a common language between all stakeholders of a process, regardless of their technical skills. This is shown in Figure 16.



Figure 16: Technical details of a process


Business analyst have vary different technical skills. Some have none. Some have a little bit of technical skills from a previous life as a developer and other people might actually combine the roles of developer and business analyst. This translates into Figure 16 as the dotted line that can shift up or down.


To translate that into the concepts of the Process Virtual Machine, there are three levels of detail.


1.       Process structure: These are the nodes and transitions. The graphical structure is the highest level that can always serve as a communication starting point. Even for technical illiterates.


2.      Node type: In process engines, the node type defines an exact runtime behaviour. In the Process Virtual Machine this is corresponds to the node behaviour implementations. That is the second level of detail. This typically also corresponds to the process constructs available in the pallette of the graphical editor. For example, a task node, a decision a web service invocation node and so on.


3.      Configurations: The finest level of detail is the configurations that customize the process construct for a particular use of the node type. In terms of the Process Virtual Machine, this corresponds to the member field values that are injected into the Executable behaviour implementation objects. In graphical editors, usually a form pops up when selecting a node in the diagram to enter those configuration details. Examples of configurations are the URL for a web service invocation, the role or swimlane for a task assignment, the condition for a decision and so on.


On top of these three traditional levels, the Process Virtual Machine has two features that we want to highlight that still give a great deal of modelling freedom to the business analyst, even for executable business processes.

First of all, if the desired behaviour of a node in a process doesn't match with any of the available process constructs in a process language, a developer is always free do write program logic to implement custom behaviour in the nodes. Secondly, in case the developer needs to add a piece of programming logic to make the process executable and this is of no interest to the business analyst, an action can be used to inject this piece program logic without a change in the graphical representation of the diagram. This provides an extra level of separation between the graphical representation and the technical execution details, keeping as much modelling freedom for the business analyst.



Business Intelligence (BI)


Business intelligence is about extracting information from the history of process executions that is useful for business people. As described above in History, a lot of information can be captured from process executions. Each time an external trigger is given or when a transition is taken, this can be logged in the history database.


Now the nice thing is that from those history logs, meaningful business level information can be captured. For example, "How long does each step in the process take on average?" or "What percentage of priority claims are handled within two weeks?" It is not a coincidence that these meaningful questions can be easily answered by the workflow engine's history logs because the graphical diagram was built in collaboration with business people.

There are many ways on how that historic information can be logged and processed. While outside of the scope of the Process Virtual Machine, we still want to highlight the typical processing and usage of historic infomration. Often process engines log every event in a kind of flat list style during runtime. Then, at some point (e.g. when the process execution finishes) the flat list of logs is processed and transformed in a separate database schema that is optimized for querying.



Workflow engines usually record these history logs by default. Many statistics typically come out of the box and it is very easy to define new meaningfull queries on this business intelligence database. While with old fashion plain programming, this will take a lot more time and make the overall project more complex. So when in doubt about using a workflow process or plain programming, consider wether this kind of information is important for your application.


Task management


Task management means that all people know exactly what they should do and when no tasks are done twice. Therefor, the right information needs to be provided to the right people at the right time. The benefits of proper task management are outside the scope of this paper. The goal of this section is to give you an idea of what task management is and how it relates to the Process Virtual Machine.

A task management component maintains a set of tasks. Task management features include direct (push) and group (pull) assignment, task forms, reassignments, notifications, and so on. The task list for a given user is typically made available through a web application and an API. The combination of task notifications and the related preferences that can be customized by users is also known as 'user awareness'.



Now, we'll zoom in to the relation between the Process Virtual Machine and a task management component. A process contains a mix of tasks for people and automatic activities. Looking from the perspective of the workflow engine, a user task represent a wait state. As we already described above, support for wait states is exactly one of the most important features of the Process Virtual Machine and workflow in general. So that is why process technologies and task management go hand in hand.

The most common scenario is when a node in the process corresponds to a single user task. In that case, a task node behaviour can be implemented as follows. When an Execution arrives in a task node, it can add task entry to the task management component. Then, the execution should wait in the task node. The new task will now appear in the assigned user's task list. As an integration between the workflow engine and the task management component, the task must keep a reference to the execution.



After some time, the assigned user might select that task from the task list and complete it with the user interface or te API. When a task with a related execution is completed, the task management component is responsible to invoke the execution's proceed. So completing the task triggers the execution to resume its execution.

Notifications can be built on top of Process Virtual Machine events with actions. Tasks can propagate their events like e.g. assign, duedate expired or completed to the execution. Once that integration is in place, listeners to those events can be specified as actions in the process definition or dynamically added to the execution at runtime. The dynamic subscription to task events is a crucial feature for user awareness.



This concludes the basic integration between task management and process execution. The many variations to this theme, like e.g. dynamic task creation, swimlanes and others can be handled by the pvm but they don't really contribute to the purpose of this article.


Cooperative applications and Human Interaction Management (HIM)


From a workflow perspective, an expense notes process is completely different from building a new leasure palm-tree resort with those nice red coctails and waitresses in miniscule biki... Anyway, the difference that we want to highlight is that an expense note process is completely known and all possible scenarios can be modelled upfront. Whereas for a leasure resort, there will be no fixed process at the start. That kind of processes will be defined on the fly. The category of software systems that support those kind of ad hoc processes is called cooperative applications or Human Interaction Management (HIM).


The Process Virtual Machine as described above suggests that all processes are as predictable as submitting an expense note. That is because a distinction is made between the process model --with Node's and Transitions-- on the one hand and the runtime execution structure --with theExecution-- on the other hand. Let's call this the static approach, reflecting the static process definition.

As a side note, the more frequent a process is executed, the more predictible all the scenarios will be and the more compelling it will be to automate that process. So that is why we took the approach with a single static definitions having many executions as the default one.



Cooperative processes can also be implemented easily on top of the process virtual machine. An execution is created before any process nodes and transitions are defined. Then typically people, roles and task nodes are created on the fly while the process is executing. So each process execution will create its own process model, whereas in the static approach the single static process model will be used for many executions.


From an implementation perspective, storing, updating and removing parts of the process model is done similarly as with executional data. The only issue to keep in mind is that process definition caching needs to be turned off in such use cases.

The nice thing is that with the Process Virtual Machine its possible to build processes that mix the static and the cooperative approach. As explained in "Process updates", a basic fixed model can be defined and updates to that model can be associated to the Execution.



Asynchronous architectures


Asycnrhonous architectures are another environment where workflow technology can be very useful. In such architectures, multiple machines or components communicate through queues or other forms of asynchronous messaging.

If you look at one component in such an architecture, it will receive many messages (or service invocations) that are related to one overall execution flow. That overall execution flow can be modelled and implemented as an executable process. The period between the response sent by the component and the next related message that is expected by the component translates to a wait state in the process.

This principle applies to various environments. For example a web services environment like e.g. an Enterprise Service Bus (ESB). In that case BPEL is the most appropriate language. In essence, with BPEL, new web services can be scripted as a function of other web services. But other languages like e.g. jPDL can be very convenient to orchestrate a set of Message Driven Beans (MDB) in a Java enterprise environment.




Process languages

This section describes the process languages for which we have proven that they can be build on top of the Process Virtual Machine.


BPEL is a standardized service orchestration language. If you can excuse us for the overly simplified statement, we describe BPEL as an executable language to script web services as a function of other web services.

BPEL is a language that fits right on top of an Enterprise Service Bus (ESB), which is a piece of integration infrastructure. Because an ESB targets integration, services are typically described in WSDL and based on XML technologies. That is where BPEL fits with ESB's. BPEL is also based on WSDL and XML technologies.


XPDL is a standardized BPM process language. The background of BPM Systems (BPMS) and hence of XPDL is very different from BPEL. XPDL processes describe a combination of user tasks and automated activities. All references to resources, automatic activities and applications are adressed indirectly, meaning there is no implicit assumption of a technological environment such as enterprise Java or an ESB.

XPDL 2.0 defines a complete mapping with BPMN. BPMN standardizes a graphical notation that defines the shapes, icons and decorations of process models.


jPDL is a process language of the JBoss jBPM platform. This language is in essense the simplest wrapper around the Process Virtual Machine, to make it available in a Java environment. jPDL processes can reference Plain Old Java Objects (POJO) directly. The process variables and API's are based on standard Java too.

SEAM pageflow

This is a language that describes the navigation between web pages of a SEAM application graphically. Nodes in the diagram represent pages and transitions represent the navigation between the pages.

The nice thing about the SEAM pageflow language is that it really shows the diversity of languages that can be built on top of the Process Virtual Machine. For instance, pageflow doesn't need persistence nor transaction while pageflow executions need to be serialized in the HTTP session.


First of all, this paper has outlined the essential principles of workflow, BPM and orchestration. This is already very crucial knowledge for understanding today's workflow and BPM systems. But the bigger goals of this article is to facilitate a big step forward in resolving the current fragmentation and confusion around workflow technologies. Both developers and workflow tool vendors will benefit significantly from a unified model for workflow.

Secondly, a component model was introduced that shows how a base framework can be build in a programming language that allows for multiple process languages to be developed on top. This served as a clear illustration for developers to indicate what exactly a workflow engine does and when this technology is appropriate. The bigger goal of this component model is also targetted at workflow tool vendors. Both jBPM in collaboration with Bonita and Orchestra, and Microsoft independently developed a very similar component model. We believe that engines based on this component model will be much more broadly applicable because of their support for multiple process languages in multiple environments, whereas most of the current workflow systems only cover a very small niche of use cases.

As a third item, a realistic and practical approach is detailed that acknowledges and copes with the dual nature of Business Process Management (BPM). Non technically skilled business people are focussed on the graphical diagram. But we have shown that an executable business process always has a graphical and a technical part. The graphical diagram serves as the common language between the business analyst and the developer.


·         JBoss jBPM

·         Bonita The XPDL workflow engine by Bull hosted at OW2

·         jPDL The workflow for Java process language of JBoss jBPM

·         JBoss SEAM Pageflow The process language build on jBPM for specifying navigation between web pages

·         Orchestra The BPEL engine from Bull hosted at OW2

·         JBoss jBPM BPELThe BPEL engine from JBoss build on top of jBPM

·         Windows Workflow Foundation

·         XPDL The workflow language defined by the WfMC and supported by Bonita

·         BPELThe service orchestration language defined by OASIS

·         (TODO) The reference to the summary article at OnJava

About the authors

Tom Baeyens

Tom Baeyens is the founder and lead developer of JBoss jBPM, the open source platform for workflow, BPM and orchestration engines. His mission is to bring the value of BPM technology to the developer community. Tom is frequent speaker on this subject at international conferences working for Red Hat. He's also participating in the Java Community Process. Tom blog is calledProcess Developments and can be found at http://processdevelopments.blogspot.com/.

Miguel Valdes Faura

Miguel Valdes Faura is the Workflow Project Manager working for Bull R&D. He is also member of the OW2 Technical Council in which he is leading the Bonita workflow project. Before that he has worked in Spain in different European projects based on J2EE platform and Open Source application servers. He joined INRIA, the French Research Institute in Computer Sciences, on February 2001 co-founding the Bonita Workflow System. He is a regular speaker at international conferences : JavaOne, Internet Global Congress, Open Source World Conference, javaHispano Conference, ObjectWebCon, COSGov, JavaBin...

posted on 2009-05-30 10:48 迷途书童 阅读(2244) 评论(1)  编辑  收藏 所属分类: 系统设计BPM


# re: 流程虚拟机  回复  更多评论   

2009-05-30 10:54 | 独孤过客