Mortgage Application Form

Building a BPMS Web Application Part 1: Code Generation

(This article assumes some basic knowledge of the JBoss BPM Suite including using Business Central.)

In a previous article, I described how to use the new jBPM Form API to build a BPMS web application. I also outlined a different approach in which code generation can be used to replicate the forms to the web application using the form metadata on the BPMS Execution Server. Although I provided a link to my OpenShift implementation of such a BPMS web application, the technical details on how this is achieved is still scarce. This article remedies that. Here is a recap on why we want to build a web front end to a business process. BPMS forms are generated and customised by business analysts when they create business processes. Few customers use the forms on the BPMS Execution Servers. They prefer to build a web application that interacts with the business process remotely running on a BPMS Execution Server so that fine-grained access control, consistent look-and-feel and better client interaction can be achieved. One way to do this is by using the new jBPM Form API and the other way is to use code generation to replicate the forms using the form metadata.

Generating BPMS Forms

In the code generation approach, I am going to make use of the BPMS form metadata, run it through a code generator to produce JSPs for the UI and Java classes to handle these JSPs (for business process instantiation and manual task interaction). The Java classes constitute the Model part in the MVC (Model-View-Controller) design pattern.

This article is presented in 2 parts:

  • Part 1 – (this article) shows the use of code generation to generate JSPs and Java code to build the BPMS web application
  • Part 2 – (a subsequent article) describes how a web application can interact and control a BPMS business process running on a BPMS Execution server using the BPMS Remote Java API

 

The business process used in this demo is the BPMS Example App Mortgage Approval Process, the same business process used in the jBPM Form API article. The mortgage application begins at the start node by using a graphical form designed using the graphical Form Designer of the JBoss BPM Suite to input data from the applicant, which includes the applicant’s personal information, annual income, social security number, the address and price of the property, and the down payment. Tasks encountered during the business process execution include: Validation, Data Correction, Credit Report, Mortgage Calculation, Appraisal, etc. The business process involves a total of 9 forms. The example Mortgage application uses server-side input validation ie, the form input is not validated on the client side. If client-side input validation is required, the Mortgage Business process will have to be modified and the client-side validation code needs to be added to the code generator/templates before re-generating the JSP and Java files.

The BPMS generated/customised forms (XML files) are copied to a directory and fed into the code generator to output JSP and Java files in an output directory. These generated files are then copied to the appropriate directories in my Developer Studio (Eclipse) project. The project is then rebuilt and deployed.

The plan is to:

  1. Choose a MVC framework
  2. Know your framework well
  3. Create at least a couple of JSPs manually
  4. Generalise them into templates
  5. Transform the form meta data into usable POJOs
  6. Generate code using templates and meta data
  7. Rebuild project and redeploy

The technologies used in in this demo application include:

  • BPMS Remote Java API (REST-based)
  • Stripes MVC framework
  • Display tag library
  • Apache Digester
  • Apache Velocity templating engine

It should be noted that the code generator is MVC framework dependent. The current code generator has been built to support the Stripes MVC framework. If you are to use, say, Spring MVC, the templates for the code generator have to be reworked and customised for it.

Code Generation Implementation

The first thing to do is to clone the Mortgage Approval Process Project from your BPMS server to the JBoss Developer Studio using its GIT Perspective. BPMS forms are XML files with .form extension. A typical form looks like the following:

Form metdata
Form metdata

The code generation process includes the following steps:

Step 1: Convert the forms XML into POJOs

POJOs are defined to match the XML elements and attributes. For example, the partial Form, FormFields and FieldProperty POJO definitions are shown below (contrast them to the metadata):.


public class Form {
	private List fields = new ArrayList();
	...
}

public class FormField {


	private int position;
	private String name;
	private String type;
	private String id;
	private List properties = new ArrayList();
	
	public void addProperty(FieldProperty property) {
		properties.add(property);
	}
	...
}

public class FieldProperty {

	private String name;
	private String value;

	...
}

Apache Digester is used to convert from the form xml file to POJOs: Form, FormField, FieldProperty and dataHolder using the formRules.xml below:



<?xml version="1.0"?>
<!DOCTYPE digester-rules PUBLIC
  "-//Apache Commons //DTD digester-rules XML V1.0//EN"
  "http://commons.apache.org/digester/dtds/digester-rules-3.0.dtd">
<digester-rules>

  <pattern value="form/field">

    <object-create-rule classname="com.redhat.bpms.generator.FormField"/>
    <set-properties-rule/>

    <pattern value="property">

      <object-create-rule classname="com.redhat.bpms.generator.FieldProperty"/>
      <set-properties-rule/>
      <set-next-rule methodname="addProperty" paramtype="com.redhat.bpms.generator.FieldProperty"/>              
                     
    </pattern>
    
    <set-next-rule methodname="addField" paramtype="com.redhat.bpms.generator.FormField"/>
    
  </pattern>
  
  <pattern value="form/dataHolder">

      <object-create-rule classname="com.redhat.bpms.generator.DataHolder"/>
      <set-properties-rule/>

      <set-next-rule methodname="addDataHolder"  paramtype="com.redhat.bpms.generator.DataHolder"/>
      
    </pattern>

</digester-rules>

Apache Digester uses a stack for recognising XML. First of all, I push a Form object onto the stack and call parse to process the form XML using the Digester configuration file formRules.xml shown below:


        // Push a reference to form on to the Stack
        Form form = new Form();
        digester.push(form);
        
        // Parse the XML document and convert to form object
        digester.parse(input);

The formRules.xml file registers all the element matching patterns and the actions associated when the patterns are recognised. For example, when it encounters the Field element, it creates a FormField object. Still within the Field element, when it encounters the FieldProperty element, it creates a FieldProperty object. The set-next-rule element establishes a parent-child relationship between the parent and a child by calling the parent (FormField)’s addProperty method with the object on the top of the stack (FieldProperty) as a parameter.

Step 2: Create templates for JSRs and Java Classes

As mentioned earlier, you have to know the MVC framework that you are using. And you have to create a least a couple of JSPs and related Java classes to see what the templates should look like. I am using the Stripes MVC framework and I need to generate JSPs and ActionBeans. Here are the templates for creating a JSP and an ActionBean for kicking off an instance of a mortgage application business process.



<@include file="/WEB-INF/jsp/common/taglibs.jsp>
#foreach( $field in $form.getFields() ) #if ($field.get("inputBinding") != "") #set($name = $field.get("inputBinding")) #else #set($name = $field.get("outputBinding")) #end #if ($field.get("fieldClass") == "Separator") #elseif ($field.get("type") == "Subform") #else #end #end


$field.get("label"):$field.get("label"):
<table>
<tbody>
<tr>
<td colspan="2">

<hr />

&nbsp;</td>
</tr>
<tr>
<td colspan="2">#if ($form.getAttribute("taskType") == "humanTask") #else #end</td>
</tr>
</tbody>
</table>
#foreach( $field in $form.getFields() ) #if ($field.get("inputBinding") != "") #set($name = $field.get("inputBinding")) #else #set($name = $field.get("outputBinding")) #end #if ($field.get("fieldClass") == "Separator") #elseif ($field.get("type") == "Subform") $field.get("label"): #else$field.get("label"):   #end #end

&nbsp;
<table>
<tbody>
<tr>
<td colspan="2">

<hr />

&nbsp;</td>
</tr>
<tr>
<td colspan="2">#if ($form.getAttribute("taskType") == "humanTask") #else #end</td>
</tr>
</tbody>
</table>



package com.redhat.bpms.webapp.action;

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

import net.sourceforge.stripes.action.DefaultHandler;
import net.sourceforge.stripes.action.ForwardResolution;
import net.sourceforge.stripes.action.RedirectResolution;
import net.sourceforge.stripes.action.Resolution;
import net.sourceforge.stripes.action.UrlBinding;
import net.sourceforge.stripes.validation.Validate;
import net.sourceforge.stripes.validation.ValidateNestedProperties;

#foreach( $module in $form.getClasses() )
import ${module};
#end

@UrlBinding("/${form.getAttribute('formName')}.action")
public class ${form.getAttribute('formName')}ActionBean extends BaseActionBean {

	private final String processId = "***************TODO: FILL IN PROCESS ID HERE****************";

#foreach( $property in $form.ClassProperties )
	private ${property.className}  ${property.propertyName} = new ${property.className}();
	
	public ${property.className} get${property.className}() {
		return ${property.propertyName};
	}

	public void set${property.className}(${property.className} ${property.propertyName}) {
		this.${property.propertyName} = ${property.propertyName};
	}
	
#end
	
	private static final String FORM_PAGE = "/WEB-INF/jsp/${form.getAttribute('formName')}.jsp";
	private static final String HOME_PAGE = "/WEB-INF/jsp/home.jsp";
	
	   @DefaultHandler
	    public Resolution show${form.getAttribute('formName')}Form() {
#foreach( $dependency in $form.ClassDependencies )
			${dependency.propertyName}.set${dependency.className}(new ${dependency.className}());
#end
			return new ForwardResolution(FORM_PAGE);
	   }
	   

	    public Resolution submit() {
	   
		   Map&lt;String, Object&gt; params = new HashMap&lt;String, Object&gt;();
		   
#foreach( $dataset in $form.outputDataset )
		   params.put("${dataset.propertyName}", ${dataset.className});
#end	
	   
		   getRemoteProcessControl().startProcess(processId, params);
		   
		   System.out.println("${form.getAttribute('formName')} submitted");
		   return new RedirectResolution(${form.getAttribute('formName')}ActionBean.class);
	   }
	    
	    public Resolution cancel() {
		   System.out.println("${form.getAttribute('formName')} cancelled");
		   return new ForwardResolution(HOME_PAGE);
	   }
}

Step 3: Generate code using the POJOS and templates

Once you have the templates, you can generate the code like below:


	// render template
	protected void evaluateTemplate(Reader reader, Writer writer) {
        VelocityContext context = new VelocityContext();
        context.put("form", formSummary);
        Velocity.evaluate(context, writer, "FormGenerator", reader);


<@include file="/WEB-INF/jsp/common/taglibs.jsp>
#foreach( $field in $form.getFields() ) #if ($field.get("inputBinding") != "") #set($name = $field.get("inputBinding")) #else #set($name = $field.get("outputBinding")) #end #if ($field.get("fieldClass") == "Separator") #elseif ($field.get("type") == "Subform") #else #end #end

$field.get("label"):$field.get("label"):
<table>
<tbody>
<tr>
<td colspan="2">

<hr />

&nbsp;</td>
</tr>
<tr>
<td colspan="2">#if ($form.getAttribute("taskType") == "humanTask") #else #end</td>
</tr>
</tbody>
</table>



package com.redhat.bpms.web;

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

import net.sourceforge.stripes.action.DefaultHandler;
import net.sourceforge.stripes.action.ForwardResolution;
import net.sourceforge.stripes.action.RedirectResolution;
import net.sourceforge.stripes.action.Resolution;
import net.sourceforge.stripes.action.UrlBinding;
import net.sourceforge.stripes.validation.Validate;
import net.sourceforge.stripes.validation.ValidateNestedProperties;

import com.redhat.bpms.examples.mortgage.Property;
import com.redhat.bpms.examples.mortgage.Applicant;
import com.redhat.bpms.examples.mortgage.Application;

@UrlBinding("/MortgageApplication.action")
public class MortgageApplicationActionBean extends BaseActionBean {

	private final String processId = "***************TODO: FILL IN PROCESS ID HERE****************";

	private Application  application = new Application();
	
	public Application getApplication() {
		return application;
	}

	public void setApplication(Application application) {
		this.application = application;
	}
	
	
	private static final String FORM_PAGE = "/WEB-INF/jsp/MortgageApplication.jsp";
	private static final String HOME_PAGE = "/WEB-INF/jsp/home.jsp";
	
	   @DefaultHandler
	    public Resolution showMortgageApplicationForm() {
			application.setApplicant(new Applicant());
			application.setProperty(new Property());
			return new ForwardResolution(FORM_PAGE);
	   }
	   

	    public Resolution submit() {
	   
		   Map<String, Object> params = new HashMap<String, Object>();
		   
		   params.put("application", application);
	   
		   getRemoteProcessControl().startProcess(processId, params);
		   
		   System.out.println("MortgageApplication submitted");
		   return new RedirectResolution(MortgageApplicationActionBean.class);
	   }
	    
	    public Resolution cancel() {
		   System.out.println("MortgageApplication cancelled");
		   return new ForwardResolution(HOME_PAGE);
	   }
}

Deployment and Usage

The web application developed for this article is deployed in its own gear running the Red Hat Enterprise Web Server on OpenShift Online:

http://bpmsdemoapp-ayuen.rhcloud.com/BpmsWebApp/

The Red Hat JBoss BPM Suite is running on another gear on OpenShift Online. The web application remotely controls the execution of the Mortgage Application business process by using the REST-based Remote Java API.

Use the menu at the top to interact with the Mortgage business process. The menu items include:

  • Home – displays this article
  • Mortgage Application – allows you to start an instance of a mortgage application by filling in a form
  • Task – lists the tasks that you can work on, click on the action to complete a task via a task form
  • Logout – only shows if you have already logged in to the application

To log in, you use user: ayuen and password: password

This simple BPMS web application may not look that impressive on the surface but it is quite remarkable if you consider how it was built, viz.: the Java classes that interact with the JSPs and the Mortgage business process running remotely on a BPMS Execution Server are generated directly from the BPMS form metadata.

Conclusion

Handcrafting the forms on the web application may be feasible if the number of forms is small. The code generator approach is a much more effective way to create the forms if we are talking about 10’s and 100’s of forms for a number of business processes.

Using a code generator has its pros and cons. They pros include:

  • Consistency – code is easy to understand due to the code generator using APIs in a consistent way in a consistent program structure
  • Quality – large number of programmers producing large amount of code tend to have inconsistent quality. When bug fixes and improvements are made to the code generator, these fixes and improvements will be applied consistently throughout the code when the generator is run
  • Agile development – easier to change and upgrade over the long run
  • Sound architecture – more time to spend on design of the overall application architecture – majority of the coding is now generated

The Cons include:

  • Slower in starting up a project – someone may have to customise the code generator and the templates based on the project requirements
  • Maintenance – someone has to maintain the code generator/templates eg, there may be some corner cases where the code generator generates incorrect code. However, this is arguably a pro as once the bugs are fixed, the generated code can be fixed simply by re-running the code generator

The pros and cons of using the code generation approach versus the jBPM Form API approach has been discussed in my previous article and I am not repeating it here.

In Part 1, I’ve shown you how code generation using the Apache Velocity templating engine. In Part 2, I shall show you how to use the BPMS Remote Java API to interact with the business process running on the BPMS Exxecution Server to kickoff business process instances and manual tasks. Stay tuned!