User Guide

This document describes how to use the Flow4J Plug-in and how to integrate it efficiently in java- and web-applications.

Eclipse Project Nature

As an introduction you should understand what an Eclipse Project Nature is. Here is an excerpt from the Eclipse Plug-in developer Guide:

"Project natures allow a plug-in to tag a project as a specific kind of project. For example, the Java development tools (JDT) use a "Java nature" to add Java-specific behavior to projects. Natures are installed on a per-project basis and are configured automatically when added to a project and deconfigured when removed. A project can have more than one nature."

Flow4J Project

Flow4J projects have the Flow4J nature and possible other natures. The Flow4j nature gives Flow4J projects the ability to transform flow models into java source code. After this automatic code generation process, the JDT Plug-in can be used to create the flow's byte code. Thus Flow4J projects should also have the Java nature to generate the flows byte code in the same build process.

You have two possibilities to get a project with the Flow4J nature:

  1. Create a Flow4J project from scratch. The new project will have the Java and the Flow4J natures automatically.

  2. Add the Flow4J nature manually to an existing project, which adds only the Flow4J nature. To accomplish this, right-click on the project's folder and select the menu item from the context menu as shown in the next image.

In the same way as you add th e nature in (2.), you can also remove the Flow4J nature from Flow4J projects. Other natures are not affected by the removal.

Adding the Flow4J nature to a project has the effect that a so called Flow Builder is added to the project's builders. This entry is visible in the "External Tools Builders" panel of the project's properties. If you look at this panel, you may notice that the flow builder is inserted in front of the java builder. This becomes clear if you look at the actions that are performed as a while building the project.

  • The Eclipse platform activates the flow builder
  • Eclipse passes a list of flow files, which need a rebuild, to the flow builder
  • For every flow file, the flow builder transforms the flow's XML model into java source code and saves the java file in the same folder where the flow file resides
  • The Eclipse paltform activates the java builder
  • Eclipse passes a list of java source files, which need a rebuild, to the java builder (JDT)
  • The java builder compiles those files

You see, the flow builder has to be in front of the java builder because the java source files need to be created first, prior to be compiled by the java builder.

Flow Model

The flow model holds the flow's logic. The Eclipse build process transforms the flow model into java source code.

If you store the flow model in Eclipse, an XML representation of the flow model is created and stored in the flow file. Flow files have the .f4j extension. Opening a flow file with a double-click activates the Flow Graphical Editor which is the de fault editor to model your flows.

Flow Graphical Editor

The Flow Graphical Editor is the "tool" to design flows. With it's help, you can model the flow's runtime behaviour in a drag and drop manner. The flow editor has two areas

  • Tool palette (left)
  • Drawing area (right)

A flow contains a series of flowlets. Flowlets are the building blocks of a flow. They are connected to each other with Connections . To place a flowlet in the drawing area, click on the desired tool's label in the palette, and then click at a free region in the drawing area where the flowlets should be placed.

Normally a flow model contains one flow. That's why I often say "flow" to the flow model. Technically, in the flow editor, it is possible to build a flow model that contains more than one flows, as in the next image. In such cases there is often one main flow and some sub flows which belong together in the same flow model. Creating sub flows is a way to modularize parts of a flow. This improves reusability, and it's also easier to work on some small, easy to understand sub flows than on one large, complex flow that does not fit on the screen.

The following flow model contains two flows which can be triggered independently by their unique start name "Start" and "Hello".

In the runtime environment, like a web application the flow has to be accessed in some way. For this purpose, every flow model has a unique name, which is not the file name itself. This name, the Flow Name should be a characteristical name for the flow model. The flow name is used to register the flow's runtime class at th e Flow4J runtime environment in order to access it, for example, by a HTTP request. The flow name can be changed in the flow's properties view. Click at a free area in the flow editor and open/select the properties view.

Inside a flow model all start flowlet names have to be different. In the previous image the start nodes can be accessed in the following way:

  • SendMailFlow-SendHtmlMail
  • SendMailFlow-SendMail

In a later section, flow execution will be explained in detail.

Flow Dictionary

The Flow Dictionary contains information in the form of key-value-pairs. The same dictionary instance is passed from one flowlet to the next while processing the flow. Every flowlet can read data from it, or add new data to it, which may be needed by following flowlets.

The flow dictionary extends the java.util.Map and adds the following convenience methods to it. All methods have only one argument, key with type String

method Return Type
getBoolean(key) java.lang.Boolean
Bool(key) java.lang.Boolean
B(key) java.lang.Boolean
bool(key) boolean
b(key) boolean
getInteger(key) java.lang.Integer
I(key) java.lang.Integer
getInt(key) int
i(key) int
getString(key) java.lang.String
Str(key) java.lang.String
str(key) java.lang.String
getFloat(key) java.lang.Float
F(key) java.lang.Float
f(key) float
getDouble(key) java.lang.Double
D(key) java.lang.Double
d(key) double
getShort(key) java.lang.Short
S(key) java.lang.Short
s(key) short
getLong(key) java.lang.Long
L(key) java.lang.Long
l(key) long

All methods of the Map interface, like get(), put() ..., are available when working with the flow dictionary.

Flowlets

Flowlets in a simple flow are like process steps in a process or chain links in a chain, which are linked together. More complicated flows contain branching and jumps to other flows etc. In most cases business processes resemble these complex flows.

As already mentioned, flowlets are the the building blocks of flows. Flowlets have sensitive connection points where they can be connected to other flowlets. A flowlet may have two types of connectors:

  • source (outgoing)
  • target (incoming)

Here is an example for the various types

  • Start flowlet has one source connector
  • End flowlet has one target connector
  • Decision flowlet has one target and two source connectors

Flowlets can have a Label like the start flowlet. The selected label's text can be changed in the properties view. Don't forget to select the flowlet before!

To see or change the properties of the flowlet, it's also possible to select the flowlet's label. Both of the following pictures will show the properties of the same flowlet:

In future releases the designer will be able to handle other figures than flowlets. One example of such a figure will be a text area where comments on the flow or flowlets can be placed. These text areas won't be connected to the flow, so they won't be flowlets. I'm still looking for an adequate name for them.

Start Flowlet

A start flowlet is the entry point for a flow.

In one flow model all start flowlets need to have a unique name , otherwise only one can be accessed and it's undetermined which one of them. The default name of start flowlets is "Start".

A flow can have more than one start flowlets, so that the flow's execution can begin at different places, at different start flowlets.

In the Flow4J Designer, Start Flowlets have a dynamically extendable property list. This means that you specify a comma separated list of property names, and Eclipse generates the Property Descriptors automatically. If you select a Start Flowlet and open the Properties View, you can add propertiy keys in the line "Property names (comma separated)".

source connectors 1 at the bottom of the figure
target connectors 0

Task Flowlet

A task flowlet wraps a specific task or functionality.

Such a task is a java class that implements the net.orthanc.flowlet.runtime.ITaskFlowlet interface or extends the AbstractTaskFlowlet class. The AbstractTaskFlowlet has default implementations of the following methods, which return null by default:

  • getDescription() - Returns a description of the task's functionality.
  • getPropertyDescriptors() - Returns a collection of property descriptor objects. Properties enable extra configuration of a task, in order to improve their reusability.
  • getInputParameterDescriptors() - Returns a collection of input parameter descriptor objects. Only needed in the designer for visualisation purposes. The returned collection should contain all parameters which are needed by the task are therefor should be in the dictionary.
  • getOutputParameterDescriptors() - Returns a collection of output parameter descriptor objects. The returned collection should contain all parameters which are put in the dictionary by the task itself.

The following methods have to be overridden/implemented by the developer:

  • getName() - returns the name of the task flowlet, that is only for visualisation purposes. This should be a short name which stands for the action that is performed by this task, like "SendMail", "Login" or "LogDictContents". Blanks can be used too like "send mail" etc.
  • execute() - contains the task functionality. This method is called by the flow's generated class while runtime. (Command design Pattern), and can contain any sort of java code, even JNI or EJB calls.

The usage of task propertie s is discussed later.

Task flowlets have a special feature, that helps a lot while developing with Flow4J: The designer indicates whether the task's class can be found in the projects classpath. Have a look at the following example:

In this example we have two java packages:

  • flows - contains the flow files and their generated java sources
  • tasks - contains task flowlet classes (SendMail and ValidateHtml)
The designer displays the ValidateHtml task's name in black and the SendMail task's classpath in red . The difference is that the ValidateHtml task's class was found in the projects classpath, but the class of task SendMail could not be found, because the class path entry in the property view was typed incorrectly. The SendMail class is not in the "task" package but in the "tasks" package. Here you can see a clarifying image on this issue:

Task flowlet names can be parameterized. This means, if the task has properties, then a property value can be displayed in the task flowlet's name.

To prameterize a property, use the syntax similar to Ant properties:

${property_name}

In the following sample the class SendMailTask declares a property "ip" and uses it in the task's name. The flow uses two SendMail tasks, each with its own smtp IP address (10.0.0.1 and 10.0.0.2).

public class SendMailTask extends AbstractTaskFlowlet {

  public void execute(FlowDictionary dict) {
    ...
  }

  public String getName() {
    return "send mail (smtp-ip: ${ip})";
  }

  public TaskPropertyDescriptors getPropertyDescriptors() {
    TaskPropertyDescriptors d = new TaskPropertyDescriptors();
    d.addPropertyDescriptor("ip", "ip of the smtp server");
    return d;
  }
}
				

The task flowlet's java class file can be created with the help of a wizard, similar to the creation of a generic java class. For this reason the toolbar contains the following tool:

The following wizard defaults to a general task flowlet base class that resides in the Flow4J runtime package. In the wizard you can specify the creation of the method stubs for task properties, in- and out-parameters. Hitting the "Finish" button generates the tasks's java source file and opens it. The All methods in the class file contain predefined javadocs and default values for the task name and description, that you may change.

source connectors 1 in the middle of at any unconnected side
target connectors 1 in the middle of at any unconnected side

Script Flowlet

Script flowlets are task flowlets written in a scripting language like Python, Groovy or others. Using scripted tasks in Flow4J is described in this document

Note that there is no tool for script flowlets in the designer's palette. You can only place a script flowlet on the designer by drag and drop.

source connectors 1 in the middle of at any unconnected side
target connectors 1 in the middle of at any unconnected side

Decision Flowlet

A decision flowlet allows the flow to branch , depending on a boolean value.

The decision checks the given statement while runtime. The Statement must be a valid java statement that will be insterted in an specific if () {} clause in the flow's java source code.

Everything in the if statement has full access to the flow dictionary. So the above-mentioned helper methods can be used (see flow dictionary section ). Here are some examples for decision flowlet labels:

"Alex".equals(str("name"))
i("counter") < 15
getInteger("numberItems") != null
//	methods of the Map interface
! isEmpty()
containsKey("hallo")

The flow dictionary extends java.util.HashMap . So all methods of a Map can be used here too.

The statement of a decision flowlet can be quite "technical" for somebody who has ot much programming experience. For this reason a decision flowlet has an additional property "description" which can be any sort of explanatory description. The next image shows an example of this feature.

As long as a description is provided, it is shown in the designer. Of cource double clicking on the decision flowlet's label enables the editing of the flowlet's statement and not its description. The decision can only be set in the properties view. The flowlet's tooltip also shows the statement.

All decision flowlets have two outgoing connections. One for the true case and one for the false case. The connection's value can be toggled in the properties view after you have selected the connection or its label. The other (not selected) outgoing connection of the decision flowlet will be adjusted automatically

source c onnectors 2 at any unconnected corner
target connectors 1 at any unconnected corner

Join Flowlet

The join flowlet's purpose is to join one to maximum three branches of the flow.

An extreme situation is if you have one incoming and one outgoing connection. In this case you can leave the join flowlet simply away and connect the two flowlets directly. If you don't remove the unnecessary join flowlet yourself, then the java source code generator detects and optimizes it away.

source connectors 1 at any unconnected side
target connectors 3 at any unconnected side

Call Flowlet

The call flowlet leaves the current flow in order to execute another flow and then returns back to the calling flow. All information that is available to the calling flow is passed over to the called flow. If the called flow adds information to the flow dictionary then this new information will be available for the rest of the calling flow.

source connectors 1 at the top or bottom
target connectors 1 at the top or bottom

Jump Flowlet

The jump flowlet passes control completely to the other flow. All information that is avaliable to the current flow is passed over to the other flow.

The jump flowlet is a terminating flowlet that has no more flowlets behind it.

source connectors 0
target connectors 1 at the top, left or bottom

Template Flowlet

The template flowlet is useful in GUI applications or web applications where the the process returns a String indicator, which JSP/HTML-page, or Swing window should be shown as next.

The name of the template or its indicator String is stored in the flow dictionary with a special key that is defined in the net.orthanc.flow4j.runtime.Flow4JRuntimeConsts class in the following way:

static final public String DICT_KEY_TEMPLATE_NAME = "TEMPLATE_NAME";

In your java or web application you can access the templete name in the following way:

FlowDictionary dict = new FlowDictionary();
dict.add("name", "Alex");
FlowManager.executeFlow(MyFlow.class, "Start", dict);
String templateName = dict.get(Flow4JRuntimeConsts.DICT_KEY_TEMPLATE_NAME);
					

The template flowlet is a terminating flowlet that has no more flowlets behind it.

source connectors 0
target connectors 1 at any side

End Flowlet

The end flowlet indicates the end of the flow. Usually sub-flows, which are called by other flows or flows which are called internally from java code are terminated in this way. The contents of the flow dictionary are available to the caller after flow execution.

The template flowlet is a terminating flowlet that has no mor e flowlets behind it.

source connectors 0
target connectors 1 at the top

Build Process

The Eclipse build process is triggered by the user, or is automatically triggered by the Eclipse platform if the "Perform build automatically on resource modification" feature is checked in the workbench preferences.

The following actions are done by the Eclipse platform, so you have no influence on the java source code generation process.

Through the build process of a flow model, that is an XML file on the filesystem, the model is transormed into java source code. The java source code contains the same flow logic as visible in the designer.

Flow files can be created everywhere in a project. But it is reasonable to place them adequately somewhere in the source path. Otherwise the Eclipse build process will take no notice of them.

Here is the java source code of an empty flow file, that is automatically created by the Flow4J Plug-in. All created flow classes implement the net.orthanc.flow4j.runtime.IFlow interface. The flow class has to implement the methods getFlowName() and execute() . In this example the flow contains no flowlets and so the execute method throws an exception to indicate that the flow is not executable.

/*
 * This class was automatically generated by the
 * Flow4J-Eclipse Plug-in
 */
package flows;

public final class testFlow3 implements net.orthanc.flow4j.runtime.IFlow {

  final public String getFlowName() {
    return "testFlow3";
  }
  
  public void execute(String startFlowletName,
                      net.orthanc.flow4j.runtime.FlowDictionary dictionary)
  {
    throw new net.orthanc.flow4j.runtime.Flow4JRuntimeException(
      "start node \"" + startFlowletName + "\" not found in flow \""
      + this.getClass().getName() + "\"");
  }
}
					

If you build a Flow4J project, in the first step flow files will be transformed to java source code and then the java source files will be compied by tha JDT Plug-in's java compiler as already described in the Flow4J Project section.

After the build process the flow's java source file resides in the same folder, beside it's flow XML file.

For the java builder, flow files with the extension .f4j are considered as resources and copied to the output folder. To prevent this behaviour, you can insert a build exclusion filter in the project properties panel.

Project Properties

With the help of project properties, you can influence the projects behaviour in the IDE. Flow4J projects have the following property sections (currently only one):

  • Flow repository

In the "Flow repository" panel you can decide whether the plugin should automatically generate a Java source file that contains a collection of all registerable flow classes.

Flow classes can be registered at the FlowManager manually one after the other. But this is time consuming and error prone, because if you forget to register a flow, then you realize your mistake only later while runtime. For this reason the FlowManager has the possibility to register a collection of flow classes at once (see IFlowRepository ). With the help of this property page the Plug-in can automatically generate a Java class wh ich contains such a collection. The class implements the IFlowRepository interface and returns the collection in the getFlowClasses() method.

package test;

public class TestFlowRepository implements net.orthanc.flow4j.runtime.IFlowRepository
{
  public java.util.Collection getFlowClasses()
  {
    java.util.List classes = new java.util.ArrayList();
    classes.add(test.SomeFlow.class);
    classes.add(test.OtherFlow.class);
    classes.add(test.login.LoginFlow.class);
    return classes;
  }
}
				

Registering all flows is quite easy:

FlowManager.registerFlows(new test.TestFlowRepository());
				

The Java source code is generated by an additional Builder object "Repository Builder". This builder is executed after the "Flow Builder" but before the "Java Builder". So that it is compiled together with the flow classes.

Now, if you have more than one source folders on your build path, in which one will the Plug-in generate the new class? The Repository Builder first looks for the class's package in all source folders. If the package already exists, then the new new java source file will be generated in this package folder. If the package does not exist yet, then the Plug-in generates the package structure and the Java file in the first source folder that is specified on the project's build path.

Flow4J Runtime

The Flow4J runtime is completely independent from the Flow4J Eclipse Plug-in.

  • With the help of the Plug-in you can design flows and create the flow's java source representation, which can be compiled to java bytecode.
  • The Flow4J runtime package provides a framework to register and execute flows which are present as compiled java classes. So no XML parsing is necessary.

After all flows are compiled to java bytecode, they can be deployed into any java web/application in the same way as other .class files, or as a jar archive.

in the most cases you will deploy the following jars:

FlowsAndTasks.jar contains your flows and dependent task flowlet classes
flow4jruntime.jar contains the Flow4J runtime classes of the package

net.orthanc.flow4j.runtime and

net.orthanc.flow4j.runtime.servlet and some basic classes from the

net.orthanc.flow4j package

Here is a list of all runtime classes:

Package net.orthanc.flow4j.runtime
FlowManager is responsible for registering flow and task flowlet singletons, an also for the lookup and execution of flows
IFlow All generated flow classes must implement this interface. Classes which implement this interface are normally created by the Plug-in flow builder
FlowDictionary Extends java.util.HashMap . Contains the runtime information while flow execution
IFlowlet Super interface for all flowlets, the user implements. This interface is currently only indirectly used by the ITaskFlowlet as a superinterface.
ITaskFlowlet All tasks implemented by the user must implement this interface.
AbstractTaskFlowlet Abstract base implementation of the ITaskFlowlet interface. Most methods return null . The methods getName() and execute() are abstract.
IFlowRepository Classes which implement this interface can be used as a convenience class to register a bunch of flows at once at the FlowManager
Flow4JRuntimeException This exception is thrown if errors occure while flow registration or execution
Flow4JRuntimeConsts Hold some consts which are used by the FlowManager
Package net.orthanc.flow4j.runtime.servlet
JSPDispatcherServlet Calling this servlet transforms all request parameters in a FlowDictionary and calls the specified flow's start flowlet. After flow execution the request is forwarded to the given JSP page, at the end of the flow.

FlowDispatcherServlet The flow is executed but no forward to a JSP page is performed. This servlet is useful if the flow returns some binary response directly in the ServletOutputStream. The flows executed by this servlet should end with the ordinary End-Flowlet.

Flow4JServletException Wraps the causing exception.

Package net.orthanc.flow4j.runtime.descriptors
TaskPropertyDescriptor Class that represents a task flowlet property, that has a name and a value. The value can be set in the Eclipse IDE and read with the getProperty(name) method, while runtime.

TaskPropertyDescriptors A collection object of TaskPropertyDescriptor instances.

Task Flowlet Properties/Parameters

Task Properties

In order to improve reusability, task flowlets may have "properties". For example you can implement a SendMail task flowlet that doesn't has its SMTP address hard coded in the task flowlet, but provided in the form of a property. So the application can use the SendMail task many times with a different SMTP address.

Users who implement the task flowlet, declare which properties can be set, in the Eclipse IDE while development time. While runtime, the execute() method can access those properties, as a java.lang.String .

The following steps have to be taken to use task properties, which are also visible in the task flowlet's tooltip:

  • Override/implement the method getPropertyDescriptors() and add the required property descriptors. The method addPropertyDescriptor(name, description) declares a task property.
    	public TaskPropertyDescriptors getPropertyDescriptors() {
    		TaskPropertyDescriptors descs = new TaskPropertyDescriptors();
    		descs.addPropertyDescriptor("smtp", "SMTP server address");
    		return descs;
    	}
    						
  • Set the value of the "smtp" property in the "properties" view of the task flowlet in the Eclipse IDE.
  • Access the property in the execute() of the task flowlet like
    	public void execute(FlowDictionary dict) {
    		String smtpAddress = getProperty("smtp");
    		....
    	}
    						

Task Parameters

Task flowlet input/output properties were introduced to visualize which parameters the task expects to be in the dictionary and which parameters the task puts into the dictionary. Here is a sample how to declare and visualize these parameters.

package my.f4jprj.tasks;

import net.orthanc.flow4j.runtime.AbstractTaskFlowlet;
import net.orthanc.flow4j.runtime.FlowDictionary;
import net.orthanc.flow4j.runtime.descriptors.TaskParameterDescriptors;
import net.orthanc.flow4j.runtime.descriptors.TaskPropertyDescriptors;


public class SendMailTask extends AbstractTaskFlowlet {

	public void execute(FlowDictionary dictionary) {
		//	send mail functionality
	}

	public String getName() {
		return "send mail";
	}

	public TaskParameterDescriptors getInputParameterDescriptors() {
		TaskParameterDescriptors descs = new TaskParameterDescriptors();
		descs.addParameterDescriptor("mailto", String.class, "Mail recipient");
		descs.addParameterDescriptor("subject", String.class, "Mail subject");
		descs.addParameterDescriptor("body", String.class, "Mail body");
		return descs;
	}

	public TaskParameterDescriptors getOutputParameterDescriptors() {
		TaskParameterDescriptors descs = new TaskParameterDescriptors();
		descs.addParameterDescriptor("errors", "java.util.List",
				"Collection of errors and exceptions. Not present if none.");
		return descs;
	}

	public String getDescription() {
		return "Sends an Email";
	}

	public TaskPropertyDescriptors getPropertyDescriptors() {
		TaskPropertyDescriptors descs = new TaskPropertyDescriptors();
		descs.addPropertyDescriptor("smtpServerIP", "IP of the smtp server");
		return descs;
	}
}
				

The input parameters a declared with the help of the getInputParameterDescriptors() method, and the output parameters with getOutputP arameterDescriptors() . Both methods return a collection of descriptor objects which are created by the addParameterDescriptor(name, type, description) method. All arguments are Strings and are only used for visualisation purposes. Thus they have no impact on the flow's java source code generation. The getXXXdescriptors() methods have a default implementation in the AbstractTaskFlowlet class, which return null .

Most important task information is shown in the task flowlet's tooltip as shown on the next image.

Registering Flows

In order to use the FlowManager , to execute flows, all needed flows have to be registered at the FlowManager . The FlowManager stores flow and task flowlet singletons. So only one instance of them will be reused many times, avoiding unnecessary flow object instantiations.

Here are all ways to register a flow:

FlowManager.registerFlow("com.package.flows.SomeFlow");

IFlow myFlow = new com.package.flows.SomeFlow();
FlowManager.registerFlow(myFlow.class);

Collection flowCollection = new ArrayList();
flowCollection.add(new com.package.flows.SomeFlow());
flowCollection.add(new com.package.flows.OtherFlow());
FlowManager.registerFlows(flowCollection);

//	the IFlowRepository interface has one method:
//	public Collection getFlowClasses();
//	the collection contains java.lang.Class instances
//	as in the last example
IFlowRepository flowRepository = new com.package.flows.FlowRepos();
FlowManager.registerFlows(flowRepository);
					

Register Tasks

Task flowlets are automatically registered in the generated flow classes. So you don't ha ve to bother with them.

Execute Flows

To execute a flow, a bunch of methods are available:

FlowDictionary dict = new FlowDictionary();
dict.put("name", "Alex");

FlowManager.executeFlow("SomeFlow", "Start");
FlowManager.executeFlow("SomeFlow", "Start", dict);

FlowManager.executeFlow("SomeFlow-Start");
FlowManager.executeFlow("SomeFlow-Start", dict);

IFlow myFlow = new com.package.flows.SomeFlow();
FlowManager.executeFlow(myFlow.class, "Start");
FlowManager.executeFlow(myFlow.class, "Start", dict);
					

Servlet Usage

Flow4J offeres the possibility to trigger flow execution directly by a browser request. Depending on the desired response type of the flow (text or binary), there are two servlets you can use:

  1. FlowDispatcherServlet - Dispathes to the flow given in the URL path info and executes the flow. This servlet is useful if you want to return binary data, like a PDF content.
  2. JSPDispatcherServlet - Executes the given flow as FlowDispatcherServlet and additionally forwards the request to the resource that is declared by the template flowlet at the end of the flow. The resource can be a JSP page or a HTML page.

To put these servlets into action, put lines similar to these in your WEB-INF/web.xml file:

<servlet>
  <servlet-name>Flow4JJSPDispatcherServlet</servlet-name>
  <display-name>Flow4J JSPDispatcherServlet</display-name>
  <servlet-class>net.orthanc.flow4j.runtime.servlet.JSPDispatcherServlet</servlet-class>
  <init-param>
    <param-name>flow-repository-class</param-name>
    <param-value>flows.MyFlowRepository</param-value>
  </init-param>
  <init-param>
    <param-name>servlet-path</param-name>
    <param-value>flow</param-value>
  </init-param>
  <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
  <servlet-name>Flow4JJSPDispatcherServlet</servlet-name>
  <url-pattern>/flow/*</url-pattern>
</servlet-mapping>
				

The JSPDispatcherServlet executes the flow that is defined in the URL's path info and then dispatches to the HTML, JSP or other resource that is declared by the template flowlet at the end of the executed flow.

The servlet's init() method is called, which automatically registers all flows that are declared in the flows.MyFlowRepository class, at the FlowManager . This is a convenient was to register the needed flowlets at once at servlet/server startup time.

The second init-parameter is important if you want to use custom JSP tags from Flow4J. Look at the next chapter for more infos on this.

To be able to return binary content, that is computed directly by some task flowlets, insert the following lines in your WEB-INF/web.xml file:

<servlet>
  <servlet-name>Flow4JFlowDispatcherServlet</servlet-name>
  <display-name>Flow4J FlowDispatcherServlet</display-name>
  <servlet-class>net.orthanc.flow4j.runtime.servlet.FlowDispatcherServlet</servlet-class>
</servlet>

<servlet-mapping>
  <servlet-name>Flow4JFlowDispatcherServlet</servlet-name>
  <url-pattern>/binary/*</url-pattern>
</servlet-mapping>
				

Important:

If you return binary content with the FlowDispatcherServlet , then flow should be terminated with a "End Flowlet". If the flow ends, by acciden t, with a "Template Flowlet" then the template information will be ignored!

The given url-pattern allows to call the following URLs to execute the "TimeFlow" beginning at the "Start" start flowlet:

http://server:port/webappcontext/flow/TimeFlow-Start
or
http://server:port/webappcontext/flow/TimeFlow-Start?name=Alex&age=33
				

if you want to return binary content like a PDF file, the following URL could be used:

http://server:port/webappcontext/binary/PDF-Create?...
					

Custom JSP Tags

The Flow4J web framework introduces a custum JSP tag to generate URLs to a flow. Here are some examples:

<%@ taglib uri="flow4j" prefix="f4j" %>

<META HTTP-EQUIV="refresh" CONTENT="0;URL=<f4j:link flowstart="Game-Start"/>">
or
<form action="<f4j:link flowstart="Game-Guess"/>" method="post">
...
</form>
or
<a href="<f4j:link flowstart="Game-Start"/>
            <f4j:param name="name" value="Alex"/>
         </f4j:link>">new game</a>
				

The link tag has one attribute "flowstart" that declares which flow should be executed at which start node.

Additional parameters can be supported by the embedded param tag. The param tag has two attributes "name" and "value".

Another attribute is the "ignorelevels" attribute. It is necessary if you use the tag in directly called jsp pages like http://server/a/b/c/d/bla.jsp

In this case the browser adds the relative folder structure to the path. So a generated link would look like

http://server/a/b/c/d/flow/SomeFlow-Start.

This would be an invalid syntax for executing flows. If you set ignorelevels="false" then flow4j compensates the path that the server will a dd, by "../" sequences. The correctly generated link would look like:

../../../../flow/SomeFlow-Start

The default value is false .

Flowdoc

Flowdoc is a way to generate flow documentation of Flow4J projects. All Flowdoc functionality is packaged in a separate Eclipse Plug-in, so don't forget to install it beside the Flow4J-Core Plug-in. See installation guide . The Flowdoc export wizard generates HTML pages similar to the javadoc tool, thus the HTML frameset contains the typical frames like packages, classes and class details. Every flow is transformed to a Java class so browsing the flow documentation is like browsing classes. The flow details page can contain the following information:

  • The flow's name and class
  • Start Flowlets if they have properties
  • Task Flowlets with their name, class, description, properties, input- and output parameters
  • Decision Flowlets if their description is specified, so that their java if-statement needs to be documented because the flow image shows the description.

The above flow documentation can be seen in action at FlowGallery documentation

The export process is triggered in the same way as the javadoc generation. Select the "Export..." menu item from the "File" menu or from the project's context menu.

On the next page, after you click on "Next", flows can be selected which should be exported and also the destination folder. The destination folder will contain a "flowdoc" dictionary which contains the flow/class structure.

Now some technical aspects. The Flowdoc export wizard generates a small ant script in the background which is executed to create the flow images and HTML pages. This ant script is saved to ECLIPSE_HOME/workspace/.metadata/.plugins/net.orthanc.flow4j/flowdoc_buld.xml . If the Flowdoc export out of the IDE does not work as expected, then you can try to execute the ant script directly like

cd ECLIPSE_HOME/workspace/.metadata/.plugins/net.orthanc.flow4j
ant -buildfile flowdoc_build.xml
				

You can also take this ant script and copy the flowdoc target in your custom ant scripts to extend it with flow documentation generation. Flowdoc uses XDoclet to generate the HTML pages.

Third Party Integration

Flow4J as an effective Controller part of any MVC architecture, can be integrated in the following frameworks:

  • Apache Struts
  • Jakarta Velocity

Integration classes of these framework are all in the package net.orthanc.flow4j.runtime.connectors .

The following sections describe how the integration can be done.

Apache Struts Integration

All integration details are describen in a separate document .

Jakarta Velocity Integration

The Flow4J integration with the powerful Velocity template engine is quite easy, as you will see in this section. The main interface between Flow4J and Velocity is the Velocity Context . If Velocity is used in a web environment, then instead of using the default flow dispatcher servlet a special velocity flow dispatcher servlet is registered in the web.xml descriptor file. This velocity controller servlet has the type net.orthanc.flow4j.runtime.connectors.velocity.VelocityFlowDispatcherServlet and extends the org.apache.velocity.servlet.VelocityServlet class.

<servlet>
  <servlet-name>VelocityFlowDispatcherServlet</servlet-name>
  <display-name>Velocity FlowDispatcherServlet</display-name>
  <servlet-class>net.orthanc.flow4j.runtime.connectors.velocity.VelocityFlowDispatcherServlet</servlet-class>
  <init-param>
    <param-name>flow-repository-class</param-name>
    <param-value>com.mycomp.myproj.flows.MyFlowRepository</param-value>
  </init-param>
  <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
  <servlet-name>VelocityFlowDispatcherServlet</servlet-name>
  <url-pattern>/flow/*</url-pattern>
</servlet-mapping>
				

With the above mapping URLs like the following example can trigger flows which result in a Velocity template:

http://server/webapp/flow/SomeVelocityFlow-Start?...

The Flow4J Velocity controller servlet may register the flows in the sepcified flow repository in its init() method if the flow repository class id specified in the servlet's init parameter.

As the VelocityFlowDispatcherServlet extends the VelocityServlet , the handleRequest(req, res, context) method is called if the user submits a http request. The handleRequest() method contains the logic to extract the flowname and the start flowlet's name from the URL and to execute the flow. All flows should be terminated with a TemplateFlowlet that declares the path to the velocity templ ate. This template declaration will be used as the return value of the handleRequest() method.

To make make the Flow4J Velocity integration as convenient as possible, all velocity task flowlets may extend from the AbstractVelocityTaskFlowlet class, that offers two execute() methods:

  • execute(FlowDictionary dict) - declared in AbstractTaskFlowlet class
  • execute(FlowDictionary dict, Context context)

The default implementation of the first method is to extract the Velocity context from the flow dictionary and to call the second execute() method. So my advice: don't override the first version, but use the second version only.







That's it, and enjoy.

Alex