Page-Flow vs. Process-Flow – how a UI Mediator might help
April 04 2012 by Bernd Rücker · 7 Comments
How the user perceives it
The following BPMN shows how the user takes part in the process. Please note, that the whole blog uses the BPMN methodology we explain in our book on BPMN (unfortunately only German or Spanish at the moment) or in this blog post.
Note that there are basically two different possible flows for the user, either a score is provided immediately and he can do the whole work at once or the score is provided later and he has to come back later, query his task list and do the last step in the process model from there. To make this more clear, I visualized the flows by adding screen-shots of the application I developed as a prototype:
The automated process for the engine
As I said earlier, we have an automated business process for implementing this application. Using a process engine for this task comes handy for two features we can leverage in this case: First we need some persistence if the score is not returned immediately and second we want to add some escalation if the answer is not in time. With an BPMN 2.0 Engine like Activiti, we just have to add an attached boundary timer event to really implement an escalation, awesome.
The following image shows the process model as I deploy it later to Activiti, basically three things are remarkable:
- The number of UserTasks does not match the number of forms the user sees. This is not uncommon since the granularity of the user forms is basically dependent on the technology, on your smartphone you might have more forms than in a normal browser on your wide screen at work.
- The form we use to enter the customer data in the beginning does not correspond to an user task. This form is filled before the process instance is started at all (if the user cancels here, no process instance will be started at all).
- We do user validation within the process engine and have a first user task for entering additional data. This means, that if the users stops here and closes his browser, the process instance will remain in the Activiti database and he can continue later from the task list. I decided to model it like this in the demo, it is not the only possibility, agreed, there would be alternatives, but I consider it not to be unrealistic to first have a “safe point” after validation in this case.
How do we get this together?
In order to understand the big picture we need to have a look at the JSF user interface I developed for our small use case, which is included in the following process model. You can see that for example the customer details are “just” two forms which are implemented by a simple page-flow in JSF (page-flows are built-in). But for the other “flow logic” we need to call the process engine.
But how do we get the synchronous one page-flow feeling for the user for this?
UI-Mediator-Pattern
The UI-Mediator-Pattern basically described for a asynchronous SOA world how to map an asynchronous behavior under the hoods to a synchronous feeling on the user interface. That’s exactly what we want. And since Activiti normally behaves synchronously in terms of threading anyway (for details see the Activiti user guide) it is easy to implement in this environment. This mediator is a generic component we add to our application, which checks after a user interacts with the process engine if some task exists for the same process instance for the current user, depending on the configuration you might even wait a couple of seconds if a task will be created. If yes, the task form it is shown immediately to the user simulating a synchronous page-flow. This can now be easily mixed with real page-flow in JSF.Done
The demo application
Cool, but how to implement it? Actually it doesn’t take more than the camunda fox platform community edition, which includes a JBoss AS 7, and an evening of time. The whole application can be checked out from our public SVN: https://svn.camunda.com/fox/demo/fox/customer-registration-ui-mediator/trunk/. You just have to build it using Maven and deploy the resulting war on the fox platform and it should work.
Important addition (2012-April-28): I adjusted the code in the showcase to use JMS for the asynchronous response. That works pretty well and shows how to correlate an incoming JMS message to the right process instance. In order to get this running you have to enable the JBoss full profile to get it working! Here you find the standalone.xml you need, just replace the existing one in your JBoss 7 (or you build that yourself, in our fox user guide we have documentation for this, unfortunately this is currently not open for public but only for our enterprise customers).
Implementation details
I don’t want to repeat how to build process application with the camunda fox stack here, we showed that for example in this blog post and recently wrote a JavaMagazin article which is published soon. I want to concentrate on the UI mediation parts of it, where I used some CDI magic. At least it felt like magic
First I specialize the existing BusinessProcess bean from the Activiti CDI module. Now my own bean is used instead of the original one and I can add additional code which is always executed (a bit like Interceptors). In this bean I only hook in the UiMediator, which is described next.
/**
* Specializes {@link BusinessProcess} bean to add generic code needed for UiMediator
*/
@Specializes
public class UiMediatedBusinessProcessBean extends BusinessProcess {
//we do not have a logged in user in the demo app
private String currentUser = "kermit";
@Inject
private UIMediator uiMediator;
@Override
public void completeTask() {
Task task = getTask();
super.completeTask();
if (task != null) {
uiMediator.checkProcessInstanceStatus(task.getAssignee(), task.getProcessInstanceId());
}
}
@Override
public ProcessInstance startProcessByKey(String key, Map<String, Object> variables) {
ProcessInstance processInstance = super.startProcessByKey(key, variables);
uiMediator.checkProcessInstanceStatus(currentUser, processInstance.getProcessInstanceId());
return processInstance;
}
}
The UiMediator just does the work I described above: it checks if a task is created for the same user in the same process instance immediately. If yes, it just saves it to some properties, which we can use in the JSF page-flow to route to the correct pages.
@RequestScoped
@Named("uiMediator")
public class UIMediator implements Serializable {
@Inject
private TaskService taskService;
@Inject
private FormService formService;
private String nextTaskId;
private String nextTaskForm;
/**
* check for new task in same process for same user
*/
public void checkProcessInstanceStatus(String assignee, String processInstanceId) {
nextTaskId = null;
nextTaskForm = null;
if (processInstanceId != null && assignee != null) {
List<Task> tasks = taskService.createTaskQuery().taskAssignee(assignee).processInstanceId(processInstanceId).list();
if (tasks.size() == 1) {
nextTaskId = tasks.get(0).getId();
nextTaskForm = formService.getTaskFormData(nextTaskId).getFormKey();
}
}
// possible extension here: wait a couple of (milli)seconds to check if that task is reached asynchronously if you have this requirement
}
public String getNextTaskId() {
return nextTaskId;
}
public String getNextTaskForm() {
return nextTaskForm;
}
}
For the page-flow we now add navigation rules, that every time a process instance is started or a task is completed (the later is shown in the code below), we check the UiMediator for a next form. If this is provided we go on this page, otherwise we go back to the task list. The nice thing about this implementation is, that our application does not need any specific code, the whole solution is pretty generic, you only need the correct navigation rule to form a proper page-flow in JSF (maybe if I have another evening of time I will come up with a workaround for this
).
<navigation-rule>
<navigation-case>
<from-action>#{businessProcess.completeTask()}</from-action>
<if>#{uiMediator.nextTaskId==null}</if>
<to-view-id>/app/taskList.jsf</to-view-id>
<redirect/>
</navigation-case>
<navigation-case>
<from-action>#{businessProcess.completeTask()}</from-action>
<if>#{uiMediator.nextTaskId!=null}</if>
<to-view-id>/app/#{uiMediator.nextTaskForm}.jsf</to-view-id>
<redirect>
<view-param>
<name>taskId</name>
<value>#{uiMediator.nextTaskId}</value>
</view-param>
</redirect>
</navigation-case>
</navigation-rule>
That’s it. Not so hard actually, right? A good example of the power of using Java EE 6 together with camunda fox (including BPMN 2.0 and Activiti) for Process Applications
Feedback welcome!










Danke Bernd!
Das Thema gehört schon lange an die BPMS Öffentlichkeit. Gerade im Bereich Prozessoptimierung sind Brüche zwischen den Aufgaben mindestens ein großes Ärgernis. Bei vielen BPMS Herstellern ist das UI-Mediator-Pattern nicht bekannt. Man musste teilweise sehr umständlich und lange erklären, was man als Kunde möchte, um dann zu hören, dass es nicht geht. Software AG und IBM mussten in einem PoC einen Workaround basteln. Andere Hersteller schlugen abenteuerliche Verrenkungen der Page-Flow-Tasks und ähnlichen Quatsch vor.
Weiter so!
2012-04-07 um 10.00 pm Uhr. Verfasst von Alexander EnnsHallo Bernd!
Sehr schöner Beitrag zu einem interessanten Thema!
Entweder ich übersehe das was oder du hast in Bild 6 (User, Activiti and the JSF Layer in between) einen kleinen Fehler eingebaut:
Wenn im Pool “User Customer Registration” eine Instanz durch das Nachrichten-Start-Event erzeugt wird, existiert keine Instanz im Pool “JSF Pageflow” mit der die Aktivität “print and send confirmation letter” kommunizieren kann. (Bei einem Start durch das Event “new customer” erreichen die Instanzen beider Pools immer mehr oder weniger gemeinsam einen Endzustand und sind damit beendet.)
VG
2012-04-10 um 3.42 pm Uhr. Verfasst von Eileen HildebrandEileen
Hi Eileen.
Aus BPMN Sicht gebe ich dir Recht. Allerdings ist der Pool “JSF Pageflow” aktuell nur zur Visualisierung/Dokumentation modelliert und wird nicht direkt umgesetzt, daher habe ich mir diese Freiheit zur Vereinfachung rausgenommen, da es für den darzustellen Sachverhalt recht wenig Relevanz hat.
2012-04-10 um 3.57 pm Uhr. Verfasst von Bernd RückerSchön das unsere Methodik verstanden und gelebt wird
Grüße
Bernd
Hallo Eileen,
touché, Du hast natürlich recht, das ist nicht so sauber modelliert wie es eigentlich sein sollte. Aber wie Bernd schon schreibt, war das in diesem Fall der pragmatische Kompromiss – letztendlich auch in Ermangelung einer guten Alternative, die uns spontan eingefallen wäre.
Falls Du hier eine Idee hast, gerne her damit!
VG Jakob
2012-04-10 um 5.05 pm Uhr. Verfasst von Jakob FreundInteressanter Post!
Wir haben ein ganz ähnliches Konzept mit JSF1/Seam2 und einer anderen Prozess-Engine umgesetzt. Die Erfahrungen “in der Technik” sind ganz gut – die UI wird damit u.a. auch von Überlast in der Prozessengine entkoppelt. Je nach Berechtigungen können User mit diesem Modell längere oder kürzere Pageflows bekommen und meistens flüssiger arbeiten.
Allerdings ist es nicht immer so einfach, die Anwender davon zu überzeugen, dass eine Wartezeit auf einen Folgetask tatsächlich ein Feature und kein Performance-Bug ist. Um so flüssiger die Pageflows laufen und um so stärker der (asynchrone) Geschäftprozess in den Hintergrund tritt, desto sensibler werden auch kurze Wartezeiten beanstandet. Schwierigkeiten gibt es auch, wenn bspw. der Scoring-Service auf ein Timeout läuft. Die JSF-UI muss dann lange blockieren, weil ja ein Folgetask kommen könnte. Hier muss man in Schulungen usw. wirklich deutlich machen, dass eine Prozess-UI kein Datenbank-Eingabgeformular ist (was mühsam ist).
Insofern ein spannendes Thema! Freut mich, dass so ein Feature auch woanders als sinnvoll angesehen wird;)
2012-04-17 um 2.30 pm Uhr. Verfasst von Jan HoffmannHallo Jan. Danke erst mal fürs Feedback! Mit dem Blockieren der GUI hast du natürlich völlig recht und ich sehe eine Alternative: Man benutzt hier irgendwas AJAX-artiges, damit könnte man das warten ja in der GUI so gestalten, das es kein Blockieren ist. Ist natürlich aufwendiger, aber technisch genauso möglich. Letztendlich sollte der Benutzer aber vermutlich so oder so für das Grundproblem der Asynchronität sensibilisiert werden. Ich finde in diesem Zusammenhang auch immer wieder diesen Artikel schön (wenn auch nur grob verwandt mit der aktuellen Diskussion: http://eaipatterns.com/ramblings/18_starbucks.html).
2012-04-18 um 8.13 am Uhr. Verfasst von Bernd RückerImportant addition: I adjusted the code in the showcase to use JMS for the asynchronous response.
That works pretty well and shows how to correlate an incoming JMS message to the right process instance. In order to get this running you have to enable the JBoss full profile to get it working! Here you find the standalone.xml you need here: https://svn.camunda.com/fox/demo/fox/customer-registration-ui-mediator/trunk/standalone.xml
Just replace the existing one in your JBoss 7 (or you build that yourself, in our fox user guide we have documentation for this, unfortunately this is currently not open for public but only for our enterprise customers).
Hopefully that doesn’t cause any trouble with running the example.
2012-04-28 um 10.48 am Uhr. Verfasst von Bernd Rücker