OpenWFE uses a small library named 'xlob' to store data into relational databases. Xlob stores XML document into relational databases, anything that is XML represented, not just workitems or engine flow expressions (pieces of workflow instances).
Since OpenWFE 1.2.x, a new service for storing workitems got implemented : swis (SqlWorkItemStore). This service doesn't use Xlob. This store only works for workitems, with Xlob there exist an expression store (engine run data) and a workitem store (worklist data).
There is a also a subservice named 'SqlPasswdCodec' that can retrieve and store user information from a relational database.
All the relevant SQL schemas may be found under openwfe-x.x.x/sql/xlob/
You may find those services in action by looking at openwfe-x.x.x/etc/engine/db-engine-configuration.xml and openwfe-x.x.x/etc/worklist/db-worklist-configuration.xml.
Here is a summary of the actions needed to 'put a relational base behind OpenWFE' :
Create the set of tables (look for instructions directly in the .sql files in openwfe-x.x.x/sql/xlob/
Make sure to create the DB users required by xlob.
Ensure that OpenWFE may access your DB. For instance, MySQL ships by default with network access turned off, but their JDBC drivers requires it.
Configure OpenWFE itself : seet the appropriate DataSource in etc/engine/db-engine-configuration.xml and in etc/worklist/db-worklist-configuration.xml
Start OpenWFE by running owfe-suite.sh start =db or by double-clicking on owfe-suite-db.bat
The focus of this chapter is on 'Xlob' as 'Swis' is currently not maintained.
A running instance of a workflow is a set of instantiated expressions. XlobExpressionStore stores those expressions in a relational database.
An expression store is a component of an engine application.
A workitem store is a component of a worklist application.
This XlobWorkItemStore uses a relational database for storing the workitems delivered to the participants it works for.
Relational database storage is done through Xlob, which uses a very limited subset of the SQL language, so virtually any RDBMS equipped with a SQL interface and a JDBC connector could be used.
An Xdbc session, can be initialized with either a javax.sql.Connection or a openwfe.org.sql.ds.OwfeDataSource instance. With a data source, Xlob handles by itself a bit of connection pooling. OpenWFE ships with 4 data sources implemented : MySQL, PostgreSQL, MS-SQL and DB2.
The two following XML snippet are taken from etc/engine/db-engine-configuration.xml. For a worklist, just the service name would change.
<!-- the datasource used for storing expressions in mysql -->
<service
name="dataSource"
class="openwfe.org.xdbc.ds.MysqlDataSource"
>
<param>
<param-name>url</param-name>
<param-value><![CDATA[jdbc:mysql://127.0.0.1/xlob?user=xlob&password=xlob]]></param-value>
</param>
</service>
You also have to make sure that MySQL accepts incoming TCP connections, i.e. that its configuration has the 'skip-networking' directive commented out.
<!-- the datasource used for storing expressions in postgresql -->
<service
name="dataSource"
class="openwfe.org.xdbc.ds.PostgresDataSource"
>
<param>
<param-name>url</param-name>
<param-value>jdbc:postgresql:xdbc</param-value>
</param>
<param>
<param-name>user</param-name>
<param-value>xlob</param-value>
</param>
<param>
<param-name>pass</param-name>
<param-value>xlob</param-value>
</param>
</service>
Warning :'swis' is currently not maintained actively anymore.
Whereas Xdbc is a mechanism for storing XML documents in a relational database, the SqlWorkItemStore stores workitems in a dedicated schema. Swis uses the same OpenWFE datasources that Xdbc requires.
Swis was first intended for a software shop whose client application had to be Access. Swiss is used to store workitems in a DB2 database and then the client application used ODBC to retrieve and manipulate workitems. Swis is thus sponsored by Infonium Inc., Canada.
Here is the 'swis' as configured in the its worklist configuration. (You can find this file under etc/worklist/swis-worklist-configuration.xml)
<service
name="Store.bravo"
class="openwfe.org.worklist.impl.swis.SqlWorkItemStore"
>
<param>
<param-name>dataSource</param-name>
<param-value>WorklistContext.dataSource</param-value>
</param>
<param>
<param-name>workItemCoder</param-name>
<param-value>swisCoder</param-value>
</param>
<!--
workitems for participant 'role-bravo' are put in this worklist
-->
<param>
<param-name>participants</param-name>
<param-value>role-bravo</param-value>
</param>
</service>
This 'swis' requires a particular workItemCoder, it is here named 'swisCoder'. This code tells the SqlWorkItemStore how to store the workitems in the relationaldatabase.
<!-- which and how attributes workitem and their attributes
are {en|de}coded -->
<service
name="EngineContext.workItemCoderLoader"
class="openwfe.org.engine.workitem.XmlCoderLoader"
>
<param>
<param-name>configurationFile</param-name>
<param-value>etc/engine/coder-configuration.xml</param-value>
</param>
<param>
<param-name>configurationFile</param-name>
<param-value>etc/worklist/swis-coder-configuration.xml</param-value>
</param>
</service>
This next snippet of configuration shows that a swis enabled worklist configuration, needs yet another configurationFile : etc/worklist/swis-coder-configuration.xml
By default, OpenWFE reads user information from etc/worklist/passwd.xml. Turning this XML file into a set of info usable by the system is done by a small service named XmlPasswdCodec. If you take a look at the PolicyService configuration (in etc/worklist/worklist-configuration.xml) :
<!-- Authentication and authorization service -->
<service
name="policyService"
class="openwfe.org.auth.PolicyService"
>
<param>
<param-name>passwdCodec</param-name>
<param-value>openwfe.org.auth.xml.XmlPasswdCodec</param-value>
</param>
<param>
<param-name>passwd</param-name>
<param-value>etc/worklist/passwd.xml</param-value>
</param>
<param>
<param-name>refreshEachTime</param-name>
<param-value>true</param-value>
</param>
</service>
With the SqlPasswdCodec, this becomes :
<!-- the data source for SqlPasswd -->
<service
name="SqlPasswd.dataSource"
class="openwfe.org.sql.ds.MysqlDataSource"
>
<param>
<param-name>url</param-name>
<param-value><![CDATA[jdbc:mysql://127.0.0.1/xlob?user=xlob&password=xlob]]></param-value>
</param>
</service>
<!-- Authentication and authorization service -->
<service
name="policyService"
class="openwfe.org.auth.PolicyService"
>
<param>
<param-name>passwdCodec</param-name>
<param-value>openwfe.org.auth.sql.SqlPasswdCodec</param-value>
</param>
<param>
<param-name>dataSource</param-name>
<param-value>SqlPasswd.dataSource</param-value>
</param>
<param>
<param-name>refreshEachTime</param-name>
<param-value>true</param-value>
</param>
</service>
The SQL schemas for SqlPasswdCodec (and an initial database dump equivalent to etc/worklist/passwd.xml) may be found under sql/passwd/
You have to extend the class openwfe.org.sql.ds.OwfeDataSource in order to provide OpenWFE with a data source for your favourite database.
For example, this is how the MySQL data source (shipped with OpenWFE) looks like :
package openwfe.org.sql.ds;
import openwfe.org.MapUtils;
import openwfe.org.ServiceException;
import openwfe.org.ApplicationContext;
/**
* An OpenWFE data source for MySQL
*/
public class MysqlDataSource
extends OwfeDataSource
{
//
// CONSTANTS (definitions)
private final static String CON_VALIDITY_SQL_CODE
= "select 1";
//
// CONSTRUCTORS (and co)
public void init
(final String serviceName,
final ApplicationContext context,
final java.util.Map serviceParams)
throws
ServiceException
{
super.init(serviceName, context, serviceParams);
final String url = MapUtils
.getMandatoryString(getParams(), P_URL);
final com.mysql.jdbc.jdbc2.optional.MysqlDataSource mds =
new com.mysql.jdbc.jdbc2.optional.MysqlDataSource();
mds.setURL(url);
this.setDataSource(mds);
}
//
// METHODS from OwfeDataSource
/**
* This method when implemented has to return a piece of SQL code
* that will be run against any database for connection
* validity checking.
* (returns "select 1")
*
* If it were to return null, no connection validity would ever be
* performed.
*/
public String getConnectionValiditySqlCode ()
{
return CON_VALIDITY_SQL_CODE;
}
}
You can see here that it's a matter of providing a decent init() override and an implementation of getConnectionValiditySqlCode(). For this latter method, you can still return 'null' if you trust your DB, the data source will then not perform any test on the connection to determine if it's still 'alive'.
Feel free to submit your own implementation of data sources to OpenWFE. Not all of them may be interesting : some databases are really obscure and/or totally proprietary.