How to Create a Customize WorkFlow Engine


How to Create a Customize WorkFlow Engine

In this Article, we will see How can we create a Workflow Engine. There are many workflow engines available in the market like jBPM but it is always better to know How can we create our Own using java.

We will Create a Workflow engine where we can plug different module like JMS Module, Database Module, File Module etc in a sequence also we have provisioned to change the order of the Module.

Say we want a Workflow where we need to read a file then parse the file and produce a message in a JMS queue and then after consuming the message we will persist the same.

So, We can do it easily If we can plug three module

File Module
JMS Module
Data Base Module

Where each Module do it’s own task say

File Module should read and parse the file and then transform it into a JMS message and put into a queue.
JMS Module consume the message from queue and push it into another queue
Database module consumes it and persists in the database.

To accomplish these task we have to create a generic framework where we can plug any module.

Each Module will have its configuration parameter. And Transformation information so it can transform the output according to Destination module.

We will create a demo here,


Step 1: Create module contract so that module can inherit from it.

It contains following  methods

public void process() throws Exception;
   public void transform() throws Exception;
   public void setSalience(Integer val);
   public Integer getSalience();
   public void addWorkFlowContext(WorkFlowContext ctx);
   public

void addDestinationContext(IContext ctx);

process(): it is the entry point of a module here we will put the actual logic in the implementation class.
transform(): It reads data from DestinationContext Object to know which is the Destination module and the transform the result according to that.
SetSalience/getSalience(): set the priority of a module higher number means higher priority, It will decide which module executes first in the WorkFlow.
addWorkFlowContext(): It holds any configuration details for a current module like if it is a JMS Module the queue name, JNDI lookup name for database etc.
addDestinationCintext() : It holds metadata for Destination module, name of the destination module and it;s details.

IWorkModule.java (interface)

package com.example.workflow;

public interface IWorkModule {
   
   public void process() throws Exception;
   public void transform() throws Exception;
   public void setSalience(Integer val);
   public Integer getSalience();
   public void addWorkFlowContext(WorkFlowContext ctx);
   public void addDestinationContext(IContext ctx);
   

}
Implementation: I create three Demo modules

FileModule.java :

package com.example.workflow;

public class FileModule implements IWorkModule{

   private WorkFlowContext ctx;
   private IContext destinationContext;
   private int salience=0;//default salience
   
   @Override
   public void process() throws Exception {
   System.out.println("This is a File Module ");
   System.out.println("Prirority of File Module is" + salience);
   System.out.println("This method is Entry point of module");
   System.out.println("can add result it Workflow Context so it can be used in Transformation");
     
   }

   @Override
   public void transform() throws Exception {
      System.out.println("Start transformation for destination context  "+destinationContext);
      System.out.println("get result from Workflow context and then transform it accordingly");
     
     
   }

   @Override
   public void setSalience(Integer val) {
      this.salience=val;
     
   }

   @Override
   public Integer getSalience() {
   return salience;
   }

   @Override
   public void addWorkFlowContext(WorkFlowContext ctx) {
      this.ctx=ctx;
     
   }

   
   @Override
   public void addDestinationContext(IContext ctx) {
      this.destinationContext=ctx;
   }

}


JMSModule.java :

package com.example.workflow;

public class JMSModule implements IWorkModule{
   
   private WorkFlowContext ctx;
   private IContext destinationContext;
   private int salience=0;//default salience
   
   @Override
   public void process() throws Exception {
   System.out.println("This is a JMS Module ");
   System.out.println("Prirority of JMS Module is " + salience);
   System.out.println("may Post msg in Queue");
   System.out.println("may consume msg from Queue");
   System.out.println("pass Queue Name , Jndi Context or requres parameter in Workflowcontext");
   System.out.println("can add result it Workflow Context so it can be used in Transformation");
     
   }

   @Override
   public void transform() throws Exception {
      System.out.println("Start transformation for destination context  "+destinationContext);
        System.out.println("get result from Workflow context and then transform it accordingly");
        System.out.println("if want to plug any third party transformer write a TransformerFactory and pass the required parameter in destinationContext Object");
     
     
   }

   @Override
   public void setSalience(Integer val) {
      this.salience=val;
     
   }

   @Override
   public Integer getSalience() {
   return salience;
   }

   @Override
   public void addWorkFlowContext(WorkFlowContext ctx) {
      this.ctx=ctx;
     
   }

   
   @Override
   public void addDestinationContext(IContext ctx) {
      this.destinationContext=ctx;
   }

}



DataBaseModule:

package com.example.workflow;

public class DataBaseModule implements IWorkModule{

   private WorkFlowContext ctx;
   private IContext destinationContext;
   private int salience=0;//default salience
   
   @Override
   public void process() throws Exception {
   System.out.println("This is a Database Module ");
   System.out.println("Prirority of Database Module is" + salience);
   System.out.println("persists require data");
   System.out.println("can add result it Workflow Context so it can be used in Transformation");
     
   }

   @Override
   public void transform() throws Exception {
      System.out.println("Start transformation for destination context  "+destinationContext);
        System.out.println("get result from Workflow context and then transform it accordingly");
        System.out.println("if want to plug any third party transformer write a TransformerFactory and pass the required parameter in destinationContext Object");
     
     
   }

   @Override
   public void setSalience(Integer val) {
      this.salience=val;
     
   }

   @Override
   public Integer getSalience() {
   return salience;
   }

   @Override
   public void addWorkFlowContext(WorkFlowContext ctx) {
      this.ctx=ctx;
     
   }

   
   @Override
   public void addDestinationContext(IContext ctx) {
      this.destinationContext=ctx;
   }

}

Step 2:  Create a Context interface so we can pass configuration/destination parameters here

It has following methods

    public void addEntry(String key,Object value);
    public Object getEntry(String key);


addEntry : By this, we can add any configuration information as Key-Value pair, It internally holds a map.
getEntry : find the configuration value/Object  using key.


Interface  IContext.java

package com.example.workflow;

public interface IContext {
   
   public void addEntry(String key,Object value);
   public Object getEntry(String key);

}

Implementation: Create a Default context as it is a demo application, One can implement module specific context

package com.example.workflow;

import java.util.HashMap;
import java.util.Map;

public class DefaultContext implements IContext{
   
   private Map configMap = new HashMap();

   @Override
   public void addEntry(String key, Object value) {
      configMap.put(key, value);
     
   }

   @Override
   public Object getEntry(String key) {
      // TODO Auto-generated method stub
      return configMap.get(key);
   }

   @Override
   public String toString() {
      return "DefaultContext [configMap=" + configMap + "]";
   }
   
   }


Step 3 :  Now we will create a Workflow Component which will contains all modules and sort them according to the priority and invoke them one by one


WorkFlow.java

package com.example.workflow;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class WorkFlow {
   
   private List<IWorkModule> moduleList = new ArrayList<IWorkModule>();
   
   
   public WorkFlow addModule(IWorkModule module)
   {
      moduleList.add(module);
      return this;
     
   }
   
   
   public void start() throws Exception
   {
      Collections.sort(moduleList, new Comparator<IWorkModule>(){
            @Override
              public int compare(IWorkModule i1, IWorkModule i2) {
                  return i2.getSalience().compareTo(i1.getSalience());
              }
   
          });
     
     
      for(IWorkModule module: moduleList){
         
          module.process();
          System.out.println("=====================");
          module.transform();
         
          System.out.println("=====================END OF FLOW==========");
         
      }
         
         
     
     
     
   }
   
   


}


Step 4: Create a WorkflowManager which will create different Modules and start the workflow

WorkFlowManager.java

package com.example.workflow;

public class WorkFlowManager {
   private WorkFlow flow = new WorkFlow();
   public void addWorkFlowFromUIOrCmdLine(IWorkModule module)
   {
      flow.addModule(module);
   }
   
   public void flow() throws Exception
   {
      flow.start();
   }
   
   public static void main(String[] args) throws Exception {
     
      IWorkModule fileModule = new FileModule();
      WorkFlowContext ctx = new WorkFlowContext();
      ctx.addEntry("test", "Demo");
      fileModule.addWorkFlowContext(ctx);
      fileModule.addDestinationContext(ctx);
      fileModule.setSalience(5);
     
      IWorkModule jmsModule = new JMSModule();
      jmsModule.addWorkFlowContext(ctx);
      jmsModule.addDestinationContext(ctx);
      jmsModule.setSalience(4);
     
      IWorkModule databaseModule = new DataBaseModule();
      databaseModule.addWorkFlowContext(ctx);
      databaseModule.addDestinationContext(ctx);
      databaseModule.setSalience(3);
     
      WorkFlowManager mgr = new WorkFlowManager();
      mgr.addWorkFlowFromUIOrCmdLine(fileModule);      
      mgr.addWorkFlowFromUIOrCmdLine(databaseModule);
      mgr.addWorkFlowFromUIOrCmdLine(jmsModule);
      mgr.flow();
     
      System.out.println("**********Changing the Flow by reset prirority***********");
      fileModule.setSalience(1);
      jmsModule.setSalience(4);
      databaseModule.setSalience(7);
      mgr.flow();
   }

}


Output :

This is a File Module
Prirority of File Module is5
This method is Entry point of module
can add result it Workflow Context so it can be used in Transformation
=====================
Start transformation for destination context  com.example.workflow.WorkFlowContext@5eab4b89
get result from Workflow context and then transform it accordingly
=====================END OF FLOW==========
This is a JMS Module
Prirority of JMS Module is 4
may Post msg in Queue
may consume msg from Queue
pass Queue Name , Jndi Context or requres parameter in Workflowcontext
can add result it Workflow Context so it can be used in Transformation
=====================
Start transformation for destination context  com.example.workflow.WorkFlowContext@5eab4b89
get result from Workflow context and then transform it accordingly
if want to plug any third party transformer write a TransformerFactory and pass the required parameter in destinationContext Object
=====================END OF FLOW==========
This is a Database Module
Prirority of Database Module is3
persists require data
can add result it Workflow Context so it can be used in Transformation
=====================
Start transformation for destination context  com.example.workflow.WorkFlowContext@5eab4b89
get result from Workflow context and then transform it accordingly
if want to plug any third party transformer write a TransformerFactory and pass the required parameter in destinationContext Object
=====================END OF FLOW==========
**********Changing the Flow by reset prirority***********
This is a Database Module
Prirority of Database Module is7
persists require data
can add result it Workflow Context so it can be used in Transformation
=====================
Start transformation for destination context  com.example.workflow.WorkFlowContext@5eab4b89
get result from Workflow context and then transform it accordingly
if want to plug any third party transformer write a TransformerFactory and pass the required parameter in destinationContext Object
=====================END OF FLOW==========
This is a JMS Module
Prirority of JMS Module is 4
may Post msg in Queue
may consume msg from Queue
pass Queue Name , Jndi Context or requres parameter in Workflowcontext
can add result it Workflow Context so it can be used in Transformation
=====================
Start transformation for destination context  com.example.workflow.WorkFlowContext@5eab4b89
get result from Workflow context and then transform it accordingly
if want to plug any third party transformer write a TransformerFactory and pass the required parameter in destinationContext Object
=====================END OF FLOW==========
This is a File Module
Prirority of File Module is1
This method is Entry point of module
can add result it Workflow Context so it can be used in Transformation
=====================
Start transformation for destination context  com.example.workflow.WorkFlowContext@5eab4b89
get result from Workflow context and then transform it accordingly
=====================END OF FLOW==========