IP-Tech Offshore

Nos compétances, votre nouveau levier de croissance

Enhancing performance
for a OSWorkflow based application

I’m writing this post in order to share with you my experience when integrating OSWorkflow 2.8 with Hibernate 3 (as a workflow store).

Open Symphony - OS Workflow

Let me begin by explaining the needs. Soon after finishing BdocInteractive development -which is a workflow based, web application- performance tests began. We rapidly found out that dealing (especially querying) with thousands of workflow instances dramatically increases time responses. We were configuring the OSWorkflow with a JDBC store. That’s why, the first idea was to use a Hibernate 3 based workflow store with a second level cache.

I began looking for a simple and a straightforward way to configure OSWorkflow to use a Hibernate store. At a first glance, I found an important number of unanswered questions about the subject, especially the ones that deal with the 2.8 version of OSWorkflow. The article in the opensymphony wiki about spring and Hibernate 3 integration didn’t interest me too much for these reasons:

- The proposed solution uses the persistence layer of Spring (underlying Hibernate), which is not the case of BdocInteractive, where we use Spring only for dependency injection and Hibernate 3 for persistence.

- It seems that the article deals about the older versions of OSWorkflow : they are mentioning classes like PropertySetItemImpl (and the PropertySetItem interface) and SpringHibernateWorkflowFactory that are no longer included in the OSWorkflow distribution.

- The article is rather confusing, as it gives two workflow configurations (without mentioning that they are alternatives of each other), the xml based one, and the Spring wiring based one. Besides, the article does not mention how to integrate one of the two configurations, even if it looks straightforward for some users, I personally think that injecting a workflow configuration using Spring beans needs a minimum of knowledge about the framework (OSWorkflow) model classes.

I can also add that some of the project constraints, like having a separate class to deal with opening and closing sessions (Hibernate ones) in a static way (don’t worry, it is thread safe), or the way that we implemented the session-per-request pattern, pushed me to develop our own Hibernate workflow store. Or, let me say, rewrite the Hibernate workflow store provided in the OSWorkflow distribution, so that it meets our needs.

The HibernateWorkflowStore class :

The main modification made in the HibernateWorkflowStore class is that it will no longer have the Hibernate session object as an instance field, but would try to obtain the session from an external class (the one mentioned above) in a static way. So, the execute method would look like:

protected Object execute(InternalCallback action)
                    throws StoreException {

    try {
        Session session =
                HibernateSessionManager.getSession();

        Transaction t = session.beginTransaction();
        Object ret = action.doInHibernate(session);
        t.commit();

        return ret;

    } catch (HibernateException e) {
        throw new StoreException(e);
    }

}

Injecting the workflow Configuration:

As we are using Spring for dependency injection, here is the fragment of the xml file declaring beans :

<bean id="workflow"
    class="any.class.implementing.the.workflow.interface">

    <property name="configuration">
        <ref local="osworkflowConfiguration" />
    </property>

    <!-- This is beyond the scope of the article -->
    <property name="*******" ref="******" />

    <!-- This is beyond the scope of the article -->
    <property name="resolver">
        <ref local="workflowTypeResolver" />
    </property>
</bean>

<bean id="osworkflowConfiguration"
    class="com.opensymphony.workflow.config.SpringConfiguration">

    <!-- Our Store !!! -->
    <property name="store">
        <ref local="workflowStore" />
    </property> 

    <!-- This is beyond the scope of the article
    (can be any valid workflow factory) -->
    <property name="factory">
        <ref local="workflowFactory" />
    </property>
</bean>

<bean id="workflowStore"
    class="our.own.implementation.HibernateWorkflowStore">

    <!-- Obvious !! that's why we wanted to implement
    the hibernate store -->
    <property name="cacheable" value="true" />

    <!-- The cache region id that will be used
    by the second level cache provider  -->
    <property name="cacheRegion" value="workflows" />
</bean>

The Hibernate configuration file:

What we have to do in the (xml based) Hibernate configuration file is to tell Hibernate which cache provider we want Hibernate to use as a second level cache provider by adding these lines:

<property name="hibernate.cache.provider_class">
    net.sf.ehcache.hibernate.SingletonEhCacheProvider
</property>

<property name="hibernate.cache.use_second_level_cache">
    true
</property>

<property name="hibernate.cache.use_query_cache">

Also, we have to map OSWorkflow related objects:

<mapping resource="path/to/mapping/files/HibernateCurrentStep.hbm.xml" />
<mapping resource="path/to/mapping/files/HibernateHistoryStep.hbm.xml" />
<mapping resource="path/to/mapping/files/HibernateWorkflowEntry.hbm.xml" />

These mapping files are slightly different from the ones included in the distribution, in order to enable caching (don’t forget collection caching). Here is an example for the workflow entry object:

<class name="com.opensymphony.workflow.spi.hibernate.HibernateWorkflowEntry"
table="OS_WFENTRY">

<cache usage="read-write"/>

<id name="id" unsaved-value="-1">
    <generator class="native"/>
</id>

<version name="version"/>

<property name="workflowName" column="name"/>

<property name="state"/>

<bag name="currentSteps" cascade="all-delete-orphan"
    lazy="true" inverse="true">
    <cache usage="read-write"/>
    <key column="entry_Id"/>
    <one-to-many class=
    "com.opensymphony.workflow.spi.hibernate.HibernateCurrentStep" />
</bag>

<bag name="historySteps" cascade="all" lazy="true" inverse="true">
    <cache usage="read-write"/>
    <key column="entry_Id"/>
    <one-to-many class=
    "com.opensymphony.workflow.spi.hibernate.HibernateHistoryStep" />
</bag>
</class>

In order not to make the post much longer, I won’t discuss the way I implemented Hibernate usage for the propertySet persistence that, by the way, contributed also in increasing the application performance. Also I won’t show the EhCache configuration, as it is easy and straightforward, neither will I discuss the EhCache choice.

Finally, I would say that OSWorkflow is so powerful and so flexible that it needs a certain learning time, and sometimes, source code analysis is a real need to have a deep understanding of the library. I personally think that the most important negative point of OSWorkflow is its documentation. Although the tutorial is rather well written, the wiki manual and the javadoc lack information and preciseness.

Tags: , , , ,

Laisser un commentaire

Security Code:


Tous les droits sont réservés pour IP-Tech