Services – mit jBPM6, JBoss, TDD

Sonntag, 25. Mai 2014 von  
unter Fachartikel Architektur

Die Motivation

Das Beispiel-Projekt mit jBPM 6 betrifft den Einsatz als Workflow Engine in einer simplen Web Applikation. Verwendung finden hier Runtime Manager mit verschiedenen Session Strategien unter Verwendung der Kie-Session, um Process Contexte besser zu trennen und um die Laufzeit-Performance besonders bei mehreren parallelen Prozessen zu verbessern. Die Idee hierzu entstand bei einem Blog-Eintrag über Hibernate 4, JBoss 7 und Vaadin 6 hier, wobei der Ansatz einer  proprietären State Machine zum Einsatz kam, und es wäre sicherlich eine interessante Übung, diese State Machine durch die entspechenden jBPM-Klassen mit den passenden Annotationen zu ersetzen.

Das Beispiel-Projekt

Dieser Blog-Eintrag basiert auf Beispielcodes aus den Open Source JBoss jBPM-Beispielen, die unter jboss.org erhältlich sind. Das hierbei verwendete Beispiel integriert sich durch den Einsatz von Context und Dependency Injection (CDI) gut mit Java EE6, was bekanntlich einige Vorteile hat. Es ist aber ebenso möglich, jBPM ohne die Verwendung von CDI und stattdessen mit simplen Java Konstruktoren oder Factories zu verwenden. Zum Thema JBoss CDI und Weld gibt es bereits einen Blog-Eintrag in diesem Blog hier.

Die Beispiel-Applikation behandelt einen simplen Business Process. Dieser Business Process, erzeugt einen Task für einen User und beendet diesen wieder.

Hier die ProcessExampleBean des EJB Process Services:

package de.binaris.jbpmexample.ejb;

import java.util.HashMap;
import java.util.Map;
 
import org.kie.api.runtime.KieSession;
import org.kie.api.runtime.manager.RuntimeEngine;
import org.kie.api.runtime.manager.RuntimeManager;
import org.kie.api.runtime.process.ProcessInstance;
import org.kie.internal.runtime.manager.context.EmptyContext;
import org.kie.internal.runtime.manager.cdi.qualifier.Singleton;
 
import javax.inject.Inject;
import javax.transaction.Status;
import javax.annotation.Resource;
import javax.annotation.PostConstruct;
import javax.transaction.UserTransaction;
 
import javax.ejb.Startup;
import javax.ejb.TransactionManagement;
import javax.ejb.TransactionManagementType;

@Startup
@javax.ejb.Singleton
@TransactionManagement(TransactionManagementType.BEAN)
public class ProcessExampleBean implements ProcessExampleLocal {
 
    @Resource
    private UserTransaction ut;
 
    @Inject
    @Singleton
    private RuntimeManager singletonRTM;
 
    @PostConstruct
    public void configure() {
        singletonRTM.toString();
    }
 
    public long startProcess(String recipient) throws Exception {
        RuntimeEngine runtime = singletonRTM.getRuntimeEngine(EmptyContext.get());
        KieSession ksession = runtime.getKieSession();
        long processInstanceId = -1;
 
        ut.begin();
        try {
            Map<String, Object> params = new HashMap<String, Object>();
            params.put(„recipient“, recipient);

            ProcessInstance processInstance = ksession.startProcess(
                    „de.binaris.jbpmexamples.example“, params);

            processInstanceId = processInstance.getId();
            System.out.println(„Process Instance Id: “ + processInstanceId);

            ut.commit();
        } catch (Exception e) {
            e.printStackTrace();
            if (ut.getStatus() == Status.STATUS_ACTIVE) {
                ut.rollback();
            }
            throw e;
        }
        return processInstanceId;
    }
}

Per Dependency Injection wird der RuntimeManager injectet, unabhängig davon, wie dieser erzeugt und initialisiert wurde (CDI):

@Inject
@Singleton
private RuntimeManager singletonRTM;

Die zweite Annotation @Singleton bedeutet einfach einen exklusiven RuntimeManager pro Session (“Singleton“-Design Pattern). Weitere Session Strategien sind

– @PerProcessInstance
– @PerRequest

@PerProcessInstance bedeutet jede Process Instanz verwendet ihren eigenen Session Context und Runtime Manager. @PerRequest bedeutet einfach zustandslos (“Stateless“), bei dem weder der  Session Context noch irgendein Zustand verwendbar ist, da der Runtime Manager nur für einen Request injectet wird.

Wie erwähnt, folgt der Runtime Manager einer Applikation üblicherweise dem “Singleton“ Design Pattern. Bei jedem Request kann man dann über den Runtime Manager eine Instanz der Runtime Engine erhalten.

Die Runtime Engine wiederum kapselt im Beispiel eine KieSession und einen Task Service. Mit dieser KieSession kann dann eine Process Instanz gestartet werden, was durch den Aufruf von startProcess(…) geschieht:

RuntimeEngine runtime = singletonManager.getRuntimeEngine(EmptyContext.get());
KieSession ksession = runtime.getKieSession();

ProcessInstance processInstance = ksession.startProcess(„de.binaris.jbpmexample“, params);

Der Task Service ist automatisch mit der KieSession verbunden und kann für Task-bezogene Abfragen und durch User-Aktionen bedingte Methodenaufrufe verwendet werden.

Hier die TaskExampleBean mit dem injecteten TaskService:

package de.binaris.jbpmexample.ejb;

import java.util.List; 
import javax.inject.Inject;
import javax.persistence.OptimisticLockException;
import javax.transaction.RollbackException;
import javax.transaction.Status;
import javax.transaction.UserTransaction;
 
import javax.annotation.Resource;
import javax.ejb.Stateless;
import javax.ejb.TransactionManagement;
import javax.ejb.TransactionManagementType;
 
import org.jbpm.services.task.exception.PermissionDeniedException;
import org.kie.api.task.TaskService;
import org.kie.api.task.model.TaskSummary;
 
@Stateless
@TransactionManagement(TransactionManagementType.BEAN)
public class TaskExampleBean implements TaskExampleLocal {
 
    @Resource
    private UserTransaction ut;
 
    @Inject
    TaskService taskService;
   
    public List<TaskSummary> retrieveTaskList(String ownerId) throws Exception {
 
        ut.begin();
        List<TaskSummary> list;
        try {
            list = taskService.getTasksAssignedAsPotentialOwner(ownerId, „de-DE“);
            ut.commit();
        } catch (RollbackException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
        System.out.println(„Task Liste fuer “ + ownerId + „:“);
        for (TaskSummary task : list) {
            System.out.println(“ task Id: “ + task.getId());
        }
        return list;
    }
 
    public void approveTask(String ownerId, long taskId) throws Exception {
        ut.begin();
        try {
            String msg = „approve task (id: „+taskeId +“, owner: „+ ownerId+“)“;
            System.out.println(msg);
            taskService.start(taskId, ownerId);
            taskService.complete(taskId, ownerId, null);
            ut.commit();
        } catch (RollbackException e) {
            e.printStackTrace();
            Throwable cause = e.getCause();
            if (cause != null && cause instanceof OptimisticLockException) {
                String errorMessage = „Konkurrierender Zugriff auf dieselbe „;
                errorMessage += „Process Instanz verursacht Verzögerung.“;
                throw new ProcessOperationException(errorMessage, e);
            }
            throw new RuntimeException(e);
        } catch (PermissionDeniedException e) {
            e.printStackTrace();
            // TaskServiceSession machte evtl. bereits den Transaction-Rollback
            if (ut.getStatus() == Status.STATUS_ACTIVE) {
                ut.rollback();
            }
            // Task wurde evtl. bereits von anderen Usern gestartet
            mString msg = „Task (id: „+taskeId+“) wurde evtl. „;
            msg += von anderen Usern gestartet“;
            throw new ProcessOperationException(msg e);
        } catch (Exception e) {
            e.printStackTrace();
            // TaskServiceSession machte evtl. bereits den Transaction-Rollback
            if (ut.getStatus() == Status.STATUS_ACTIVE) {
                ut.rollback();
            }
            throw new RuntimeException(e);
        }
    }
}

Hierbei wird einfach ein TaskService in die TaskExampleBean injectet. In jBPM5 war der TaskService jeweils entkoppelt von der ProcessEngine und musste für jeden Process mit der ProcessEngine verbunden werden, um User-bezogeneTasks auszuführen. In jBPM6 ist der TaskService sinnvollerweise wieder in der ProcessEngine integriert. Es bleibt dabei sehr simpel, eine eigene Service Methode zu implementieren und aufzurufen:

@Inject
TaskService taskService;
// […]
List list = taskService.getTasksAs
signedAsPotentialOwner(ownerId, „de-DE“);

Ein kurzer Hinweis noch zu den verwendeten UserTransactions, die optional verwendet werden können. Sie liefern vorteilhafterweise genau die Transaktion, welche JPA-Implementierungen, wie z.B. Hibernate, verwenden können, wenn innerhalb der ProcessEngine eine Task ausgeführt wird und Informationen (z.B. Task-bezogene Daten) in einer Unternehmens-Datenbank gespeichert werden sollen. Durch die Verwendung der Transaktionen kann sichergestellt werden, dass alle Persistenz-Operationen entweder committet werden oder per Rollback der Ausgangszustand der Daten vor der Transaktion wieder hergestellt wird.

Zu bemerken ist noch, dass sowohl die ProcessExampleBean, als auch die TaskExampleBean jeweils ihr EJB Local Interface “ProcessExampleLocal“ bzw. “TaskExampleLocal“ implementieren:

Hier das ProcessExampleLocal – Interface:

package de.binaris.jbpmexamples.ejb;
import javax.ejb.Local;
@Local
public interface ProcessExampleLocal {
    public long startProcess(String recipient) throws Exception;
}

Hier das TaskExampleLocal – Interface:

package de.binaris.jbpmexamples.ejb;
import java.util.List;
import javax.ejb.Local;
import org.kie.api.task.model.TaskSummary;
@Local
public interface TaskExampleLocal {
    public List<TaskSummary> retrieveTaskList(String ownerId) throws Exception;
    public void approveTask(String ownerId, long taskId) throws Exception;
}

Ein paar Bemerkungen noch zu den verschiedenen CDI Producer Klassen, die als konfigurierte Instanzen injectet werden können: Ein Vorteil von CDI besteht in der losen Kopplung (“loose coupling”) zwischen den injecteten Delegaten und den Consumer Beans (“Producer-Consumer“  Design Pattern). Beispielsweise wird ein Service in den Consumer Beans aufgerufen, der evtl. (temporär) nicht verfügbar ist. Hierbei kommen die Vorteile des TDD (Test-Driven Development) zum Einsatz, wobei z.B. mit EasyMock oder Mockito einfach ein Mock Service für die Unit Tests implementiert werden kann. Die alternative Mock Klasse “ServiceMock“ ersetzt dann in der CDI Config Datei beans.xml den Eintrag des nicht verfügbaren Services.

In diesem Zusammenhang darf auf das sehr gut passende Seminar „TDD mit Java“ von Binaris hier hingewiesen werden.

Hier ein Beispiel zu einem Request Scope-bezogenen Producer, der sowohl eine Request Scope-bezogenen EntityManager instanziiert und implizit konfiguriert, als auch den Request Scope-bezogenen Logger. Der Request Scope-bezogene EntityManager wird je Request instanziiert, um die Requests zu trennen, damit jeder in einer separaten Transaktion ausgeführt wird:

package de.binaris.jbpmexamples;
 
import java.util.logging.Logger;
 
import javax.enterprise.inject.Disposes;
import javax.enterprise.inject.Produces;
import javax.enterprise.inject.spi.InjectionPoint;
import javax.inject.Inject;
 
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
 
import javax.enterprise.context.RequestScoped;
 
@RequestScoped
public class ExampleRequestScopedProducer {
 
    @Inject
    private EntityManagerFactory emf;
 
    @Produces
    @RequestScoped
    public EntityManager getEntityManager() {
        EntityManager em = emf.createEntityManager();
        return em;
    }
 
    public void close(@Disposes EntityManager em) {
        em.close();
    }
 
    @Produces
    public Logger createLogger(InjectionPoint injectionPoint) {
        return Logger.getLogger(injectionPoint.getMember().getDeclaringClass()
                .getName());
    }
}
 

Hier ein Beispiel für den Application Scope-bezogenen Producer mit einer Application Scope-bezogenen EntityManagerFactory aus der PersistenceUnit namens“de.binaris.jbpmexamples.example“, mit deren Methode getEntityManager() der Request Scope-basierte EntityManager erhältlich ist.

Die @ApplicationScoped Annotation bedeutet für den CDI Container (z.B. den Application Server), dass Instanzen, die hierbei erzeugt werden für den gesamten Lebenszylus dieser deployten Version (also z.B. bis zum Herunterfahren/Neustart des Application Servers bzw. einem erneuten (Hot-)Deployments der Applikation). Es ist logisch, dass die unter Verwendung der Persistence Unit erhaltene Datenbank Connection sich während der Verwendung einer Web Application nicht ändert, solange exklusiv auf eine Datenbank zugegriffen wird.

Der RuntimeEnvironment Producer, bietet den gesamten Setup für den Runtime Manager, inklusive der verwendeten Process Definition. Der RuntimeManager wird aus einer beim Deployment und dem Start der Applikation geladenen jBPM Library zum Zeitpunkt seines Aufrufs einmalig instanziiert, dieselbe jBPM Library injectet intern auch die RuntimeEnvironment Instanz in die Beispielapplikation (CDI), ohne die Beispielapplikation überhaupt zu kennen.

package de.binaris.jbpmexamples.example;
 
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.Produces;
import javax.inject.Inject;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.PersistenceUnit;
 
import org.jbpm.runtime.manager.impl.cdi.InjectableRegisterableItemsFactory;
import org.kie.api.io.ResourceType;
import org.kie.api.runtime.manager.RuntimeEnvironment;
import org.kie.api.runtime.manager.RuntimeEnvironmentBuilder;
import org.kie.api.task.UserGroupCallback;
import org.kie.internal.io.ResourceFactory;
import org.kie.internal.runtime.manager.cdi.qualifier.PerProcessInstance;
import org.kie.internal.runtime.manager.cdi.qualifier.PerRequest;
import org.kie.internal.runtime.manager.cdi.qualifier.Singleton;
 
@ApplicationScoped
public class ExampleApplicationScopedProducer {
 
    @Inject
    private InjectableRegisterableItemsFactory factory;
 
    @PersistenceUnit(unitName = „de.binaris.jbpmexamples.example“)
    private EntityManagerFactory emf;
 
    @Produces
    public EntityManagerFactory produceEntityManagerFactory() {
        if (this.emf == null) {
            this.emf = Persistence
                    .createEntityManagerFactory(„de.binaris.jbpmexamples.example“);
        }
        return this.emf;
    }
 
    @Produces
    @Singleton
    @PerProcessInstance
    @PerRequest
    public RuntimeEnvironment produceEnvironment(EntityManagerFactory emf) {
        RuntimeEnvironment environment = RuntimeEnvironmentBuilder.Factory.get()
                .newDefaultBuilder()
                .entityManagerFactory(emf)
                .registerableItemsFactory(factory)
                .addAsset(ResourceFactory.newClassPathResource(„example.bpmn“),
                        ResourceType.BPMN2).get();
        return environment;
    }
}

Die von der ResourceFactory für das RuntimeEnvironment benötigte Ressourcen-Datei example.bpmn mit den XML-Definitionen der Zustände und deren (un-)bedingten Zustandsübergangen wird hier aus Gründen der Übersichtlichkeit nicht extra gezeigt. Die .bpmn-Dateien werden üblicherweise in der jBPM-View innerhalb des gewählten Eclipse Derivats (z.B. dem JBoss Developer Studio) interpretiert und können dort innerhalb dieses graphischen Editors  auch besser bearbeitet, überprüft und visualisiert werden.

Die Persistence Unit sieht hierbei prinzipiell folgendermaßen aus:

<?xml version=“1.0″ encoding=“UTF-8″ standalone=“yes“?>
<persistence version=“2.0″
xsi:schemaLocation=“http://java.sun.com/xml/ns/persistence                                 http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd
http://java.sun.com/xml/ns/persistence/orm                                  http://java.sun.com/xml/ns/persistence/orm_2_0.xsd“
             xmlns:orm=“http://java.sun.com/xml/ns/persistence/orm“
             xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance“
             xmlns=“http://java.sun.com/xml/ns/persistence“>
 
  <persistence-unit name=“de.binaris.jbpmexamples.example“ transaction-type=“JTA“>
    <provider>org.hibernate.ejb.HibernatePersistence</provider>
    <jta-data-source>java:jboss/datasources/ExampleDS</jta-data-source>
        <mapping-file>META-INF/JBPMorm.xml</mapping-file>
        <mapping-file>META-INF/Taskorm.xml</mapping-file>
 
        <class>org.drools.persistence.info.SessionInfo</class> <class>org.jbpm.persistence.processinstance.ProcessInstanceInfo</class>
        <class>org.drools.persistence.info.WorkItemInfo</class>
        <class>org.jbpm.persistence.correlation.CorrelationKeyInfo</class>        <class>org.jbpm.persistence.correlation.CorrelationPropertyInfo</class>       
        <class>[…]</class>       
        <exclude-unlisted-classes>true</exclude-unlisted-classes>
 
    <properties>
      <property name=“hibernate.max_fetch_depth“ value=“3″/>
      <property name=“hibernate.hbm2ddl.auto“ value=“update“ />
      <property name=“hibernate.show_sql“ value=“false“ /> 
      <property name=“hibernate.transaction.manager_lookup_class“ value=“org.hibernate.transaction.JBossTransactionManagerLookup“ />
         <property name=“hibernate.dialect“ value=“org.hibernate.dialect.H2Dialect“/>
      <property name=“hibernate.id.new_generator_mappings“ value=“false“/>
    </properties>       
  </persistence-unit>   
</persistence>

Das Controller Servlet

Es gibt bereits einen sehr guten Blog-Eintrag in diesem Blog zum Thema JBoss Seam, PageFlow, und jBPM hier, und einen Blog-Eintrag zur Erstellen von Web-Applikationen mit Facelets und XHTML-Benutzeroberflächen (User Interfaces) mittels Seam-Framework und dem JBoss Developer Studio hier, weshalb in diesem Blog-Eintrag auf die Erstellung von Benutzeroberflächen mit JBoss Seam, nicht weiter eingegangen werden soll. Stattdessen wird ein simples Servlet und eine index.jsp als Front Controller bzw. Startseite des Beispiels verwendet, da der Focus in diesem Blog-Eintrag auf der internen Service Logik liegt.

Hier das ProcessExampleServlet:

package de.binaris.jbpmexamples.web;
 
import java.io.IOException;
 
import javax.ejb.EJB;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.RequestDispatcher;
 
import de.binaris.jbpmexamples.ejb.ProcessExampleLocal;
 
public class ProcessServlet extends HttpServlet {
 
    private static final long serialVersionUID = 1L;
 
    @EJB
    private ProcessExampleLocal processService;
 
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse res)
            throws ServletException, IOException {
 
         doPostGet(req, res);
    }
 
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse res)
            throws ServletException, IOException {
 
         doPostGet(req, res);
    }
 
    private void doPostGet(HttpServletRequest req, HttpServletResponse res)
            throws ServletException, IOException {
       
        String recipient = req.getParameter(„recipient“);
        long processId = -1;
        try {
            processId = processService.startProcess(recipient);
        } catch (Exception e) {
            throw new ServletException(e);
        }
        String message = „Process Instanz (id: „+processId+“) gestartet.”;
        req.setAttribute(„message“, message);
 
        ServletContext context = this.getServletContext();
        RequestDispatcher dispatcher = context
                .getRequestDispatcher(„/index.jsp“);
        dispatcher.forward(req, res);
    }
}

Hier das TaskExampleServlet:

package org.jbpm.examples.web;
 
import java.io.IOException;
import java.util.List;
 
import javax.ejb.EJB;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
import de.binaris.jbpmexamples.ejb.ProcessOperationException;
import de.binaris.jbpmexamples.ejb.TaskExampleLocal;
import org.kie.api.task.model.TaskSummary;
 
public class TaskExampleServlet extends HttpServlet {
 
    private static final long serialVersionUID = 1L;
 
    @EJB
    private TaskExampleLocal taskService;
 
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse res)  
        throws ServletException, IOException {
 
        doPostGet(req, res);
    }
 
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse res)
         throws ServletException, IOException {
 
        doPostGet(req, res);     
    }
 
    private void doPostGet(HttpServletRequest req, HttpServletResponse res)
         throws ServletException, IOException {
 
        String cmd  = req.getParameter(„cmd“);
        String user = req.getParameter(„user“);
 
        if (cmd.equals(„list“)) {
 
            List<TaskSummary> taskList;
            try {
                taskList = taskService.retrieveTaskList(user);
            } catch (Exception e) {
                throw new ServletException(e);
            }
            req.setAttribute(„taskList“, taskList);
            ServletContext context = this.getServletContext();
            RequestDispatcher dispatcher = context.getRequestDispatcher(„/task.jsp“);
            dispatcher.forward(req, res);

        } else if (cmd.equals(„approve“)) {

            String message = „“;
            long taskId = Long.parseLong(req.getParameter(„taskId“));
            try {
                taskService.approveTask(user, taskId);
                message = „Task (id: “ + taskId + „) beendet von “ + user;
            } catch (ProcessOperationException e) {
                // Recoverable exception
                message  = „Task Fehler. Bitte wiederholen. Stacktrace: „;
                message += e.getMessage();
            } catch (Exception e) {
                // Unexpected exception
                throw new ServletException(e);
            }
            req.setAttribute(„message“, message);
            ServletContext context = this.getServletContext();
            RequestDispatcher dispatcher = context.getRequestDispatcher(„/index.jsp“);
            dispatcher.forward(req, res);
        }
    }
}

Da die Bedeutung der Test-getriebenen Entwicklung mit Java immer weiter gestiegen ist,

hier eine Empfehlung für das „Test-Driven Development mit Java“ Seminar von Binaris.

Hier auch die pom.xml für ein jBPM Maven-Projekt:

<?xml version=“1.0″ encoding=“UTF-8″?>
<project xmlns=“http://maven.apache.org/POM/4.0.0″ xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance“
xsi:schemaLocation=“http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd“>
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>de.binaris.jbpmexamples</groupId>
<artifactId>jbpm-web-examples</artifactId>
<version>6.0.1-SNAPSHOT</version>
</parent>

<artifactId>example</artifactId>
<packaging>war</packaging>
<name>example</name>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.jbpm</groupId>
<artifactId>jbpm-bom</artifactId>
<version>${jbpm.version}</version>
<scope>import</scope>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>

<dependencies>
<!– Java EE dependencies –>
<dependency>
<groupId>org.jboss.spec.javax.ejb</groupId>
<artifactId>jboss-ejb-api_3.1_spec</artifactId>
<version>1.0.2.Final</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
<artifactId>hibernate-jpa-2.0-api</artifactId>
<version>1.0.1.Final</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.transaction</groupId>
<artifactId>jta</artifactId>
<version>1.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.1</version>
<scope>provided</scope>
</dependency>
<!– jBPM 6 dependencies –>
<dependency>
<groupId>org.kie</groupId>
<artifactId>kie-api</artifactId>
</dependency>
<dependency>
<groupId>org.jbpm</groupId>
<artifactId>jbpm-audit</artifactId>
</dependency>
<dependency>
<groupId>org.jbpm</groupId>
<artifactId>jbpm-bpmn2</artifactId>
</dependency>
<dependency>
<groupId>org.jbpm</groupId>
<artifactId>jbpm-human-task-core</artifactId>
</dependency>
<dependency>
<groupId>org.jbpm</groupId>
<artifactId>jbpm-human-task-audit</artifactId>
</dependency>
<dependency>
<groupId>org.jbpm</groupId>
<artifactId>jbpm-kie-services</artifactId>
<exclusions>
<exclusion>
<groupId>org.jboss.netty</groupId>
<artifactId>netty</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.jbpm</groupId>
<artifactId>jbpm-shared-services</artifactId>
</dependency>
<dependency>
<groupId>org.jbpm</groupId>
<artifactId>jbpm-persistence-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.jbpm</groupId>
<artifactId>jbpm-runtime-manager</artifactId>
</dependency>
<!– can compile, but cannot deploy without these seam dependencies –>
<dependency>
<groupId>org.jboss.seam.persistence</groupId>
<artifactId>seam-persistence</artifactId>
<version>3.1.0.Final</version>
</dependency>
<dependency>
<groupId>org.jboss.seam.transaction</groupId>
<artifactId>seam-transaction</artifactId>
<version>3.1.0.Final</version>
</dependency>
<!– to enable all examples run together on the same app server –>
<dependency>
<groupId>org.jboss.netty</groupId>
<artifactId>netty</artifactId>
<version>3.2.5.Final</version>
<scope>provided</scope>
</dependency>
</dependencies>

<repositories>
<repository>
<id>jboss-public-repository-group</id>
<name>JBoss Public Maven Repository Group</name>
<url>https://repository.jboss.org/nexus/content/groups/public-jboss/</url>
<layout>default</layout>
<releases>
<enabled>true</enabled>
<updatePolicy>never</updatePolicy>
</releases>
<snapshots>
<enabled>true</enabled>
<updatePolicy>daily</updatePolicy>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<releases>
<enabled>true</enabled>
<updatePolicy>never</updatePolicy>
</releases>
<snapshots>
<enabled>false</enabled>
<updatePolicy>never</updatePolicy>
</snapshots>
<id>jboss-public-repository</id>
<name>JBoss Public</name>
<url>https://repository.jboss.org/nexus/content/groups/public-jboss/</url>
</pluginRepository>
</pluginRepositories>

<build>
<plugins>
<plugin>
<groupId>org.jboss.as.plugins</groupId>
<artifactId>jboss-as-maven-plugin</artifactId>
<version>7.3.Final</version>
</plugin>
</plugins>
</build>
</project>

Hier noch der DZone Link zu einem weiteren coolen Beispiel-Projekt: der Cool Store
http://java.dzone.com/articles/jboss-brms-cool-store-demo-1

Allen interessierten Leserinnen und Lesern noch eine schöne Sommerzeit und viel Spaß bei der WM.

Kommentare

Einen Kommentar hinzufügen...

Sie müssen registriert und angemeldet sein um einen Kommentar zu schreiben.