Disclaimer:
These pages about different languages / apis / best practices were mostly jotted down quckily and rarely corrected afterwards.
The languages / apis / best practices may have changed over time (e.g. the facebook api being a prime example), so what was documented as a good way to do something at the time might be outdated when you read it (some pages here are over 15 years old).
Just as a reminder.

jBPM developer notes; creating business processes in java

Business Process Management using jBPM in Java

jBOSS (separate page)

using jBPM with regular jBoss server (not starter kit)

Installing

You need to copy the follwing libs:

using jBPM starter kit 3.1

(this stuff might or might not apply to earlier/later versions)

Installing

(using Eclipse 3.1.1)
Note that the jBPM designer will complain with "An error occurred. See error log for details", if you already have jBPM 3.0 projects in your workspace (even if you try to create a new project
I created a new workspace and then it worked fine.

missing hibernate/hbm2ddl

Seems like one must add the hibernate.jar as external library to the project in eclipse

Assign a task node to a user

SimpleProcessTest.java:
package com.sample;

import junit.framework.TestCase;

import org.jbpm.graph.def.ProcessDefinition;
import org.jbpm.graph.exe.ProcessInstance;

import org.jbpm.JbpmConfiguration;
import org.jbpm.JbpmContext;

import org.jbpm.taskmgmt.exe.TaskMgmtInstance;
import org.jbpm.taskmgmt.exe.SwimlaneInstance;

import java.util.List;

//needed for JbpmSessionFactory
import org.jbpm.db.*;

public class SimpleProcessTest extends TestCase {

static JbpmConfiguration jbpmConfiguration = null;

static {
// An example configuration file such as this can be found in
// 'src/config.files'. Typically the configuration information is in the
// resource file 'jbpm.cfg.xml', but here we pass in the configuration
// information as an XML string.

// First we create a JbpmConfiguration statically. One JbpmConfiguration
// can be used for all threads in the system, that is why we can safely
// make it static.

jbpmConfiguration = JbpmConfiguration.parseXmlString(
"<jbpm-configuration>" +

// A jbpm-context mechanism separates the jbpm core
// engine from the services that jbpm uses from
// the environment.

" <jbpm-context>" +
" <service name='persistence' " +
" factory='org.jbpm.persistence.db.DbPersistenceServiceFactory' />" +
" </jbpm-context>" +

// Also all the resource files that are used by jbpm are
// referenced from the jbpm.cfg.xml

" <string name='resource.hibernate.cfg.xml' " +
" value='hibernate.cfg.xml' />" +
" <string name='resource.business.calendar' " +
" value='org/jbpm/calendar/jbpm.business.calendar.properties' />" +
" <string name='resource.default.modules' " +
" value='org/jbpm/graph/def/jbpm.default.modules.properties' />" +
" <string name='resource.converter' " +
" value='org/jbpm/db/hibernate/jbpm.converter.properties' />" +
" <string name='resource.action.types' " +
" value='org/jbpm/graph/action/action.types.xml' />" +
" <string name='resource.node.types' " +
" value='org/jbpm/graph/node/node.types.xml' />" +
" <string name='resource.varmapping' " +
" value='org/jbpm/context/exe/jbpm.varmapping.xml' />" +
"</jbpm-configuration>"
);
}
public void setUp() {
//jbpmConfiguration.createSchema();
}

public void tearDown() {
//jbpmConfiguration.dropSchema();
}

//init of the database, is performed ONCE (e.g. setupProcesses, exectued by admin)
//afterwards instantiateProcess can fetch the processdefinition from the dB
public void deployProcessDefinition() {
String xmlFile="simple.par/processdefinition.xml";

// This test shows a process definition and one execution
// of the process definition.
ProcessDefinition processDefinition =
ProcessDefinition.parseXmlResource(xmlFile);

// Lookup the pojo persistence context-builder that is configured above
JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
try {
// Deploy the process definition in the database
jbpmContext.deployProcessDefinition(processDefinition);

} finally {
// Tear down the pojo persistence context.
// This includes flush the SQL for inserting the process definition
// to the database.
jbpmContext.close();
}
}

public void instantiateProcess() {

// Lookup the pojo persistence context-builder that is configured above
JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
try {

GraphSession graphSession = jbpmContext.getGraphSession();

ProcessDefinition processDefinition =
graphSession.findLatestProcessDefinition("simpleDeluxe");

// With the processDefinition that we retrieved from the database, we
// can create an execution of the process definition just like in the
// hello world example (which was without persistence).
ProcessInstance processInstance =
new ProcessInstance(processDefinition);

// here some stuff COULD happen, but not necessary?

//we want assign some user to the swimlanes "reader" and "writer"

//fetch the TaskMgmtInstance
TaskMgmtInstance myTaskMgmtInstance = processInstance.getTaskMgmtInstance();

//get the "reader" swimlane
SwimlaneInstance swimLaneInstance = myTaskMgmtInstance.getSwimlaneInstance("reader");

if(swimLaneInstance==null)
fail("reader swimlane is null");
//assign user "donald" (he can read a book, but is crap at writing books)
swimLaneInstance.setActorId("donald");

//swimLaneInstance = myTaskMgmtInstance.getSwimlaneInstance("writer");
//assign user "goofy" (he is an excellent author)
//swimLaneInstance.setActorId("goofy");

// Now the processInstance is saved in the database. So the
// current state of the execution of the process is stored in the
// database.
jbpmContext.save(processInstance);
// The method below will get the process instance back out
// of the database and resume execution by providing another
// external signal.

} finally {
// Tear down the pojo persistence context.
jbpmContext.close();
}
}

// Will not be used for database, just for testing
// when using dB we will have an init that loads and saves the xml files in the database
// once, so the instantiation will only process database content (see ch3 in userguide)
/*
private ProcessDefinition instantiateProcessSimple(String name){
//1 Load property file and lookup which xml that this name maps to

String xmlFile="";
if(name.equals("test")){
xmlFile="simple.par/processdefinition.xml";
}else{
return null;
}

// Extract a process definition from the processdefinition.xml file.
ProcessDefinition definition =
ProcessDefinition.parseXmlResource(xmlFile);
assertNotNull("Definition should not be null", definition);
return definition;
}*/

public void testGetTodo(){
// Let's open a new persistence session
/*
JbpmSession jbpmSession = jbpmSessionFactory.openJbpmSession();

TaskMgmtSession taskMgmtSession = null;
taskMgmtSession = new TaskMgmtSession(jbpmSession);
List myTasks = taskMgmtSession.findTaskInstances("donald");

fail("list size="+myTasks.size());
*/
}

public void testDbProcess() throws Exception {
deployProcessDefinition();
instantiateProcess();
}
/*
public void testSimpleProcess() throws Exception {
ProcessDefinition definition = instantiateProcessSimple("simpleDeluxe");

// Create an instance of the process definition.
ProcessInstance instance = new ProcessInstance(definition);
assertEquals(
"Instance is in start state",
instance.getRootToken().getNode().getName(),
"start");
assertNull(
"Message variable should not exist yet",
instance.getContextInstance().getVariable("message"));

// Move the process instance from its start state to the first state.
// The configured action should execute and the appropriate message
// should appear in the message process variable.
instance.signal();
assertEquals(
"Instance is in first state",
instance.getRootToken().getNode().getName(),
"first");
assertEquals(
"Message variable contains message",
instance.getContextInstance().getVariable("message"),
"Going to the first state!");

// Move the process instance to the end state. The configured action
// should execute again. The message variable contains a new value.
instance.signal();
assertEquals(
"Instance is in end state",
instance.getRootToken().getNode().getName(),
"end");
assertTrue("Instance has ended", instance.hasEnded());
assertEquals(
"Message variable is changed",
instance.getContextInstance().getVariable("message"),
"About to finish!");

} */
}

processdefinition.xml:
<?xml version="1.0" encoding="UTF-8"?>
<process-definition xmlns="http://jbpm.org/3/jpdl" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://jbpm.org/3/jpdl http://jbpm.org/xsd/jpdl-3.0.xsd" name="simpleDeluxe">
<swimlane name="writer"></swimlane>
<swimlane name="reader"></swimlane>
<start-state name="start">
<task swimlane="writer">
<controller>
<variable name="item" />
<variable name="quantity" />
<variable name="address" />
</controller>
</task>
<transition name="tr1" to="first"></transition>
</start-state>
<state name="first">
<transition name="tr1" to="writeBook"></transition>
</state>
<task-node name="writeBook">
<task swimlane="writer">
<controller>
<variable name="booktitle" />
</controller>
</task>
<transition name="tr1" to="BookIsWritten"></transition>
</task-node>
<state name="BookIsWritten">
<transition name="tr1" to="readBook"></transition>
</state>
<task-node name="readBook">
<task swimlane="reader">
<controller>
<variable name="review" />
</controller>
</task>
<transition name="tr1" to="BookHasBeenRead"></transition>
</task-node>
<state name="BookHasBeenRead">
<transition name="tr1" to="end"></transition>
</state>
<end-state name="end"></end-state>
</process-definition>

Get User tasks

  
 public ArrayList getUserTasks(String uid){
   List myTasks =null;
     JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
             try{
             TaskMgmtSession taskMgmtSession = jbpmContext.getTaskMgmtSession();
             myTasks = taskMgmtSession.findTaskInstances("donald");
             } finally {
 
                 jbpmContext.close();
 
             }
             //create an ArrayList
             ArrayList returnList = new ArrayList();
 
             //build up something we can return over a remote interface
             for (Iterator iter = myTasks.iterator(); iter.hasNext();) {
 
                 TaskInstance myTmp = (TaskInstance) iter.next();
 
  
 
                 TaskTO myTask = new TaskTO();
 
                 myTask.name = myTmp.getName();
 
                 myTask.description = myTmp.getDescription();
 
                 myTask.id = myTmp.getId();
 
                 myTask.dueDate = myTmp.getDueDate();
 
                 myTask.startDate = myTmp.getStart();
 
                 returnList.add( myTask );
 
             }
   //note that the List could not be transferred via a remote interface (due to connection to hibernate),
   //so you havo create a transfer object with the data you want to return	   
   return returnList;
 }
 

Swimlanes

Useful thread on Swimlanes, assignment of actors
http://www.jboss.com/index.html?module=bb&op=viewtopic&t=74156
How to instantiate the swimlanes:
 //This ensures that all swimlanes are initialized and available for reporting and reassignment immediately.
 //If we don't initialize them up front, they won't be created until a task calls for them.
 Map swimlanes = processDefinition.getTaskMgmtDefinition().getSwimlanes();
 Iterator itr = swimlanes.keySet().iterator();
 log.debug(logPrefix + ".instantiateProcess: Start looping over Swimlanes...");
 while(itr.hasNext()) {
   String sSwimlaneName = (String) itr.next();
   log.debug(logPrefix + ".instantiateProcess: found: " + sSwimlaneName);
   Swimlane swimlane = (Swimlane)swimlanes.get( sSwimlaneName );
   SwimlaneInstance swi = processInstance.getTaskMgmtInstance().getInitializedSwimlaneInstance(
                             new ExecutionContext(processInstance.getRootToken()), swimlane);
 }
 log.debug(logPrefix + ".instantiateProcess: ....end looping over Swimlanes");
 

Start/end a task

 Integer taskinstanceid = new Integer(23432);
 
 try{
   TaskMgmtSession taskMgmtSession = jbpmContext.getTaskMgmtSession();           
   TaskInstance myTask = taskMgmtSession.loadTaskInstance( taskinstanceid.longValue()  );
   myTask.start(uid);
 } catch(Exception e) {
   log.error(logPrefix + "processTask exception thrown : " + e);
 } finally {                        
   jbpmContext.close();
 }
 

Setting and getting variables

 //start the task  
 try{
   TaskMgmtSession taskMgmtSession = jbpmContext.getTaskMgmtSession();           
   TaskInstance myTask = taskMgmtSession.loadTaskInstance( taskinstanceid.longValue()  );
 
   //pass on any arguments to the Actions
   Token token = myTask.getToken();
   ProcessInstance processInstance = token.getProcessInstance();
   ContextInstance contextInstance = processInstance.getContextInstance();
   contextInstance.setVariable("inputdataActivity", data);
 
   myTask.start(userid);                
 } catch(Exception e) {
   log.error(logPrefix + "processTask exception thrown : " + e);
 } finally {
   jbpmContext.close();
 }
 
in for example action handler we fetch the data:
 public void execute(ExecutionContext executionContext) throws Exception {
   //fetch any input data (see WorkManagerBean for more info)
   String varName="inputdataActivity";
   ContextInstance contextInstance = executionContext.getContextInstance();
   Activity myActivity = (Activity) contextInstance.getVariable(varName);
   if(myActivity != null)
     log.debug(">>>>Tried fetching variable \""+ varName2 + "\", value="+ myActivity.approved);
 
   //do stuff
 }
 

Decision handler - example

 <decision name="Someone approved?">
   <handler class='somewhere.workflows.CommonHandlers$DecisionOnEnd'>
     <leaveReject>applicant rejected</leaveReject>
     <leaveOk>applicant approved</leaveOk>
   </handler>
 
   <transition name="applicant approved" to="HP approve"></transition>
   <transition name="applicant rejected" to="rejected"></transition>
    
 </decision>
 
 public static class DecisionOnEnd implements org.jbpm.graph.node.DecisionHandler {
 
   //hmm? must this always be implemented
   private static final long serialVersionUID = 1L;
             
   //will be set with the name of the transition to go if user rejects
   private String leaveReject;
   //will be set with the name of the transition to go if user approves
   private String leaveOk;
              
   public String decide(ExecutionContext executionContext) throws Exception {
     log.debug(logPrefix +"DecisionOnEnd: leaveReject: "+ leaveReject);
     log.debug(logPrefix +"DecisionOnEnd: leaveOk:     "+ leaveOk);
 
     //which way to go, as default we reject
      String exitTransition = leaveReject;
 
     try {
       //fetch input data from the processInstance variables (see WorkManagerBean for more info)    
       String varName="inputdataActivity";
       ContextInstance contextInstance = executionContext.getContextInstance();
 
       Activity myActivity = (Activity) contextInstance.getVariable(varName);
 
       if(myActivity != null) {
         log.debug(logPrefix +"DecisionOnEnd: Reading processInstance variable to determine if approved or not. \""+ varName
                   + "\", value="+ myActivity.bApproved);          
 
         //if approved we go to the transition defined in leaveOk
         if(myActivity.bApproved)
             exitTransition = leaveOk;
 
       }else{
         log.error(logPrefix +"DecisionOnEnd: there data regarding if the task was approved or rejected is null! (i.e. "+ varName
                   + "=null). The task is rejected since we have to do something");
       }
                 
     } catch (Exception e) {
       log.error(logPrefix +"DecisionOnEnd: Catched exception when trying to read variable, " +
               "the task is rejected since we have to do something  e="+e);
     }                  
                 
     log.debug(logPrefix +"DecisionOnEnd: Attempt to leave on the transition: " + exitTransition );
     return exitTransition;
   }
 }
 

Action handler - example

<task-node name="Someone approve">
        <task swimlane="applicant">     
          <controller>
            <variable name="approved"/>
            </controller>
          <event type='task-end'>
             
        <action class='somewhere.workflows.Process$ApplicantOnEnd'>
        </action>
      </event>
    </task>
 
     <transition name="applicant finished" to="Applicant approved?"></transition>
   </task-node>
 
 /**
  * Process data after someone has verified if the data is correct or not
  */
 public static class ApplicantOnEnd implements ActionHandler {
   private static final long serialVersionUID = 1L;
 
   public void execute(ExecutionContext executionContext) throws Exception {
     //fetch any input data
     try {
       String varName="inputdataActivity";
       ContextInstance contextInstance = executionContext.getContextInstance();
       Activity myActivity = (Activity) contextInstance.getVariable(varName);
       if(myActivity != null)
         log.debug(">>>>Tried fetching variable \""+ varName + "\", value="+ myActivity.bApproved);
                 
     } catch (Exception e) {
        log.error(logPrefix +"ApplicantOnEnd: Could not read variable. e="+e);
     }
 
                 
     //do stuff
                  
     //executionContext.leaveNode(leaveReject);
    }
 }
 


using jBPM starter kit 3.0.1

(this stuff might or might not apply to earlier/later versions)

When starting jBPM: Unsupported major.minor version 49

The version of jBPM probably compiled with java 5 and your JAVA_HOME points to 1.4.
Set JAVA_HOME to point to the jdk for java 5 (note: it should not point to the jre, have to be jdk)

errors about "socket creation error"

http://www.jboss.com/index.html?module=bb&op=viewtopic&t=74234:
...Alright, here it goes: you're looking in the right place. In hsqldb-ds.xml, near the start, comment the section that reads "for in-process persistent db" and uncomment "for tco connection". Also, near the end, comment the section "this mbean can be used when using in-process persistent db" and uncomment "this mbean should be used only when using tcp connection".
Please note that this opens a security hole so you shouldn't deploy such a data source in production. In addition, you shouldn't need to do this if you are accessing the database from within JBoss.

errors about mchange c3p0 etc

It uses that for the jdbc connections, you can disable it by comment out the following lines in hibernate.properties
 #hibernate.c3p0.min_size=1
 #hibernate.c3p0.max_size=3
 
or download the jar and put it in your classpath.

Starting hsqldb gui

 java -cp hsqldb.jar org.hsqldb.util.DatabaseManager
 or
 go to http://localhost:8080/jmx-console/HtmlAdaptor?action=inspectMBean&name=jboss%3Aservice%3DHypersonic
 and invoke startDatabaseManager()
 

Configuring mysql for the basic sample process

todo

Getting a task list for a user

(not tested)
 //open a new persistence session
 JbpmSession jbpmSession = jbpmSessionFactory.openJbpmSession();
 
 TaskMgmtSession taskMgmtSession = null;
 taskMgmtSession = new TaskMgmtSession(jbpmSession);
 			
 List myTasks = taskMgmtSession.findTaskInstances("someuser");
 

More programming related pages

Workflow: Release process
Workflow: Bug tracking
Teambox (Redbooth) - Mantis connector
Design Patterns
Git & Github
Go / Golang
CVS
CVS backup script
Distribution process
Installation script
Java Server Faces
Facelets
jibx
jBoss
jBpm
Perl tips
Perl links
PostgreSQL
Python / py2app
Shell scripts
Xslt
Node.js
Facebook / Opengraph
PHP developer notes
Redbooth API through php
Website optimization
jqTableKit demo
Javascript / html / css links