“Less-Code BPM” with camunda fox server, Activiti and JBoss AS 7

September 28 2011 by Daniel Meyer · 4 Comments

camunda fox icon

the brand new camunda fox icon

Yes you have read correctly: “less-code”. At camunda we are pretty sure that there are large classes of business process that cannot be implemented in a zero-code fashion (i.e. without having to write custom implementation code). So if we are taking away “zero-code”, but what we can do is offer “less-code” :)

Our experience in numerous process implementation projects has shown that one of the largest challenges in BPM, is defining and setting up the infrastructure. Getting integration right is extremely hard and mostly about nasty stuff like deployment, classloading, transactions, versioning, etc. So in order to be able to implement “features” or “business logic” (whatever you want to call it), you have to solve a lot of technical problems. But what if I said, that this is all in the past? What if I said that you could deploy a simple .jar file containing a process diagram and a couple of services and that’s it? And classloading, transactions and stuff like @Inject ProcessEngine just “magically” works? You would read the rest of this blog post, now, wouldn’t you? :)

Haha, got you, you read on! But sadly there is no magic – just great technology :) . There are a lot of challenges left, but I think that we have come up with a pretty cool infrastructure addressing the most common infrastructural problems in technical BPM. So let’s have a look!

The camunda fox server is about the following things:

Make sure not to miss the screencasts:

Process Engine as a Service

The first concept I want to discuss is something I call “Process Engine as a Service” and it is about how we can provide activiti as a BPM service to applications just as we provide for instance Hibernate as a persistence service. So basically if I am developing an application, at some point I might decide that I need BPM. What I do then is I start looking for process engines and (hopefully) find out that activiti is the best process engine out there. Ok, great, now I am starting to learn how to configure and use activiti, and most of the time this opens up a can of worms, about transactions, classloading, process versioning etc.

What we wanted to achieve in camunda fox server is different. We wanted to offer you a possibility to use the process engine as a service, without having you set it up and configure it yourselves. To achieve this I developed a Jboss Application Server 7 subsystem which is responsible for controlling the activiti process engine inside Jboss AS. This allows you to use activiti as a container-managed service just like JPA, JTA and the rest. Concretely speaking, this means that you can define and configure a process engine not in a configuration file that is local to your application but in a central configuration file of the application server and then also share a process engine in different applications.

In the Jboss AS 7 standalone.xml configuration file you can configure a process engine in the following way:

<subsystem xmlns="urn:com.camunda.fox.fox-engine:1.0">
            <process-engines>
                <process-engine name="default" datasource="java:jboss/datasources/ExampleDS" history-level="audit" db-schema-update="true"/>
            </process-engines>
</subsystem>

When you now start the server you will see the following log entries:

We can see that we are starting a process engine named „default“ and binding it in JNDI. I explain this in some more detail in the accompanying screencast.

In addition I can also use jboss management tools for inspecting the process engine configuration and even start or stop process engines at runtime. So using the jboss-admin console I can log into the fox server (potentially from a remote host) and inspect the configuration. I can then also start a new process engine or take down an existing one. Such changes can be rolled out cluster-wide. This is discussed in greater detail in the screencast.

But this is not actually “zero-configuration”, now is it? Well obviously we have to declare somewhere which engine(s) we want to start and which datasource(s) they shall be pointed to. But what is different here is that we do not have to think about purely technical stuff like transactions and classloading. So its about providing you with configuration options which “actually mean something”.

Introducing: Process Archives

In the second part of this post I discuss how we can access the process engine service. To this end, I want to introduce a term I have coined: the “process archive”. A process archive can be any Java EE module (jar/war/ear) which carries a special marker file named META-INF/processes.xml. You can compare this file to a persistence.xml or a beans.xml file in which you declare which JPA persistence services you want to use or in which you configure CDI. So while a beans.xml file turns a deployment into a bean archive, a processes.xml file turns it into a process archive.

Process archive are provided with certain services:

First, the activiti engine module is added to the classpath of a process archive. So for example if you have a jar file containing a bpmn20.xml file and a couple of Java Delegate classes, providing a META-INF/processes.xml file, activiti-engine is automatically added to the classpath of that jar. (In Jboss AS 7 this is called an “implicit module dependency”.) This is also true for .war files, so if we have a web application which uses activiti, we do not have to bundle activiti as a library in the WEB-INF/lib folder.

Second, the deployment is scanned for files ending in .bpmn20.xml which are subsequently deployed to the process engine. So for each process archive we create an activiti engine deployment containing the processes provided in the process archive. In the processes.xml file we can also declare which engine we want to deploy to. Here too we can control what should happen on undeployment, so if the .war file containing a certain process is removed, whether we then should also delete the corresponding engine deployment or not.

And third, if the process archive is also a CDI deployment, activiti CDI is added as a module dependency and booted. What this gives us is the simplest possible way to accessing a process engine from a process archive: we simply write

@Inject ProcessEngine processEngine;

and that’s it. In addition this allows me to resolve CDI beans from a bpmn process and do everything else activiti cdi offers me.

To sum this up:

  • A process archive can be any module carrying a processes.xml marker file,
  • Process archives are scanned for files ending in .bpmn20.xml, which are deployed to the process engine,
  • Process archives can access the process engine either through CDI or look up the process engine from JNDI.

Time for an Example!

I think now it’s time for an example, a process where we twitter something. Let’s create a minimal process archive in this case a .jar file named twitter.jar. That process archive contains four files:

META-INF/processes.xml
META-INF/beans.xml
com/camunda/fox/demo/TwitterService.class
diagrams/twitter.bpmn20.xml

On the camunda fox server this is all I need in order to use activiti. The TwitterService.class looks like this:

@Named
public class TwitterService implements JavaDelegate {

  public void execute(DelegateExecution execution) throws Exception {
    System.out.println("Received a new tweet: "+execution.getVariable("message"));
  }

}

TwitterProcess.bpmn20.xml looks like this:

<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.signavio.com/bpmn20">
  <process id="twitter" name="twitter">
    <startEvent id="startevent1" name="Start">
      <extensionElements>
        <activiti:formProperty id="tweet" name="Tweet" type="string" required="true" readable="true" writable="true"></activiti:formProperty>
        <activiti:formProperty id="tweeter" name="Tweeter" type="string" required="true" readable="true" writable="true"></activiti:formProperty>
      </extensionElements>
    </startEvent>
    <userTask id="usertask1" name="review tweet" activiti:assignee="kermit">
      <extensionElements>
        <activiti:formProperty id="tweet" name="Tweet" type="string" required="false" readable="true" writable="false"></activiti:formProperty>
        <activiti:formProperty id="approved" name="Approved" type="boolean" required="true" readable="true" writable="true"></activiti:formProperty>
      </extensionElements>
    </userTask>
    <exclusiveGateway id="exclusivegateway1" name="approved?"></exclusiveGateway>
    <serviceTask id="servicetask1" name="publish" activiti:delegateExpression="#{twitterService}"></serviceTask>
    <endEvent id="endevent1" name="End"></endEvent>
    <sequenceFlow id="flow1" name="" sourceRef="startevent1" targetRef="usertask1"></sequenceFlow>
    <sequenceFlow id="flow2" name="" sourceRef="usertask1" targetRef="exclusivegateway1"></sequenceFlow>
    <sequenceFlow id="flow3" name="" sourceRef="exclusivegateway1" targetRef="servicetask1">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[#{approved}]]></conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="flow4" name="" sourceRef="exclusivegateway2" targetRef="endevent1"></sequenceFlow>
    <exclusiveGateway id="exclusivegateway2" name="Exclusive Gateway"></exclusiveGateway>
    <sequenceFlow id="flow6" name="" sourceRef="servicetask1" targetRef="exclusivegateway2"></sequenceFlow>
    <sequenceFlow id="flow7" name="" sourceRef="exclusivegateway1" targetRef="exclusivegateway2">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[#{not approved}]]></conditionExpression>
    </sequenceFlow>
  </process>
</definitions>

A very simple process. Note how the CDI bean is called from the process using a delegate expression in the ServiceTask.

If we deploy this jar to the fox server, it is recognized as a process archive and provided with BPM services. Make sure to catch the second part of the accompanying screencast where I go into this in more detail.

What is now interesting is that I can configure the activiti explorer to use the same process engine as the process archive. This allows me to start the process and complete user tasks using the activiti explorer but resolve delegate classes and beans from the process archive (this is also demonstrated in the second screencast).

The full Power of Java EE 6

To make this more interesting, let’s slightly change the process: in the new version we are going to persist accepted tweets to a database, so that we track them and do all kind of interesting analytics stuff later :)

To achieve this I need to change three things:

  • I am adding a file named “persistence.xml” to the META-INF directory in which I configure a persistence unit:
    <?xml version="1.0" encoding="UTF-8"?>
    <persistence version="2.0"
       xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
       <persistence-unit name="primary">
          <jta-data-source>java:jboss/datasources/ExampleDS</jta-data-source>
          <properties>
             <property name="hibernate.hbm2ddl.auto" value="update" />
             <property name="hibernate.show_sql" value="false" />
          </properties>
       </persistence-unit>
    </persistence>
    

    In this file I am basically stating that I want to use a datasource named “ExampleDS”.

  • I am adding an entity named TweetEntity:
    @Entity
    public class TweetEntity {
    
      @Id
      @GeneratedValue
      private Long id;
    
      @Version
      private Long version;
    
      private String content;
    
      private String tweeter;
    // + getters & setters ...
    }
    
  • I am changing the TwitterService into an EJB:

    @Named
    @Stateless
    public class TwitterService implements JavaDelegate {
    
      @PersistenceContext
      private EntityManager entityManager;
    
      public void execute(DelegateExecution execution) throws Exception {
        System.out.println("Received a new tweet: "+execution.getVariable("tweet"));
        TweetEntity tweetEntity = new TweetEntity();
        tweetEntity.setContent((String) execution.getVariable("tweet"));
        tweetEntity.setTweeter((String) execution.getVariable("tweeter"));
        entityManager.persist(tweetEntity);
      }
    
    }
    

    Into the EJB we are injecting an entity manager (JPA) which we use to persist the tweet that is passed in. Note that this EJB implements JavaDelegate which is an activiti interface. We could also refactor this to not implementing this interface and passing in the process variables as arguments. In that case the service task in the bpmn process would look like this:

    <serviceTask id="servicetask1" name="publish" activiti:expression="#{twitterService.tweet(tweeter,tweet)}"></serviceTask>
    

    Now the TwitterService would not be aware that it is called from a business process:

    public void tweet(String tweeter, String tweet) throws Exception {
        System.out.println("Received a new tweet: "+ tweet);
        TweetEntity tweetEntity = new TweetEntity();
        tweetEntity.setContent(tweet);
        tweetEntity.setTweeter(tweeter);
        entityManager.persist(tweetEntity);
      }
    

This is just to show how “tight” the integration with Java EE 6 is: we can just use Java EE services in activiti delegate code. This helps us to keep process archives lean and clean of infrastructure code like a pure “delegate layer” where we lookup EJBs in activiti delegates. Now we can just lookup an EJB from the process and that’s it. This means that in process archives we have the full power of Java EE 6.

Great Testability

Now let’s say we want to test the twitter process. Traditionally what we would have to do now is set up a huge testing environment: setup EJB, CDI, JPA, activiti, configure them correctly, so that they share transactions and so on. This is a good example how you can bury weeks of manpower in infrastructure. So we are not going to do that, after all I want to finish this blogpost this evening. :)

What we are going to do is use Jboss Arquillian. Arquillian is a next generation testing framework that, instead of having us setup an application server in the unit test, is going to take the test to an existing application server. So concretely speaking, Arquillian is copying the testcase (and additional resources) to the camunda fox server (Jboss AS 7) and run it there.
In order to set this up, you have to add a couple of dependencies to the Maven pom-file:

    <dependencies>
	<dependency>
		<groupId>org.activiti</groupId>
		<artifactId>activiti-cdi</artifactId>
		<version>5.8-SNAPSHOT</version>
		<scope>provided</scope>
	</dependency>
	<dependency>
		<groupId>junit</groupId>
		<artifactId>junit</artifactId>
		<version>4.8.2</version>
		<scope>test</scope>
	</dependency>
	<dependency>
		<groupId>org.jboss.arquillian.junit</groupId>
		<artifactId>arquillian-junit-container</artifactId>
		<version>1.0.0.CR4</version>
		<scope>test</scope>
	</dependency>
	<dependency>
		<groupId>org.jboss.arquillian.protocol</groupId>
		<artifactId>arquillian-protocol-servlet</artifactId>
		<version>1.0.0.CR4</version>
		<scope>provided</scope>
	</dependency>
	<dependency>
		<groupId>org.jboss.spec</groupId>
		<artifactId>jboss-javaee-web-6.0</artifactId>
		<version>2.0.0.Final</version>
		<type>pom</type>
		<scope>provided</scope>
		<exclusions>
			<exclusion>
				<artifactId>xalan</artifactId>
				<groupId>org.apache.xalan</groupId>
			</exclusion>
		</exclusions>
	</dependency>
</dependencies>

And then we can also add a profile for executing the tests in a remote application server:

<profile>
	<id>arq-jbossas-remote</id>
	<dependencies>
		<dependency>
			<groupId>org.jboss.as</groupId>
			<artifactId>jboss-as-arquillian-container-remote</artifactId>
			<version>7.0.1.Final</version>
			<scope>test</scope>
		</dependency>
	</dependencies>
</profile>

Now all I have to do is write that testcase:

@RunWith(Arquillian.class)
public class TestTwitterProcess {

  @Deployment
  public static JavaArchive createDeployment() {
    return ShrinkWrap.create(JavaArchive.class, "test.jar")
            .addClass(TwitterService.class)
            .addClass(TweetEntity.class)
            .addAsManifestResource("META-INF/persistence.xml", ArchivePaths.create("persistence.xml"))
            .addAsManifestResource("META-INF/processes.xml", ArchivePaths.create("processes.xml"))
            .addAsManifestResource("META-INF/beans.xml", ArchivePaths.create("beans.xml"))
            .addAsResource("twitter.bpmn20.xml");
  }

  @Inject private RuntimeService runtimeService;
  @Inject private TaskService taskService;

  @PersistenceContext
  private EntityManager entityManager;

  @Test
  public void testHappyPath() throws InterruptedException{
    Map processVariables = new HashMap();
    processVariables.put("tweet", "Camunda Fox rocks!");
    processVariables.put("tweeter", "Daniel Meyer");
    runtimeService.startProcessInstanceByKey("twitter", processVariables);

    Task task = taskService.createTaskQuery().singleResult();
    Assert.assertEquals(task.getName(), "review tweet");

    processVariables.clear();
    processVariables.put("approved", Boolean.TRUE);
    taskService.complete(task.getId(), processVariables);

    // now the process is ended
    Assert.assertNull(runtimeService.createProcessInstanceQuery().singleResult());
    // and a tweet is in the database:
    CriteriaBuilder cb = entityManager.getCriteriaBuilder();
    CriteriaQuery cq = cb.createQuery(TweetEntity.class);
    cq.from(TweetEntity.class);
    TweetEntity tweetEntity = entityManager.createQuery(cq)
      .getSingleResult();
    Assert.assertEquals("Daniel Meyer", tweetEntity.getTweeter());
    Assert.assertEquals("Camunda Fox rocks!", tweetEntity.getContent());
  }
}

In the Method annotated with @Deployment we are programmatically assembling a .jar file adding the necessary resources to it, so that Arquillian can copy it to the application server and run the test.
The cool thing about this test is,

  • We can access container managed services like JPA, EJB and also activiti.
  • We are executing the test in the actual same environment as the application is running in.
  • It costs us nothing :) So we do not have to setup that environment ourselves.

Because we have integrated activiti into the Jboss Application server the way we have, when this jar is deployed, the deployment infrastructure is activated and the twitter process is deployed to the activiti engine. This is demonstrated in the third part of the screencast.

Standing on the Shoulders of Giants

So I think that this infrastructure is actually pretty darn cool! I think what I have showcased here is really how the Java EE 6 platform can be extended to provide first class BPM services to applications. This extension is composed of two parts: integration with the Java EE 6 programming model (CDI) and integration with the technical platform (Jboss AS7).

The Java EE 6 programming model allows drastically reducing the semantic gap between specification and implementation. So even if there is no zero-code, we can reduce the amount of code necessary to implement a process solution. We do that by eliminating as much of purely technical, infrastructural problems as possible. In the environment I have presented here we can actually “get down to business”, so focus on features and not on infrastructure.

The other way that we have extended Java EE 6 is that we have extended the technical platform. So in contrast to other approaches like Spring, Java EE applications can rely on a specific environment being present when they are deployed. That environment is the application server. The burden of setting up and configuring that environment is taken off our shoulders so that we can focus on solving actual business problems. Here we have achieved the same thing for BPM. So we have integrated activiti in a way that we can use it as a service in our applications, so that when developing such an application we do not have to know how to correctly bundle and and setup a process engine. (Or if we do know this, we do not have to reproduce it all the time : )).

So now we have automatic deployment of resources, versioning and classloading, cdi integration and in-container testing with arquillian and it just works, and it works out of the box.

Obviously, to make this possible we are really standing on the shoulders of giants here: what we call the “camunda fox server” is really more than 90% Java EE6, Jboss and Activiti, and a small portion camunda fox (but wait for the sophisticated monitoring tooling we are currently adding). But I think the fact that you can add that in a way that it just naturally fits into the platform and everything plays together the way it does, really speaks for Java EE 6 as a programming model and Jboss AS7 and activiti as highly extensible frameworks you can build on!

Going beyond Enterprise Application Architecture (EAA)

In the introduction I boldly stated that this solves “all infrastructure problems in technical bpm”. Well ok granted: obviously not :) Let’s rephrase it like this: this solves basic infrastructure problems for a certain class of technical bpm projects. Which class is that?

I would say it’s the class of enterprise applications. So the class of applications at which Java EE is targeted: business applications which potentially have different user interfaces, that manage complex, persistent data in a transactional fashion and which implement complex business logic. This is the class of applications that’s targeted by Java EE and also the primary target of this infrastructure.

Now can you go beyond this? I already said that some projects are “larger” than Java EE. For example EAI is larger than Java EE. Java EE is about developing enterprise applications; EAI (Enterprise Application Integration) on the other hand is about integrating different enterprise applications. So there I have different challenges: while in an enterprise application my main challenge is to structure concurrent access to persistent data, EAI is about messaging, message transformation, routing and so on. So in an EAI-BPM project I have infrastructural needs like reliable message correlation which are not addressed by the infrastructure I have shown here.

On the other hand the infrastructure we are presenting is extremely flexible and can easily be enhanced or customized in different ways. If I am looking at this from a high level perspective, we basically have the possibility to define managed process engines in a container, and then add deployments of different sorts. So this allows different deployments to share a process engine which allows me to build highly modular applications. This is what we have in EAI environments like apache service mix, there we have an environment where we can deploy highly modular applications which collaboratively provide and use services. We can also realise architecture like that on top of Jboss AS 7, It has its own module layer and on top of that it is also OSGi compliant. And: it’s a full blown application server based on the Java EE standards!

Conclusion

Bottom line is that the camunda fox server is primarily targeted at enterprise applications and there it solves a lot of infrastructure problems and we finally have something we could call “zero-configuration” BPM. On top of that it might also provide the basic infrastructure for larger, more EAI style projects, but there it’s often not enough – which is good because we still have energy to tackle new problems :)

This concludes this blog post, I hope that you are as excited as we are! And as all of this its open source, so you go and get it!

4 Kommentare zu “Less-Code BPM” with camunda fox server, Activiti and JBoss AS 7

  1. REALLY really impressive stuff!

  2. Hello,

    I’ve been trying this with WebSphere 8.0 and activiti-5.8 SNAPSHOT, however the EntityManager is not injected into the Stateless bean. I have to look it up through JNDI to use it inside java delegates. Is this an issue known in some way?

    Thanks.

  3. Hello again,

    nevermind, I found out that for activiti-cdi to work correctly, one HAS to use one of the specific CDI-versions of the Process Engine Configurations, as otherwise the addtional ELResolver, etc. is not setup correctly. In my case choosing class=”org.activiti.cdi.CdiStandaloneProcessEngineConfiguration” worked perfectly :-)

    Nevertheless, thanks for the great work!

    Heiko

  4. Very interesting :-D ! But screencast #2 seems to be broken at ~10min… it turns to black until the end :-(

Schreiben Sie einen Kommentar

Powered by WP Hashcash