Develop dynamic forms using WebSphere Operational Decision Management V7.5

In this article, you'll learn how to develop the Execution Object Model (XOM), Business Object Model (BOM), and rules for implementing dynamic forms in WebSphere® Operational Decision Management V7.5. Using a simple web-based questionnaire as an example, the article demonstrates how to render the forms with Java™ Enterprise Edition (JEE) technology.

Share:

Jian Feng Cai (jfcai@cn.ibm.com), Associate Architect, IBM China

Jian Feng Cai has been working in the area of enterprise system application development for over 9 years. During this time he has applied various technologies such as JEE, business rules and process technology to build enterprise applications across various domains.



03 October 2012

Also available in Portuguese

Introduction

This article demonstrates how to create a very simple web-based questionnaire for collecting a customer's personal information. The questionnaire consists of two pages containing a total of three questions. The visibility of the second page is determined by a customer's response to questions on the first page. This article describes how to use WebSphere Operational Decision Management to implement dynamic forms for creating the questionnaire.

You should have a working knowledge of WebSphere Operational Decision Management V7.5 and understand the general concepts of business rules management. Knowledge of Java, JEE and programming languages in general is assumed.

What are dynamic forms?

Forms are the key components of all web-based applications. A form consists of multiple pages, each containing one or more field types such as option box, radio box or text box. Using WebSphere Operational Decision Management you define forms and implement decision logic with business rules. For example, decision logic can be used to change page visibility based on a user's responses. Forms are built using standard JEE technology. Web applications interact with WebSphere Operational Decision Management to retrieve forms information for presentation and to send updated forms with user-entered responses to Operational Decision Management for decision-making. Forms can be changed by updating rules, thus the name dynamic forms.

Why use dynamic forms?

Although forms are important, they can be difficult to develop and hard to maintain. Dynamic forms can be developed with less coding; moreover, dynamic forms are visible to business users allowing them to define and maintain the content and flow of complex forms with minimal IT support. Thus the following problems are addressed:

  • User interfaces are a bottleneck in adapting to changing business needs – even minor updates are costly and time-consuming.
  • Business users have no direct control or visibility into the UI functionality, which is hidden in legacy code.
  • Customers are dissatisfied because of inflexible forms and redundant data entry.
  • The user experience is not consistent across channels - web, agent and call-center apps each implement similar logic in different ways.

Develop the Execution Object Model (XOM)

In this step, you create the Execution Object Model (XOM) -- Java classes for representing a form. The basic models include: Form, Page, and Field. The relationships between these models are:

  • There are multiple pages in a form.
  • A page is the container of fields.
  • There are multiple fields on a given page.

The classes,their properties, and their methods are described below. A helper class is included.

  1. Form: currentPage, pages.
  2. Page: code, label, visible, fields.
  3. Field: code, label, type, options, responses.
  4. FieldType: Text, Radio, Checkbox and Select and it is an enum class.
  5. DynamicFormsHelper: a helper class that contains the following methods:
    • addPage(String code, String label, Form form): adds a page into the form with specified code and label.
    • addField(String code, String label, FieldType type, String options, String pageCode, Form form): adds a field with specified code, label and type to the page of form.
    • gotoNextPage(Form form): makes the form move to next page.
    • lastVisiblePageIndex(Form form): returns the index of the last visible page.
    • currentPageIndex(Form form): returns the index of current page that the form stays on.
    • getField(String code, String pageCode, Form form): returns the field with specific code on the page with specific code of the form.
    • getPage(String pageCode, Form form): returns the page with specific code.
    • getCurrentPageLabel(Form form): returns the current page label.
    • getCurrentPage(Form form): returns the current page.

Figure 1 shows the class diagram.

Figure 1. Class diagram
Class diagram

Click to see larger image

Figure 1. Class diagram

Class diagram

We can use a form object, a page object and a field object to represent a web-based questionnaire, a page and a question respectively. Now we can open the Rule Designer in WebSphere Operational Decision Management to create a Java project with the name DynamicForms-XOM. We then create models according to the class diagram and the code of the helper class.

Listing 1. DynamicFormsHelper.java
package com.ibm.wodm.df.models;

public class DynamicFormsHelper {

    public static void addPage(String code, String label, Form form) {
        Page page = new Page();
        page.setCode(code);
        page.setLabel(label);
        form.getPages().add(page);
    }

    public static void addField(String code, String label, FieldType type, 
      String options, String pageCode, Form form) {
        Page existingPage = null;
        for (Page page : form.getPages()) {
            if (page.getCode().equals(pageCode)) {
                existingPage = page;
                break;
            }
        }

        Field field = new Field();
        field.setCode(code);
        field.setLabel(label);
        field.setType(type);
        if (options != null) {
            for (String option : options.split(",")) {
                field.getOptions().add(option);
            }
        }
        existingPage.getFields().add(field);
    }

    public static void gotoNextPage(Form form) {
        int currentPageIndex = currentPageIndex(form);

        if (form.getPages().size() != (currentPageIndex + 1)) {
            for (int i = currentPageIndex + 1; i < form.getPages().size(); i++) {
                Page page = form.getPages().get(i);
                if (page.isVisible()) {
                    form.setCurrentPage(page.getCode());
                    break;
                }
            }
        }
    }

    public static int lastVisiblePageIndex(Form form) {
        int lastVisiblePageIndex = 0;
        for (int i = 0; i < form.getPages().size(); i++) {
            Page page = form.getPages().get(i);
            if (page.isVisible()) {
                lastVisiblePageIndex = i;
            }
        }

        return lastVisiblePageIndex;
    }

    public static int currentPageIndex(Form form) {
        if (form.getCurrentPage() == null) {
            return -1;
        }

        int currentPageIndex = 0;
        for (int i = 0; i < form.getPages().size(); i++) {
            Page page = form.getPages().get(i);
            if (page.getCode().equals(form.getCurrentPage())) {
                currentPageIndex = i;
                break;
            }
        }

        return currentPageIndex;
    }

    public static Field getField(String code, String pageCode, Form form) {
        for (Page page : form.getPages()) {
            if (page.getCode().equals(pageCode)) {
                for (Field field : page.getFields()) {
                    if (field.getCode().equals(code)) {
                        return field;
                    }
                }
            }
        }

        return null;
    }

    public static Page getPage(String pageCode, Form form) {
        for (Page page : form.getPages()) {
            if (page.getCode().equals(pageCode)) {
                return page;
            }
        }
        return null;
    }

    public static String getCurrentPageLabel(Form form) {
        return form.getPages().get(currentPageIndex(form)).getLabel();
    }

    public static Page getCurrentPage(Form form) {
        return form.getPages().get(currentPageIndex(form));
    }

}

Other classes are Plain Old Java Objects and are therefore not listed here.


Create a rule project

In the same workspace as above, we create a rule project with the name DynamicForms-Rules and then we create a BOM entry, rules and ruleflow for defining forms with rules. The rule project can be published to the Decision Center of WebSphere Operational Decision Management so that business users can manage the rules.

Develop Business Object Model (BOM)

In Rule Designer, we create a Business Object Model (BOM) entry based on the XOM created in the previous section. We only import mandatory attributes of the XOM classes and verbalize some of them so that they can be used for developing rules. All created BOM classes and relevant vocabularies are as follows:

  • Form: its term is form and there is no verbalized attribute.
  • Page: its term is page and the attribute “visible” is verbalized as make it {a boolean} that {a page} is visible to show or hide a page.
  • Field: its term is field and the attribute “responses” is verbalized as {the responses} of {a field} to get users’ responses to a field or a question.
  • FieldType: its term is field type and all attributes are added as domain values: Checkbox, Radio, Select and Text.
  • DynamicFormsHelper: it is not verbalized and its four member methods are imported and verbalized:
    • addField: the vocabulary is add a field with code: {a string}, label {a string}, type {a field type} and options {3} to the page with code {a string} of form {a form}. It is used to add a field specified with code, type and label to an existing page of a form.
    • addPage: the vocabulary is add a page with code: {a string} and label: {a string} to form {a form}. It is used to add a page specified with code and label to a form.
    • getField: the vocabulary is the field with code {a string} on page {a string} of form {a form}. It is used to get a field with some code that exists on a specific page of a form.
    • getPage: the vocabulary is the page with code {a string} of form {a form}. It is used to get a page with a specific code.
Figure 2. Vocabulary for DynamicForms-Rules
Figure 2. Vocabulary for DynamicForms-Rules

Define ruleset parameters

We use the Form class as a ruleset parameter with direction IN_OUT. Figure 3 shows the definition.

Figure 3: Ruleset parameters
Figure 3: Ruleset parameters

Create rule structure and ruleflow

For implementing the scenario, we need rules for defining a web-based questionnaire and rules for changing the visibility of the second page based on a user's responses to questions on the first page. Two additional requirements are to validate the user's responses and to convert between user responses and domain objects of the application. Rules are categorized into four packages: Initialize, Pre Process, Page Navigation and Post Process. Figure 4 shows the rule structure.

Figure 4: Rule structure
Figure 4: Rule structure

The overall rule flow has four rule tasks:

  1. Initialize: executes rules for initializing a form. The rules are fired only once when a questionnaire is presented to a user.
  2. Pre-process: executes rules for validating users’ responses.
  3. Page Navigation: executes rules for dynamically changing page-visibility based on conditions and navigates to the next page in its Final Action section.
  4. Post-process: executes rules for post-processing such as mapping all responses to domain objects of the application.

These rule tasks are depicted in the ruleflow shown in Figure 5.

Figure 5: Rule flow
Figure 5: Rule flow

Develop rules

The business rules used in this example fall into the following categories:

  • Definition rules that create a form consisting of pages and fields that define the web-based questionnaire.
  • Validation rules that validate users’ responses.
  • Page visibility rules for dynamically showing or hiding pages.
  • Mapping rules that perform post-processing to map users’ responses to domain objects of the application.

In the first rule package “1 - Initialize”, two sub-packages “1 - Pages Initialization” and “2 - Fields Initialization” are created. These subpackages define decision tables “Add Pages” and “Add Fields” respectively. The decision table “Add Pages” is used to define and add pages to the form.

  • The condition column is: true is {a boolean}
  • The action column is: add a page with code: <a string> and label: <a string> to form form.

Figure 6 shows how to define pages for the questionnaire.

Figure 6: Add Pages decision table (sample)
Figure 6: Add Pages decision table (sample)

The Add Fields decision table is used to define and add fields to the pages of the form.

  • The condition column is the same as the Add Pages decision table
  • The action column is: add a field with code: <a string> , label <a string> , type <a field type> and options <a string> to the page with code <a string> of form form.

Figure 7 shows how to define questions and add them to the pages of the questionnaire.

Figure 7: Add Fields decision table (sample)
Figure 7: Add Fields decision table (sample)

We need to ensure that pages are created prior to creating the fields for the page. That is, the decision table for adding pages must be evaluated prior to the decision table for adding fields. To do this, the Rule Execution Ordering attribute of the rule task Initialization is set to Literal as shown in Figure 8.

Figure 8: Initialization rule task
Figure 8: Initialization rule task

Next we can add validation logic. For example, we can add an action rule to validate whether a user answered mandatory questions on some page of the questionnaire.

In the rule package Page Navigation, we can add rules for changing page visibility. Figure 9 shows the rules for changing the visibility of the second page titled “FamilyInfo” according to the answer to the question “Married”, which is on the first page titled “PersonalInfo”.

Figure 9: Rule sample: Changing visibility of pages
Figure 9: Rule sample: Changing visibility of pages

For processing a user's answers, we can map all user responses to domain objects of the application. Relevant rules can be added in the rule package Post-process.


Develop a web-based questionnaire with JEE technology

To display defined forms to users as a web-based questionnaire and to interact with the rule engine, we create a Dynamic Web Project named DynamicForms-Web. The artifacts created are:

  1. The servlet class DynamicFormsServlet, which handles requests and responds to users.
  2. The rules execution class RulesExecutor, which invokes the rule engine.
  3. JSP pages and Cascading Style Sheets file, which render forms to display the questionnaire to users.
Listing 2. DynamicFormsServlet.java
package com.ibm.wodm.df.web.servlet;

import java.io.IOException;
import java.util.Enumeration;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.ibm.wodm.df.models.DynamicFormsHelper;
import com.ibm.wodm.df.models.Field;
import com.ibm.wodm.df.models.Form;
import com.ibm.wodm.df.models.Page;
import com.ibm.wodm.df.web.rulesexecution.RulesExecutor;

public class DynamicFormsServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) 
       throws ServletException, IOException {
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) 
       throws ServletException, IOException {
        String actionMethod = req.getParameter("actionMethod");

        Form form = (Form) req.getSession().getAttribute("form");
        if (form != null) {
            setResponses(req, form);
        }

        if (actionMethod.equals("next")) {
            String previousPage = null;
            if (form != null) {
                previousPage = form.getCurrentPage();
            }
            form = RulesExecutor.executeRules(form);

            String currentPageCode = form.getCurrentPage();
            if (formIsComplete(form, previousPage, currentPageCode)) {
                req.getRequestDispatcher("pages/summary.jsp").forward(req, resp);
                return;
            }
        } else if (actionMethod.equals("back")) {
            form = backToPreviousPage(form);
        }
        req.getSession().setAttribute("form", form);
        req.getRequestDispatcher("pages/form.jsp").forward(req, resp);
    }

    private boolean formIsComplete(Form form, String previousPage, 
      String currentPageCode) {
        return form != null && previousPage != null && 
            previousPage.equals(currentPageCode);
    }

    @SuppressWarnings("unchecked")
    private void setResponses(HttpServletRequest req, Form form) {
        String currentPage = form.getCurrentPage();

        Enumeration<String> params = req.getParameterNames();
        while (params.hasMoreElements()) {
            String paramName = params.nextElement();
            Field field = DynamicFormsHelper.getField(paramName, currentPage, form);
            if (field != null) {
                if (field.getResponses() != null) {
                    field.getResponses().clear();
                }
                String[] values = req.getParameterValues(paramName);
                for (String str : values) {
                    field.getResponses().add(str);
                }
            }
        }
    }

    private static Form backToPreviousPage(Form form) {
        int currentPageIndex = DynamicFormsHelper.currentPageIndex(form);
        for (int i = currentPageIndex - 1; i >= 0; i--) {
            Page page = form.getPages().get(i);
            if (page.isVisible()) {
                form.setCurrentPage(page.getCode());
                break;
            }
        }
        return form;
    }

}

The servlet class handles a user's request to navigate the forms (move to the next or previous page) and collects users’ responses. When a user moves to a "next page" this class calls the rules execution class to invoke the rules engine and then displays pages to the user. If a user reaches the last page of the questionnaire, a summary page is displayed. When a user navigates to a "previous page" the requested page is retrieved from a page list of forms.

The rules execution class uses J2SE to execute rules.

Listing 3. RulesExecutor.java
package com.ibm.wodm.df.web.rulesexecution;

import ilog.rules.res.model.IlrPath;
import ilog.rules.res.session.IlrJ2SESessionFactory;
import ilog.rules.res.session.IlrSessionFactory;
import ilog.rules.res.session.IlrSessionRequest;
import ilog.rules.res.session.IlrSessionResponse;
import ilog.rules.res.session.IlrStatelessSession;

import java.io.PrintWriter;

import com.ibm.wodm.df.models.Form;

public class RulesExecutor {

    public static Form executeRules(Form form) {
        try {
            IlrSessionFactory ilrSessionFactory = createJ2SESessionFactory();

            IlrSessionRequest request = ilrSessionFactory.createRequest();
            request.setInputParameter("form", form);
            request.setRulesetPath(new IlrPath
                ("DynamicFormsRuleApp", "DynamicFormsRules"));

            IlrStatelessSession statelessSession = 
                ilrSessionFactory.createStatelessSession();
            IlrSessionResponse response = statelessSession.execute(request);
            form = (Form) response.getOutputParameters().get("form");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return form;
    }

    private static IlrSessionFactory createJ2SESessionFactory() {
        PrintWriter writer = new PrintWriter(System.out);
        IlrJ2SESessionFactory sessionFactory = new IlrJ2SESessionFactory();
        sessionFactory.setOutput(writer);
        return sessionFactory;
    }
}

The JSP page renders fields based on their types, which are set with rules. Each field is rendered with standard HTML elements. Listing 4 shows how to render the field with type Radio.

Listing 4. Code snippet of forms.jsp for rendering field with type radio
<% if (field.getType() == FieldType.Radio) { %>
	<table> <tr>
<%	    
	for (int i = 0; i < field.getOptions().size(); i++) {
		String option = "";
		if (field.getOptions().get(i) != null) {
			option =  field.getOptions().get(i);   
		}
		boolean checked = false;
        if (field.getResponses() != null
			checked =  field.getResponses().contains(option);
		}
		if (checked) {
%>
<td><span class="label"><%=option%></span></td>
<td><input type="radio" name="<%=field.getCode()%>" class="controlText" 
    size="200px" value="<%=option%>" checked /></td>					
<% } else { %>
<td><span class="label"><%=option%></span></td>
<td><input type="radio" name="<%=field.getCode()%>" class="controlText" 
    size="200px" value="<%=option%>" /></td>
<% } } %>
	</tr> </table>
<% } %>

Figure 10 shows the sample questionnaire implemented using dynamic forms.

Figure 10: Web-based questionnaire (sample page)
Figure 10: Web-based questionnaire (sample page)

Conclusion

Dynamic forms allow you to define and maintain forms using business rules. Through the example described in this article, you have seen how an application can be implemented with business rules and JEE technologies. Dynamic forms provide your application with the flexibility needed to quickly respond to business changes. You can leverage other technologies such as Ajax for dynamically generating Ajax invocation scripts to change forms, and the Dojo framework for generating forms with Dojo widgets.

Acknowledgments

The author would like to thank Ming Li and Michael Liu for reviewing the technical content of this article.


Download

DescriptionNameSize
Dynamic forms downloadDF_workspace.zip32KB

Resources

  • developerWorks BPM zone: Get the latest technical resources on IBM BPM solutions, including downloads, demos, articles, tutorials, events, webcasts, and more.
  • IBM BPM Journal: Get the latest articles and columns on BPM solutions in this quarterly journal, also available in both Kindle and PDF versions.

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into Business process management on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Business process management, WebSphere
ArticleID=838753
ArticleTitle=Develop dynamic forms using WebSphere Operational Decision Management V7.5
publish-date=10032012