Showing posts with label bpmn. Show all posts
Showing posts with label bpmn. Show all posts

Monday, March 18, 2013

Porting Activiti Explorer to Liferay Portal

In the following blog post,  I have highlighted the matter of embedding Activiti BPMS inside a Liferay Portal. With Activiti Explorer built in Vaadin and Spring, it is fairly easy to use entire  components in a totally different environment. 

Proof of concept fork with Tasks and Admin portlets is available in 
https://github.com/tlipski/Activiti/tree/master/modules/activiti-portlets.

Porting Activiti Explorer to Liferay Portal

Activiti is a popular Open Source, BPMN2.0-compliant BPMS that provides a great overall package. Not only there is a library that can be embedded inside your application, but Activiti also comes with an Explorer web application, which provides an ergonomic user interface for basic operations:



But, Activiti is "just" a BPMS - you can run your processes with it, maybe even connect to Active Directory, Alfresco or ESB - but if you want to embed it in your intranet/extranet portal, you are almost on your own. 

Please note, that embedding Activiti in portal is not the same as using it to manage portal assets - for example, as a replacement for Liferay's Kaleo Workflow. The matter discussed here is more focused on using all of Activiti features, including user interface.

Luckily, this is when frameworks used in Activiti pay off - Activiti Explorer is written in Vaadin 6 and Spring. With component oriented approach from Spring and UI modularity encouraged by Vaadin, it is fairly easy to port this application to Liferay. We can extract entire components from Activiti Explorer (e.g. Tasks panel) and use them in a whole new environment.
Extracting and re-using entire components requires much more work in a classical MVC approach and even may not always be possible. Also, portlet support in Vaadin makes a whole task much more pleasant.

For the impatient: The code is available in my fork of Activiti 5.13-SNAPSHOT: http://github.com/tlipski/Activiti, especially in activiti-portlets module https://github.com/tlipski/Activiti/tree/master/modules/activiti-portlets.

Necessary steps

"Fairly easy" does not mean without any effort. There are differences between a standard Vaadin application and a portletized one:
  • Portlet descriptors are necessary
  • GUI should be divided into separate portlets
  • Spring-Vaadin integration is different due to a portal-specific request life-cycle
  • Navigation can be provided by portal - including friendly URLs, which makes for a more standard user experience.
In addition to that, some mechanisms used by Activiti are already provided by Liferay Portal and we need to bridge them:
  • Users and Groups should be managed by Liferay Portal
  • Authentication data should be taken from a portlet container
  • Mail notifications could possibly use Liferay Mail API
Some other things have to be taken into account as well:
  • Distribution and management of Vaadin widgetset and version - we should use Vaadin Control Panel portlet and Liferay to manage Vaadin dependencies. This requires special handling, since Activiti Explorer 5.13 utilizes dCharts Vaadin Add-on.
  • Vaadin theme used in portlets - which is also customized by Activiti, but should be coherent with portal's look & feel.

Current state of work

It is nice to theorize, but certain things need to be verified in practice. At this moment, my fork at http://github.com/tlipski/Activiti provides:
  • Activiti Tasks portlet, which works in a user's context as if the user would log in to Activiti Explorer app. The portlet provides full functionality, including operations as starting a new task, fetching it from a task list, displaying task's events, assignment/ownership transfers, file attachments and so on.
  • Activiti Admin portlet in Liferay Control Panel - most tabs seem to work fine - except Users and Groups, which should be disabled as we are using Liferay Portal to do that.




Implementation details:

On the implementation side, most noteworthy changes which have been implemented in activiti-portlets module: https://github.com/tlipski/Activiti/tree/master/modules/activiti-portlets are:
  • IdentityProvider implementation utilizing Liferay API (need to work on user photos though!)
  • Spring-Vaadin bridging for portal (see my previous post).
  • Custom portlet applications and main Window class. The interesting detail here is that with Vaadin portlets, you should not set width of components to 100%, as it will result in 0px height. Generally speaking, the height in Vaadin portlet components should be set to undefined - null, or a preset value in pixels.

Next steps?

Connecting Liferay with Activiti brings a myriad of new possibilities, but before that, some basic things need to be done:
  • More portlets! Reporting and Processes tabs need their respective portlets, but also "start process" portlet might be worth considering since we can manage UI contents in a more flexible way.
  • More testing! If anyone is interested in testing Activiti portlets in Liferay, let me know and I will provide prebuilt applications. So far, I've used Liferay 6.1 GA2 CE and H2 in-memory database for Activiti.
  • Friendly urls - especially for tasks resolved by id, and for interportlet communication - e.g. when starting a process.

Sunday, February 24, 2013

Integrating Spring, Vaadin & Liferay

As there are numerous materials on how to integrate Vaadin with Spring, Spring with Liferay (using portlet dispatcher) and Vaadin with Liferay, the integration of all three technologies at once is not documented well.
In such approach, there are two major differences between Vaadin with Spring in a standard web application and portlets-based one. This post is based on Vaadin 6, but the rules itself apply to Vaadin 7 as well.

The setup

The solution requires that we will overwrite two methods defined in com​.vaadin​.terminal​.gwt​.server​.AbstractApplicationPortlet2.
To achieve that, we have to create our own subclass:
package com.tomeklipski.blog.sample.vaadin;

import com.vaadin.Application;
import com.vaadin.terminal.gwt.server.ApplicationPortlet2;

import javax.portlet.*;
import java.io.IOException;

/**
 * Created with IntelliJ IDEA.
 *
 * @author tomek@lipski.net.pl
 *         Date: 2/23/13 4:06 PM
 */
public class MyApplicationPortlet extends ApplicationPortlet2 {

/* overwritten methods here */

}
The newly created class has to be used in web.xml:
<servlet>
    <servlet-name>VaadinPortletServlet</servlet-name>
    <servlet-class>com.liferay.portal.kernel.servlet.PortletServlet</servlet-class>
    <init-param>
        <param-name>portlet-class</param-name>
        <param-value>com.tomeklipski.blog.sample.vaadin.MyApplicationPortlet</param-value>
    </init-param>
    <load-on-startup>0</load-on-startup>
</servlet>
And in portlet.xml as well:
<portlet>
    <portlet-name>MyPortlet</portlet-name>
    <display-name>My Sample Portlet</display-name>
    <portlet-class>com.tomeklipski.blog.sample.vaadin.MyApplicationPortlet</portlet-class>

    <init-param>
        <name>application</name>
        <value>com.tomeklipski.sample.vaadin.SomeApplication</value>
    </init-param>
    <init-param>
        <name>widgetset</name>
        <value>com.vaadin.portal.gwt.PortalDefaultWidgetSet</value>
    </init-param>
    <supports>
        <mime-type>text/html</mime-type>
        <portlet-mode>view</portlet-mode>
    </supports>
    <portlet-info>
        <title>My Sample Portlet</title>
        <short-title>Sample</short-title>
    </portlet-info>
</portlet>

Accessing Spring context in portlet

First of all - portlets are configured using listeners as well. Liferay adds its listeners to web.xml during deploy, so listeners in web.xml might look like this:
<listener>
    <listener-class>
        com.liferay.portal.kernel.servlet.PluginContextListener
    </listener-class>
</listener>
<listener>
    <listener-class>
        com.liferay.portal.kernel.servlet.SerializableSessionAttributeListener
    </listener-class>
</listener>
<listener>
    <listener-class>
        org.springframework.web.context.ContextLoaderListener       
    </listener-class>
</listener>
<listener>
    <listener-class>
        org.springframework.web.context.request.RequestContextListener
    </listener-class>
</listener>
<listener>
    <listener-class>
        com.liferay.portal.kernel.servlet.PortletContextListener
        </listener-class>
</listener>


As you can see, they are all mixed up and we cannot rely that during invocation of method javax.portlet.Portlet.init(PortletConfig config) the spring context is available.
If we want to refer to our application as a Spring bean, we have to overwrite method com​.vaadin​.terminal​.gwt​.server​.AbstractApplicationPortlet​.getNewApplication​(​PortletRequest request), and attempt to retrieve Spring context here:
@Override
protected Application getNewApplication(PortletRequest request) {
    PortletContext portletContext = getPortletContext();
    ApplicationContext webApplicationContext
            = PortletApplicationContextUtils.getWebApplicationContext(portletContext);
    Application app = (Application) webApplicationContext.getBean("app");
    return app;
}
The fetching of portlet and web application contexts can of course be optimized.

Accessing "session" scope

The "session" scope in Spring is a convient way to store objects related to a session, for example:
<bean name="app" class="com.tomeklipski.sample.vaadin.SomeApplication" scope="session">
    <property name="dataManager" ref="dataManager" />
    <property name="i18nManager" ref="i18nManager" />
</bean>
In standard web application, we can enable it using HttpRequestListener in web.xml:
<listener>
    <listener-class>
        org.springframework.web.context.request.RequestContextListener
    </listener-class>
</listener>
But, in JSR286 portlet, the listener is not invoked as it should and portlet specification does not provide the ability to add portlet request listeners as above.
Luckily, we can overwrite the method com​.vaadin​.terminal​.gwt​.server​.AbstractApplicationPortlet​.handleRequest​(​PortletRequest request, PortletResponse response) with a code, that will set appropriate ThreadContext values:
@Override
protected void handleRequest(PortletRequest request,
                             PortletResponse response)
        throws PortletException, IOException {
    LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
    LocaleContextHolder.setLocaleContext(
            new SimpleLocaleContext(request.getLocale()), false);

    // Expose current RequestAttributes to current thread.
    RequestAttributes previousRequestAttributes = RequestContextHolder
            .getRequestAttributes();
    PortletRequestAttributes requestAttributes = null;
    if (previousRequestAttributes == null ||
            previousRequestAttributes.getClass()
                    .equals(PortletRequestAttributes.class)) {
        requestAttributes = new PortletRequestAttributes(request);
        RequestContextHolder.setRequestAttributes(requestAttributes,
                false);
    }

    try {
        super.handleRequest(request, response);
    } finally {
        LocaleContextHolder.setLocaleContext(previousLocaleContext,
                false);
        if (requestAttributes != null) {
            RequestContextHolder.setRequestAttributes(
                    previousRequestAttributes, false);
            requestAttributes.requestCompleted();
        }
    }
}

Summary

It is fairly easy to use Spring in Vaadin portlets - but there are some differences between standard Vaadin-Spring application approach and portletized one. We can easily overcome them by overwriting two methods in com​.vaadin​.terminal​.gwt​.server​.AbstractApplicationPortlet.

Monday, April 23, 2012

Hacking Activiti BPM engine: how to use custom MyBatis queries

The issue

One big change that Activiti has introduced over jBPMv4 was to forego Hibernate ORM and use MyBatis. MyBatis is simpler and cleaner to use, but - it does not provide simple interface to perform queries that are not defined in annotations or XML files.

And even though Activiti provides great query interface, so you can do things like this:

List<Task> tasks = getProcessEngine().getTaskService().createTaskQuery()  
    .taskName(taskName)  
    .executionId(taskExecutionId)  
    .taskAssignee(user.getLogin())  
    .listPage(0, 1);

with relative ease, there are still some limitations to the API provided by Activiti Service classes (e.g. you cannot add IN clause for task assignees).

Possible solutions

One thing that you can do, is to just skip Activiti interface, and access database directly. Surely it will work, but such approach has many downsides - you will be fetching information about Activiti processes in two different ways, you will have to map data to Activiti objects yourself, and so on.

What we have managed to do for Aperte Workflow <-> Activiti integration (in version 2.0, where API is a bit more demanding), is indeed a hack. It is not as elegant as constructing query for jBPM, but still is quite simple and manageable. It propably can be done with Spring in a similiar fashion, or with SelectBuilder - but the principle is similiar.

The hack

1. Enhance Activiti's MyBatis mapping file - by adding our own version in a different package.

The enhanced file looks almost like the original - with one exception:

<?xml version="1.0" encoding="UTF-8"?>  
    
 <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" 
  "http://mybatis.org/dtd/mybatis-3-config.dtd">  
    
 <configuration>  
 <settings>  
 <setting name="lazyLoadingEnabled" value="false" />  
 </settings>  
 <mappers>  
     <mapper resource="org/activiti/db/mapping/entity/Attachment.xml" />  
     <mapper resource="org/activiti/db/mapping/entity/Comment.xml" />  
     <mapper resource="org/activiti/db/mapping/entity/Deployment.xml" />  
     <mapper resource="org/activiti/db/mapping/entity/Execution.xml" />  
     <mapper resource="org/activiti/db/mapping/entity/Group.xml" />  
     <mapper resource="org/activiti/db/mapping/entity/HistoricActivityInstance.xml" />  
     <mapper resource="org/activiti/db/mapping/entity/HistoricDetail.xml" />  
     <mapper resource="org/activiti/db/mapping/entity/HistoricProcessInstance.xml" />  
     <mapper resource="org/activiti/db/mapping/entity/HistoricTaskInstance.xml" />  
     <mapper resource="org/activiti/db/mapping/entity/IdentityInfo.xml" />  
     <mapper resource="org/activiti/db/mapping/entity/IdentityLink.xml" />  
     <mapper resource="org/activiti/db/mapping/entity/Job.xml" />  
     <mapper resource="org/activiti/db/mapping/entity/Membership.xml" />  
     <mapper resource="org/activiti/db/mapping/entity/ProcessDefinition.xml" />  
     <mapper resource="org/activiti/db/mapping/entity/Property.xml" />  
     <mapper resource="org/activiti/db/mapping/entity/Resource.xml" />  
     <mapper resource="org/activiti/db/mapping/entity/TableData.xml" />  
     <mapper resource="org/activiti/db/mapping/entity/Task.xml" />  
     <mapper resource="org/activiti/db/mapping/entity/User.xml" />  
     <mapper resource="org/activiti/db/mapping/entity/VariableInstance.xml" />  
     <mapper resource="org/aperteworkflow/ext/activiti/mybatis/Task-enhanced.xml" />  
 </mappers>  
 </configuration>

And the addition is:

<mapper resource="org/aperteworkflow/ext/activiti/mybatis/Task-enhanced.xml" />

2. Create enhanced mapper resource

This mapper resource add additional query conditions to the initial task query. The beginning of it is the same as original, but at the end we have added support for in clauses on several fields:

<!-- new conditions -->  
                 <if test="owners != null && owners.size() > 0">  
                     and T.OWNER_ IN  
                     <foreach item="owner" index="index" collection="owners"  
                              open="(" separator="," close=")">  
                         #{owner}  
                     </foreach>  
                 </if>  
                 <if test="notOwners != null && notOwners.size() > 0">  
                     and T.OWNER_ NOT IN  
                     <foreach item="owner" index="index" collection="notOwners"  
                              open="(" separator="," close=")">  
                         #{owner}  
                     </foreach>  
                 </if>  
                 <if test="groups != null && groups.size() > 0">  
                     and T.ASSIGNEE_ is null  
                     and I.TYPE_ = 'candidate'  
                     and I.GROUP_ID_ IN  
                     <foreach item="group" index="index" collection="groups"  
                              open="(" separator="," close=")">  
                         #{group}  
                     </foreach>  
                 </if>  
                 <if test="taskNames != null && taskNames.size() > 0">  
                     and T.NAME_ IN  
                     <foreach item="name" index="index" collection="taskNames"  
                              open="(" separator="," close=")">  
                         #{name}  
                     </foreach>  
                 </if>  
             </foreach>  
         </where>  
     </sql>  

The entire mapper file can be downloaded here from Aperte Workflow's github repository - core/activiti-context/src/main/resources/org/aperteworkflow/ext/activiti/mybatis/Task-enhanced.xml.

3. Introduce new configuration to MyBatis

As MyBatis runs inside of Activiti internals, we have to access and alter Activiti configuration mechanisms. To do that, we simply override one of the methods with our copy (which points to a new mapping file):

 public class CustomStandaloneProcessEngineConfiguration 
           extends StandaloneProcessEngineConfiguration {  
    
         @Override  
         protected void initSqlSessionFactory() {  
             if (sqlSessionFactory == null) {  
                 InputStream inputStream = null;  
                 try {  
                     inputStream = ReflectUtil.getResourceAsStream(
                       "org/aperteworkflow/ext/activiti/mybatis/mappings-enhanced.xml");  
    
                     // update the jdbc parameters to the configured ones...  
                     Environment environment = new Environment("default", transactionFactory, 
                                                               dataSource);  
                     Reader reader = new InputStreamReader(inputStream);  
                     XMLConfigBuilder parser = new XMLConfigBuilder(reader);  
                     Configuration configuration = parser.getConfiguration();  
                     configuration.setEnvironment(environment);  
                     configuration.getTypeHandlerRegistry().register(VariableType.class, 
                             JdbcType.VARCHAR,  
                             new IbatisVariableTypeHandler());  
                     configuration = parser.parse();  
    
                     sqlSessionFactory = new DefaultSqlSessionFactory(configuration);  
    
                 } catch (Exception e) {  
                     throw new ActivitiException(
                      "Error while building ibatis SqlSessionFactory: " + e.getMessage(), e);  
                 } finally {  
                     IoUtil.closeSilently(inputStream);  
                 }  
             }  
         }  
     }  

You can of course do lots of amazing customizations here. And use this new class for Activiti BPM engine initialization:

CustomStandaloneProcessEngineConfiguration customStandaloneProcessEngineConfiguration =   
         new CustomStandaloneProcessEngineConfiguration();  
         customStandaloneProcessEngineConfiguration  
         .setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE)  
         .setDataSource(getDataSourceWrapper(sess))  
         .setHistory(ProcessEngineConfiguration.HISTORY_FULL)  
         .setTransactionsExternallyManaged(true); 

4. Enhance query object

Activiti uses wonderful, clean and simple query objects, that provide structure to MyBatis query parameters. To introduce new parameters, all we have to do, is to enhance TaskQueryImpl class with our own:

 package org.aperteworkflow.ext.activiti.mybatis;  

 import org.activiti.engine.impl.TaskQueryImpl;  
 import java.util.HashSet;  
 import java.util.Set;  
 /** 
  * @author tlipski@bluesoft.net.pl 
  */  
 public class TaskQueryImplEnhanced extends TaskQueryImpl {  
     private Set<String> creators = new HashSet<String>();  
     private Set<String> owners = new HashSet<String>();  
     private Set<String> groups = new HashSet<String>();  
     private Set<String> notOwners = new HashSet<String>();  
     private Set<String> taskNames = new HashSet<String>();  
    
     public Set<String> getGroups() {  
         return groups;  
     }  
    
     public Set<String> getNotOwners() {  
         return notOwners;  
     }  
    
     public Set<String> getOwners() {  
         return owners;  
     }  
    
     public Set<String> getCreators() {  
         return owners;  
     }  
    
     public Set<String> getTaskNames() {  
         return taskNames;  
     }  
    
     public TaskQueryImplEnhanced addTaskName(String name) {  
         taskNames.add(name);  
         return this;  
     }  
     public TaskQueryImplEnhanced addOwner(String login) {  
         owners.add(login);  
         return this;  
     }  
    
     public TaskQueryImplEnhanced addGroup(String name) {  
         groups.add(name);  
         return this;  
     }  
    
     public TaskQueryImplEnhanced addNotOwner(String login) {  
         notOwners.add(login);  
         return this;  
     }  
    
     public TaskQueryImplEnhanced addCreator(String login) {  
         creators.add(login);  
         return this;  
     }  
 }  

5. Invoke new query

And finally, we can invoke our new query with multiple assignees and other custom where clauses:

final TaskQueryImplEnhanced q = new TaskQueryImplEnhanced();  
 for (UserData u : filter.getOwners()) {  
     q.addOwner(u.getLogin());  
 }  
 for (UserData u : filter.getCreators()) {  
     q.addCreator(u.getLogin());  
 }  
 for (UserData u : filter.getNotOwners()) {  
     q.addNotOwner(u.getLogin());  
 }  
 for (String qn : filter.getQueues()) {  
     q.addGroup(qn);  
 }  
    
 ActivitiContextFactoryImpl.CustomStandaloneProcessEngineConfiguration 
   processEngineConfiguration = getProcessEngineConfiguration();  
 CommandExecutor commandExecutorTxRequired = processEngineConfiguration
   .getCommandExecutorTxRequired();  
 List<Task> tasks = commandExecutorTxRequired.execute(
   new Command<List<Task>>() {  
     @Override  
     public List<Task> execute(CommandContext commandContext) {  
        return commandContext.getDbSqlSession()
         .selectList("selectTaskByQueryCriteria_Enhanced", q);  
     }  
 });  

Please note, that we are also exposing CommandExecutor object instance to access DbSqlSession.

Summary

The technique presented here would be unnecessary or much simpler if Activiti would provide external means to configure MyBatis. Maybe that is a thing, that will be available in the future versions of Activiti.

Still, even at this moment, it is fairly easy to enhance/alter Activiti's behaviour. I haven't seen any final classes (a common sight in Hibernate - e.g. org.hibernate.impl.SessionImpl) and the internals of Activiti are quite simple to understand.

Wednesday, April 18, 2012

Aperte Workflow - why another BPMS?

What is Aperte Workflow?

Aperte Workflow is a BPMS (Business Process Management Suite) designed and developed by BlueSoft. The project has started in 2011 and is currently approaching version 2.0.

BlueSoft is an Independent Software Vendor specializing in custom, tailor-made IT solutions for the enterprise customers - based either on Open Source or proprietary software.

The company itself comes with over 9 years of experience in developing such solutions, which gives a certain perspective on how things should be done. But still, young enough to be able to innovate effectively.

Why build another Open Source BPM solution?

That is a good question, and we ask it ourselves every time we forego existing, available technology and start creating something new. 

With Open Source BPMs and Aperte Workflow, the decision came from a simple observation: that every time you need to deploy a BPM-based solution (be it Open Source or commercial software) in a real application, which provides real processes, used by real users, you need to build an application around it.

You have to provide at least:

  • Integration with Customer's authentication/authorization mechanisms.
  • Custom User Interface. Forms with text, number and date field are great, but usually the business requirements are much more complicated. 
  • Integration on the browser-level with Customer's intranet portal (desktop based BPMS in my opinion are a total misunderstanding) - or some kind of base for BPM application.
  • Enterprise integration with the Customer's other IT systems (CRM, ERP, ...).
  • Flexible data model

And yes, it is possible to build such solution yourself. Integrate existing technologies into one solution, learn how to use them, find bugs in your code and in the code of the technologies you are using - and then, finally you can start developing your processes. 

This is exactly what Aperte Workflow provides - a ready-to-use, well-tested and mature product, which provides a platform or a placeholder for your processes and custom mechanisms. Done right, with reusability, scalability and performance in mind.

We do not aim to it be a complete solution for your business process (although it is very easy to build one with Aperte Workflow!) - we understand, that every process in every company can be a little different. But, there are some things that have to be done for every BPM deployment, so why not do them once and right?

For us, Aperte Workflow is different from the other solutions, as it approaches problems on a different level. We do not try to provide an answer to every problem that rises during BPM deployment in an organization - but rather to provide means, that the solution will be worked out easily and will be coherent with other parts of the deployment and will be re-usable in other areas.

What Aperte Workflow provides?

Aperte Workflow provides common things that usually have to be built when deploying BPM in an organization. But, since this is a standalone product, even more features, that wouldn't be usually implemented in a custom BPM deployment are provided.

Portal Integration

Aperte Workflow integrates with JSR168/286 compliant portals (e.g. Liferay). By doing so, you do not only have an awesome integration with existing portal or enterprise IT infrastructure (e.g. Active Directory, NTLM, ...), but you also have a place to introduce and integrate other applications.

Using JSR168/286 allows you to standarize and unify all your browser-based applications - into a one, coherent intranet portal. 

Custom User Interfaces

With Aperte Workflow, user interface for each human task (on the task list or in details) is built from re-usable components. These components, provided as plugins, can provide:

  • all-round, generic features, such as simple input form or a tabsheet organizing other components
  • specific features - usable in most of the processes, such as comment form or process history log
  • custom business logic - related to a certain process or organization, a CRM customer search form would be an example for that.

The UI is provided by Vaadin, therefore its customization requires no HTML/CSS skills - it is programmed directly in Java. And with Vaadin, we can build beautiful, rich web interfaces with ease.

Enterprise integration

Aperte Workflow allows users to build automated steps as plugins. As with User Interface, some of them can be quite generic (e.g. invoke Drools), others - very specific. All of them can be reused in different processes without any trouble.

Also, Aperte Workflow comes with a sample plugin, that embeddes Mule ESB as an OSGi service. Other plugins can deploy simple Mule services, and processes can invoke them using Mule automated step. But - this is just a convienent sample of integration mechanisms and if for example ServiceMix would to be embedded as an integrated ESB - why not?

Extensible data model 

Aperte Workflow's data model is built with JPA/Hibernate. That grants us significant portability with little effort, but also allows the plugins to provide their own data model without performance downgrade - the data model is mapped directly to SQL database. 

BPMN2.0 modeler

When designing BPM system from scratch, around existing BPM engine or solution, you usually have to configure process in two places - one for the BPM engine and the other for your customizations. Rarely you would take the effort to create a graphical, coherent tool to do that. 

But, as Aperte Workflow is designed for more than one deployment, such effort pays off. That's why we have taken Signavio Core Components BPMN2.0 editor and combined it with our configuration controls into one, complete, browser-based process modeler.

How Aperte Workflow is built?

We have decided to build Aperte Workflow on top of existing, Open Source technologies. That not only saves us from the trouble of building everything from scratch, but also provides other, even more significant advantages:

  • Maturity from the start. We don't have to test, mature and grow our components. They are already checked by thousands of their users and built around their feedback.
  • Much easier to learn. It is much easier to start using or diagnose a problem with a solution built from technologies you are already familiar with, or that have a thriving community around them.
  • New features. If you are using your own software, there is a great chance, that all the features have to analyzed, provided, tested and checked by you. 

Aperte Workflow is built from many Open Source libraries and frameworks, to mention only the most significant:

Create once, use many times

Aperte Workflow is great when it comes to building reusable components. Yes, many processes may differ, but building blocks for them can be re-used many times. Some of them, such as simple form displaying/editing process attributes or comment input form can be universal across the whole world, other - can be universal only in a specific organization - for example CRM customer search form would be applicable to many processes but only in current organization.

As Aperte Workflow is created in Java(tm) technology, the natural and simplest way to provide plugin mechanisms is to use an OSGi framework. OSGi is great, because it provides a standarized, well-thought way to:

  • isolate each plugin within its classloader space - with its own libraries and dependencies
  • provide a means for plugins to communicate with each other
  • manage dependencies between plugins

With Aperte Workflow, reusability is provided on many levels, in compliance with the spirit of Service Oriented Architecture. Aperte Workflow services (bundled in OSGi plugins) can provide for example:

  • user interface components
  • automated steps performing business logic or integration
  • localization (I18N)
  • custom mechanisms that access Aperte Workflow API - e.g. start processes or provide embedded ESB for processes
  • process dictionaries

and many more..