New-age Transactional Systems - Not Your Grandpa's OLTP
John Hugg discusses high volume transaction processing applications with high and low frequency profiles, and how VoltDB can be used for that purpose.
The content has been bookmarked!
There was an error bookmarking this content! Please retry.

Posted by Boris Lublinsky on Mar 19, 2010
One of the common BPM trends today is centralizing BPM executions across the company or large enough division of the company. This means that a single BPM server (cluster) runs many instances of many process definitions across the company. The challenge of such an approach is that although BPM engines (including jBPM) typically provide authorization of the task access[1], they typically do not provide authorization for viewing and deleting process definitions, starting, ending, viewing and deleting process instances, etc. In this article we will describe how to extend jBPM engine (based on jBPM 4.3) to implement this functionality.
The overall implementation approach is fairly simple and straightforward – for every process definition introduce (similar to task definitions) a list of users/user groups authorized to work with definitions, instances and history for a given process. Furthermore we want to support multiple authorization levels for a given user/group – currently we are introducing two roles: “starter” and “user”. Here starter is the role that allows to do anything with process definitions/instances/histories, while user role is limited to querying process/history permissions.
Implementation of such approach requires the following changes:
In addition to the above we also want to extend process instance query to allow user to narrow the search result by specifying certain process variables values. One of the common cases for such search is querying for processes “started by me”. To ensure that this query is always available our updated implementation of the start process instance command transparently adds current user ID to the set of the process variables.
Finally, in order to be able to support multiple methods of user authentication, we have implemented a custom identity session implementation, that supports programmatic setting and access of current user’s credentials. The intent here is to separate obtaining user credentials – ID and groups participation – from usage of this information by jBPM runtime.
Our implementation leverages a very powerful and flexible jBPM 4 configuration mechanism, which allowed us to:
Before delving in the details of our implementation we will first discuss jBPM 4 configurations that we heavily used.
jBPM is based on the process virtual machine (PVM)[2], which is build on custom dependency injection implementation. Dependency injection is control by a very powerful, XML-based configuration mechanism used for creation of binding between tags and specific implementations, adhering to the predefined interfaces.
At the heart of this mechanism is jbpm.wire.bindings.xml file describing[3] major components of the jBPM PVM, including:
This file is part of JBPM distribution. If a user wants to add his own bindings, instead of modifying jbpm.wire.bindings.xml file he can create jbpm.user.wire.bindings.xml, describing them.
Both files are read and parsed by jBPM PVM at startup and serves as a foundation PVM execution configuration, defined in file jbpm.cfg.xml. This file typically contains multiple parts describing configuration of the specific components of PVM execution.
jBPM PVM is comprised of a set of services that provide PVM functionality[4]. Main PVM services include:
The list of available services and classes implementing these services (using binding described above) is configured as process engine context.
Services execution is implemented as a set of commands, that get invoked as part of executions of service methods. The actual execution of commands is controlled by command service.
The command service is configured in the command service context as a set of interceptors, implementing cross-cutting concerns, around command invocation (command execution pipeline). Default jBPM distribution comes with the following interceptors in the command pipeline:
Interceptors are the core mechanism for porting jBPM to different environments and/or introduction of additional cross-cutting concerns.
Command execution typically leverage environment, which is also configured. Typical components of environment are:
Additional sessions can be added to extend PVM functionality.
Finally, deployment manager configuration allows specifying a set of deployers, that are executed sequentially to deploy business processes into PVM. Such an approach allows to implement additional deployment steps for extended process definition rather then overwriting a deployer that is shipped with jBPM distribution.
Overall PVM architecture[5] is presented at Figure 1

Figure 1 PVM Architecture
As we can see (Figure 2) allows adding any attributes to the process definitions. Using this extensibility option, we can now define the following process attributes, describing its authorization policies:
The value of each attribute is a comma-separated list of group/user ids.

Figure 2 Process Definition schema
Additionally we are defining a special user type – “any” and two user groups – “all” and “admin”. Any user, regardless of what his real ID is also “any” user. Any group, regardless of its ID is also “all”. Finally member of “admin” group is considered to be a member of any group.
Process authorization definition is driven by following rules:
Based on this, a process (Listing 1) can be both started and used by anyone
<process package="com.navteq.jbpm"
key="NO_AUTHORIZATION"
name="Test Authorization not required" version="1"
xmlns="http://jbpm.org/4.0/jpdl">
<start g="68,14,48,48" name="start" > <transition to="end"/> </start>
<end g="78,383,48,48" name="end"/>
</process>
Listing 1 Process with no authorization
A process (Listing 2) can be both used and started by mark or anyone in the group tomcat.
<process package="com.navteq.jbpm"
<process package="com.navteq.jbpm" key="AUTHORIZATION" name="Test Authorization Required" version="1" xmlns="http://jbpm.org/4.0/jpdl" user-users="mark" user-groups="tomcat">
<start g="68,14,48,48" name="start" >
<transition to="end"/>
</start>
<end g="78,383,48,48" name="end"/>
</process>
Listing 2 Process with user authorization
We are introducing a new class – ACL, which contains an individual access list (user or group and type) for a given process (processDefinitionID, processDefinitionKey, DeploymentID) and corresponding hibernate definition.
Deploying of the process (Listing 1) creates 2 ACL for the user “any” with 2 roles – “user” and “starter” (Figure 3), while deploying of process (Listing 2) will create 4 – user “mark”, group “tomcat” with 2 roles – “user” and “starter” (Figure 4).

Figure 3 ACLs for the process with no authorization

Figure 4 ACLs for the process with user authorization
Population of the ACLs is done through introduction of an additional deployer, which runs after “standard” jBPM deployer, extracts authorization attributes described above and builds ACLs for a given process.
A general approach to securing jBPM commands, that we adopted includes implementation of custom annotations for defining command requiring authorization and a custom authorization session (command interceptor) implementation processing this annotation.
Authorization annotations (Listing 3) allow specifying required user role and the way a process is represented.
@Retention(value=RetentionPolicy.RUNTIME)
@Target(value=ElementType.METHOD)
public @interface AuthorizedCommand {
/** Access type */
public String role();
String key();
}
Listing 3 Authorization annotation
A user role - “starter” or “user” – refers to the role given user should have for a given process[6]. Because different command can refer to either deploymentID or process ID or process key an annotation allows specifying an appropriate reference as a key.
An implementation of an authorization interceptor (Listing 4) checks whether any of the command’s methods is decorated with the authorization annotation. If annotation is found, then appropriate queries are executed to determine a set of users/user groups are authorized for a given command and check whether current user belong to them.
…………..
@SuppressWarnings("unchecked")
public void checkPermission(Command<?> command, EnvironmentImpl environment) {
environment.setAuthenticatedUserId(environment.get(AuthorizationIdentitySession.class).getAuthenticatedUserId());
for( Method method : command.getClass().getMethods()) {
AuthorizedCommand sc = method.getAnnotation(AuthorizedCommand.class);
if(sc != null){
log.debug("Checking Class based Secured Function");
String ID = environment.get(AuthorizationIdentitySession.class).getAuthenticatedUserId();
Object value = null;
try {
log.debug("Checking authorization: " + command.getClass().getName());
Session session = environment.get(SessionImpl.class);
value = method.invoke(command, (Object[])null);
Query uQ = session.createQuery(userQuery.get(sc.key())).
setString("role", sc.role()).setString("value",(String) value);
Query gQ = session.createQuery(groupQuery.get(sc.key())).
setString("role", sc.role()).setString("value", (String) value);
List<String> userIds = (List<String>)uQ.list();
List<String> groups = (List<String>)gQ.list();
if(!isAuthorized(environment, userIds, groups))
throw new AccessControlException(ID+" attempted access to ProcessDefinition #"+value);
} catch (IllegalArgumentException e) {
log.error("Caught " + IllegalArgumentException.class, e);
throw new AccessControlException(ID+" attempted access to ProcessDefinition #"+value);
} catch (IllegalAccessException e) {
log.error("Caught " + IllegalAccessException.class, e);
throw new AccessControlException(ID+" attempted access to ProcessDefinition #"+value);
} catch (InvocationTargetException e) {
log.error("Caught " + InvocationTargetException.class, e);
throw new AccessControlException(ID+" attempted access to ProcessDefinition #"+value);
}
}
}
return;
}
……………………..
public boolean isAuthorized(EnvironmentImpl env, List<String> authorizedUserIds, List<String> authorizedGroupIds) {
AuthorizationIdentitySession identitySession = env.get(AuthorizationIdentitySession.class);
if (authorizedUserIds.contains(AuthorizationIdentitySession.ANONYMOUS_USER_ID))
return true;
if (authorizedUserIds.contains(identitySession.getAuthenticatedUserId()) )
return true;
//check if any of userGroups is an authorized group. if so then return true
List<Group> groups = identitySession.findGroupsByUser(identitySession.getAuthenticatedUserId());
for(Group group : groups){
String g = group.getId();
// admin is allowed to execute any command
if(g.equals(AuthorizationIdentitySession.ADMINISTRATORS_GROUP))
return true;
if(authorizedGroupIds.contains(g))
return true;
}
return false;
}
Listing 4 Authorization interceptor
In order to make a command implementation secure, we create a new class extending existing command and adding an annotated method (Listing 5) returning a key available for a given command.
@AuthorizedCommand(role = ACL.STARTER, key = NavteqAuthorizationSession.PROCESSID)
public String getProcessDefinitionKey() {
return processDefinitionId;
}
Listing 5 Introducing authorization to start process instance command
Based on the proposed approach we have annotated the following commands:
Introduction authorization means that results of the query should return only information that user is authorized for[7]. This can be achieved by extending a where clause for the existing queries (Listing 6)
//check authorization
String userId = EnvironmentImpl.getCurrent().getAuthenticatedUserId();
List<Group> userGroups = EnvironmentImpl.getCurrent().get(IdentitySession.class).findGroupsByUser(userId);
hql.append(", " + ACL.class.getName() + " as acl ");
appendWhereClause("acl.deployment=deployment and acl.type='"
+ ACL.STARTER + "' ", hql);
appendWhereClause("((acl.userId in "
+ Utils.createHqlUserString(userId) + ") or " + "(acl.groupId in "
+ Utils.createHqlGroupString(userGroups) + ")) ", hql);
Listing 6 Additional where clause for supporting authorization for process definition query
Additional where clause is implemented by extending existing query implementation and overwriting hlq method.
The following queries have been extended based on this approach:
We also additionally extended a process instance query to be able to add string instance variables to further narrow query results
The implementation presented above relies on the usage of the execution environment for obtaining current user ID and IdentitySession for obtaining user groups membership. jBPM distribution comes with 2 implementations of this interface:
Instead of rolling out additional technology dependent implementation, for our implementation we decided to separate obtaining user ID/groups from storing this information and make it available to the rest of jBPM implementation (Figure 5).

Figure 5 User management implementation
To ensure that environment is available during setting/resetting of user credentials we have implemented these two operations as commands (Listing 7), thus relying on jBPM command execution service to set the execution environment correctly.
public static class SetPrincipalCommand extends AbstractCommand<Void> {
private static final long serialVersionUID = 1L;
private String userId;
private String[] groups;
public SetPrincipalCommand(String u, String...groups) { this.userId=u; this.groups=groups; }
public Void execute(Environment environment) throws Exception {
environment.get(AuthorizationIdentitySession.class).setPrincipal(userId,groups);
return null;
}
}
public static class ResetPrincipalCommand extends AbstractCommand<Void> {
private static final long serialVersionUID = 1L;
public Void execute(Environment environment) throws Exception {
environment.get(AuthorizationIdentitySession.class).reset();
return null;
}
}
Listing 7 Set user credentials commands
Because jBPM does not provide any support for configuring commands - services relationships, in order to change a command in a given service it is necessary to overwrite a service implementation with a new one invoking a new command. An example of overwriting history service with new commands is presented at Listing 8.
public class NavteqHistoryServiceImpl extends HistoryServiceImpl {
@Override
public HistoryActivityInstanceQuery createHistoryActivityInstanceQuery() {
return new AuthorizedHistoryActivityInstanceQueryImpl(commandService);
}
@Override
public HistoryDetailQuery createHistoryDetailQuery() {
return new AuthorizedHistoryDetailQueryImpl(commandService);
}
@Override
public HistoryProcessInstanceQuery createHistoryProcessInstanceQuery() {
return newAuthorizedHistoryProcessInstanceQuery(commandService);
}
}
Listing 8 Introduction of authorization commands in history service
The bulk of implementation[8] presented in this article extends not JPDL, but rather JBoss PVM which is used by all jBPM supported languages[9] . As a result it can be leveraged by any of them.
I am thankful to my NAVTEQ colleagues, especially Mark Kedzierski for discussions on overall design and implementation of majority of code and Stefan Balkowec, Vlad Zhukov, Eugine Felds and Catalin Capota for help in defining the overall solution.
[1] Based on the user credentials – user ID and groups
[2] Tom Baeyens on the Process Virtual Machine ; Tom Baeyens and Miguel Valdes Faura - The Process Virtual Machine.
[3] The file contains not the bindings themselves, but rather names of java classes, defining bindings
[4] http://docs.jboss.com/jbpm/v4/userguide/html_single/
[5] http://docs.jboss.com/jbpm/v4/devguide/html_single/
[6] See above
[7] A useful side effect of this approach is that it limits the amount of information that is returned to the user as a query result.
[8] With the exception of the extended JPDL parser
[9] Currently JPDL and BPMN
Hi,
first of all really interesting article. Some time ago I was looking at the possibilities of adding new interceptors (for handling compensation) and could not find a good explanation for it. I found it here ;) And even more important information about internals of jBPM.
Another thing, I was thinking that you could put this article on jBPM community space, this is really valuable information that I think many would be happy to read. Moreover perhaps it could be added to jBPM future releases?!
Cheers,
Maciej
Hi,
very interesting article. I have one question about the possibility to override standard implementation of execution service and command.
How to declare a custom service implementation in a jbpm configuration file ?
I found only a declaration in jbpm.wire.bindings.xml and i tried to change the binding class without success.
Thanks for any help.
John Hugg discusses high volume transaction processing applications with high and low frequency profiles, and how VoltDB can be used for that purpose.
Kevlin Henney examines code samples to see what can be learned from them starting from the premise that one won’t write great code unless he knows how to read it.
Jason Ayers share the observations he made watching a team of developers collaborating in real time on the same code base, pushing XP, pair programming and continuous integration to their extremes.
Michael Snoyman presents Yesod, a web framework written in Haskell and containing a web server, templating, ORM, libraries (templating, gravatar, etc.).
Richard Kreuter and Kyle Banker on how to avoid classical RDBMS transactional systems by using compensation mechanisms, transactional messaging or transactional procedures.
Attila Szegedi talks about performance tuning Java and Scala programs at Twitter: how to approach GC problems, the importance of asynchronous I/O, when to use MySQL/Cassandra/Redis, and much more.
One category of risk that project teams need to ensure they address is business value failure – delivering a product that fails to provide value for the business investor.
InfoQ spoke to the authors of Software Systems Architecture on a couple of new topics, the System Context viewpoint and Agile, which have been added to the second edition.
2 comments
Watch Thread Reply