Hi Guys,
Thanks for visiting my blog !!!
This is my first blog, that is why I decided to write about JBPM because it was the first open source tool on which I worked.
JBPM is workflow management tool available as a open source. It has many features,which one could expect in any workflow management tool. JBPM is getting more and more popular among developers and consultants. Many organizations are now recommanding JBPM as workflow management solutions for their clients.
The process engine which is core of JBPM is purely written in JAVA, which makes it easier for customization. Last year I got a chance to work on JBPM. My job was to customize the features of JBPM. Some times I got success and some times I spent sleepless night to find solution. Here I am going discuss process to customize identity service of JBPM.
Most of the organization has their own user management database like LDAP. We also had similar system to deal with the user and group management task. Clients wanted to use their own user management instead of out of the box functionality. So we decided to plug-in custom user managment tool with the JBPM's process engine.
Here I am going to describe the entire process in simple way. To demonstrate the solution I need custom identity components like users, groups and one database which contains all the active users and groups information.
I have created my own user class which extends JBPM's User Interface.
import org.jbpm.api.identity.User; public class CustomUser implements User { private String name; private String businessemail; private String familyname; private String id; public CustomUser(final String id, final String name, final String businessemail, final String familyname) { this.name = name; this.businessemail = businessemail; this.familyname = familyname; this.id = id; } @Override public String getBusinessEmail() { return businessemail; } @Override public String getFamilyName() { return familyname; } @Override public String getGivenName() { // TODO Auto-generated method stub return name; } @Override public String getId() { // TODO Auto-generated method stub return id; } }
Similarly I have created custom group class which extends JBPM's Group Interface.
import org.jbpm.api.identity.Group; import org.jbpm.api.identity.User; public class CustomGroup implements Group { private String id; private String name; private String type; private Map<String, CustomUser> memberList = new HashMap<String, CustomUser>(); public CustomGroup(final String groupid, final String groupname, final String type) { id = groupid; name = groupname; this.type = type; } @Override public String getId() { // TODO Auto-generated method stub return id; } @Override public String getName() { // TODO Auto-generated method stub return name; } @Override public String getType() { // TODO Auto-generated method stub return type; } public void addUser(final String role, final CustomUser user) { this.memberList.put(role, user); } public List<User> getMembers() { List<User> memberlist = new ArrayList<User>(); for (CustomUser member : memberList.values()) { memberlist.add(member); } return memberlist; } }
Now for database I created a cache using Java HashMap collection.
CustomUser user1 = new CustomUser("atul1", "Atul Kotwale", "Atul.kotwale@gmail.com", "@tul"); CustomUser user2 = new CustomUser("atul2", "Atul Kotwale", "Atul.kotwale@gmail.com", "@tul"); customUserCashe.put("atul1",user1); customUserCashe.put("atul2",user2); CustomGroup group1 = new CustomGroup("auditors", "A Group of Auditors", "Admin"); group1.addUser("Lead Auditor", user1); group1.addUser("Substitute Auditor", user2); customGroupCashe.put("auditors", group1);
After that I created CustomIdentitySession class which extends IdentitySession interface of JBPM. This class contains custom implementation of Service methods which Process Engine uses to retrieve user and group information. I have implemented all the methods using my custom user, group classes and cache which I built using Java HashMap. See the bellow class constructor for cache logic.
package org.jbpm.examples.identity.customidentitycomponent; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.jbpm.api.identity.Group; import org.jbpm.api.identity.User; import org.jbpm.pvm.internal.identity.spi.IdentitySession; public class CustomIdentitySession implements IdentitySession { private static final Map<String, CustomUser> customUserCashe = new HashMap<String, CustomUser>(); private static final Map<String, CustomGroup> customGroupCashe = new HashMap<String, CustomGroup>(); public CustomIdentitySession() { CustomUser user1 = new CustomUser("atul1", "Atul Kotwale", "Atul.kotwale@gmail.com", "@tul"); CustomUser user2 = new CustomUser("atul2", "Atul Kotwale", "Atul.kotwale@gmail.com", "@tul"); customUserCashe.put("atul1",user1); customUserCashe.put("atul2",user2); CustomGroup group1 = new CustomGroup("auditors", "A Group of Auditors", "Admin"); group1.addUser("Lead Auditor", user1); group1.addUser("Substitute Auditor", user2); customGroupCashe.put("auditors", group1); } @Override public String createGroup(String id, String name, String type) { // TODO Auto-generated method stub CustomGroup group = new CustomGroup(id, name, type); customGroupCashe.put(id, group); return null; } @Override public void createMembership(String arg0, String arg1, String arg2) { // TODO Auto-generated method stub } @Override public String createUser(String arg0, String arg1, String arg2, String arg3) { // TODO Auto-generated method stub return null; } @Override public void deleteGroup(String arg0) { // TODO Auto-generated method stub } @Override public void deleteMembership(String arg0, String arg1, String arg2) { // TODO Auto-generated method stub } @Override public void deleteUser(String arg0) { // TODO Auto-generated method stub } @Override public Group findGroupById(String id) { // TODO Auto-generated method stub return (Group)this.customUserCashe.get(id); } @Override public List<group> findGroupsByUser(final String id) { // TODO Auto-generated method stub List<group> groupList = new ArrayList<group>(); for(CustomGroup group : customGroupCashe.values()) { for(User user : group.getMembers()) { if(user.getId().equalsIgnoreCase(id)) { groupList.add(group); } } } return groupList; } @Override public List<group> findGroupsByUserAndGroupType(String arg0, String arg1) { // TODO Auto-generated method stub return null; } @Override public User findUserById(String arg0) { // TODO Auto-generated method stub return (CustomUser)customUserCashe.get(arg0); } @Override public List<user> findUsers() { Set<Entry<String, CustomUser>> entryList = customUserCashe.entrySet(); List<user> allUserList = new ArrayList<user>(); for(Entry<String, CustomUser> entry : entryList) { allUserList.add((User)entry.getValue()); } return allUserList; } @Override public List<user> findUsersByGroup(String id) { Object group = this.customUserCashe.get(id); List<user> memberlist = null; if(group instanceof CustomGroup) { memberlist = ((CustomGroup) group).getMembers(); } return memberlist; } @Override public List<user> findUsersById(String... arg0) { // TODO Auto-generated method stub return null; } }
Now we have completed development of our custom identity components. Next task is to tell Process Engine to use Custom Identity components. JBPM framework has different modules. Each module has their own configuration file. JBPM's process engine plugs all the modules together using one centralize configuration file, The file name is "jbpm.cfg.xml". You may find jbpm.cfg.xml in the class path of your jbpm installation. In order to plug-in our custom identity component we have to make following changes in jbpm.cfg.xml file.
- Remove entry of jbpm.identity.cfg.xml file. This will unplug the default implementation of identity service.
- Add following code to plug-in our custom identity session class. Which plug-in our custom component.
<transaction-context> <object class="your.package.CustomIdentitySessionImpl" /> </transaction-context>
After change your file should looks like this.
Now we have done with configuration part. In order to test the implementation, I have created a simple workflow.
Before I explain you about sample workflow, I would like to give you some knowledge about types of JBPM task. In JBPM we have two type of task.
- Interactive Task : This type of task required human intervention of we can say that workflow will wait on that task unless and until user mark it complete.
- Automatic Task :- A task which do not required user intervention, workflow pass through the task and complete it automatically.
I have used the same junit example setup which comes with JBPM distribution to test my code. I have created new package in same workspace with the name "org.jbpm.examples.identity.customidentitycomponent".
My package contains following files.
- audit.process.jpdl.xml :- JBPM process defination files contains workflow defination.
- CustomIdentityComponentTest :- A Junit test class contains logic to test the integation of custom identity component with JBPM core process engin. The test class will perform following task.
- It deloy workflow and intentiates process instance. After starting process, task will be assigned to group ("Audit").
- The group has two user. The user will take the task. and complete. This will ensure that our custom identity component has plugged-in correctly.
Bellow is the code of my test class.
public class CustomIdentityComponentTest extends JbpmTestCase { String deploymentId; String dept; protected void setUp() throws Exception { super.setUp(); // deploy process deploymentId = repositoryService.createDeployment() .addResourceFromClasspath("org/jbpm/examples/identity/customidentitycomponent/audit.process.jpdl.xml") .deploy(); } protected void tearDown() throws Exception { // delete process deployment repositoryService.deleteDeploymentCascade(deploymentId); super.tearDown(); } public void testTestCustomIdentityComponent() { executionService.startProcessInstanceByKey("AuditProcess"); ListtaskList = taskService.findGroupTasks("atul1"); assertEquals("Expected a single task in atul1's task list", 1, taskList.size()); Task task = taskList.get(0); String taskId = task.getId(); assertEquals("Audit", task.getName()); assertNull(task.getAssignee()); assertEquals(0, taskService.findPersonalTasks("atul1").size()); // lets assume that atul1 takes the task taskService.takeTask(taskId, "atul1"); // we'll check that the group task lists for atul1 and joesmoe are empty assertEquals(0, taskService.findGroupTasks("atul1").size()); // and that the task is directly assigned to atul1 taskList = taskService.findPersonalTasks("atul1"); assertEquals(1, taskList.size()); task = taskList.get(0); assertEquals("Audit", task.getName()); assertEquals("atul1", task.getAssignee()); // submit the task taskService.completeTask(taskId); taskList = taskService.findPersonalTasks("atul1"); assertEquals(0, taskList.size()); } }
That's all I wanted to tell you about the identity process customization. I hope you like it. Please share your valuable comments, which help me to make this article more useful.
3 Comments
This is really good help for learning JBPM. it helps a lot for beginner.
ReplyDeleteYour Given Information About JBPM Customization era. thanks for sharing Information.
ReplyDeleteJBPM 4.4 is now available for download. This new version resolves nothing short of 108 issues including feature requests and bugs. Compared to v4.3, with 47 issues, and v4.2, with 40, this release is a double pack.
For More Info :- JBPM Customization
Thanks.
nice
ReplyDelete