Decoupling business process and business data in WebSphere Process Server: A new twist to the MVC pattern

This article describes a development pattern separating the life cycle of a business process and its corresponding business data. This approach provides strong decoupling of the respective life cycles, increasing robustness to cope with inevitable changes. The process keeps only a minimal set of information, while the actual business data is represented by managed Java Persistence API entities in the logic layer. The same entities are exchanged as detached entities between the logic and user interface layers in the form of data transfer objects. After introducing the various layers of the architecture, the article leads you through the steps of implementing a basic application based on this pattern in WebSphere® Integration Developer V7.0 and WebSphere Process Server V7.0.

Walter M. Jenny (wjenny@at.ibm.com), Business Solution Architect, IBM

Author photo of Walter JennyWalter Jenny is a Business Solution Architect in IBM's Software Group. He has over 20 years experience in application development and business process management. Walter holds a Master's degree in Computer Science, a Master’s in Business Administration, and a Doctorate in Business Administration.



01 June 2011

Also available in Chinese

Introduction

Separating the business process from its data has been a longstanding topic in business process management architectures. Such an approach provides strong decoupling of the respective life cycles, making both of them more robust to cope with inevitable changes: the process can be changed using established techniques, while the data can be manipulated independently using well known persistence features. The message model, which defines the contract between the process and its supporting services, is therefore minimal, based only on a small number of key values. This makes versioning fairly straightforward.

This approach also avoids carrying around excessive data that your process most likely does not need, and hence enables it to run more efficiently. In long-running processes, WebSphere Process Server (hereafter called Process Server) stores the state of the business process after each transaction into the Business Process Choreographer (BPC) database. On each write to the database, the Service Data Object (SDO) is serialized and de-serialized on each read operation. If the data is held externally, these steps are not necessary, and the data can be loaded lazily on demand.

All that is required is to keep references between the process and data, and to maintain the status to orchestrate the business process. The approach of detaching data from a process has been discussed several times (for example, in the Claim Check pattern article). This article, however, goes a step further as it shows you how to permanently keep all business data in a separate database and access it via the Java™ Persistence API (JPA). Detached JPA entities are used as data transfer objects (DTOs) between the various layers of the architecture.

For the creation of the data related artifacts, we will borrow some aspects of model-driven development (MDD). This approach can significantly expedite your development process, as it gives you a high level of control over your business data from one common model that is reused across all major architectural layers.

In this article, we use a sample scenario to illustrate how to implement an application following this pattern. The sample application in the Download section demonstrates the approach and contains all the basic building blocks that you can use as a base to develop your own solution. The application comes with a user interface based on the ZK Open Source Framework and includes an efficient way to query the task list.


Main architectural layers

Suppose you are developing a banking application that creates trades in the front office. These trades go through review and approval in the back office, and are eventually executed. The main layers and building blocks of the application are shown in Figure 1.

Figure 1. Main architectural layers
Main architectural layers
  • Since we are using the ZK framework, the Browser merely displays the user interface (UI) components and listens to the user's activity via asynchronous JavaScript and XML (Ajax). In other words, it just reflects the state of the User Interface layer. The synchronization is done automatically by ZK, which is transparent to the application.
  • The User Interface layer runs in the web tier. It controls the UI components in the browser, and communicates with the WebSphere Process Server V7.0 Human Task Manager (HTM) and the logic layer via the client façade Enterprise JavaBeans (EJB). The UI provides basic features to create new trades, query the Process Server task list, and claim and complete the tasks.
  • The Process layer orchestrates the services and creates human tasks. The process has been modeled in WebSphere Business Modeler V7 (hereafter called Business Modeler) and then imported into WebSphere Integration Developer V7.0 (hereafter called Integration Developer). The actual service implementations are invoked as Service Component Architecture (SCA) components in the logic layer.
  • The Logic layer contains the actual application logic, implemented as EJB 3.0 Stateless Session Beans. Exposed as SCAs, these EJBs are invoked by the process. The UI uses the detached JPA entities to communicate with the client façade EJB, which offers coarse-grained access to the business logic and managed JPA entities that are deep enough for the UI layer to perform its work. When a detached entity is received by the facade, a managed version is extracted to be used by the stateless service to perform its business logic.

The remote interface of the Human Task Manager is shown in Figure 2 to illustrate the overall communication.

Figure 2. Using JPA entities as DTOs
Using JPA entities as DTOs
  • The JPA Persistence layer serves two purposes: providing an object relational mapping (ORM) as managed JPA entities for the logic layer, and providing DTOs as detached entities between the logic and UI layers.

    The detached entities are transferred to and from the UI, either as a complete object graph or through lazy loading. As an implementation, we have chosen OpenJPA that is shipped with WebSphere Application Server. The JPA classes are generated from a Unified Modeling Language (UML) model.

  • The underlying Data layer illustrates how the business data is kept in a separate database instance under full control of the application.

The required projects

The four main layers have their own dedicated Integration Developer project, which implements the integration solution shown in Figure 3.

Figure 3. WebSphere Integration Developer integration solution
WebSphere Integration Developer integration solution
  • The Trading_UI project uses the remote interfaces in the Trading_LogicClient project to talk to the Trading_Logic EJBs. The data is exchanged via detached JPA entities.
  • The Trading_Process project also uses the remote interfaces to talk to the EJBs. However, it does not use the JPA objects to ensure a strict separation of process and business data.
  • The Trading_Logic project implements the backend logic comprised of EJB 3.0 Stateless Session Beans. The EJBs create and manipulate the business data and persist it via the Trading_Persistence project.
  • Finally, the Trading_Persistence project holds the annotated Java classes that are used for persistence.

Let's have a closer look at each of these projects and how they are integrated into one consistent application.

The user interface layer

For the user interface, we use the ZK Open Source Framework, an open source Asynchronous JavaScript and XML (Ajax) framework written in Java. It lets you write a Web 2.0-enabled, rich Internet application without writing a single line of JavaScript code. This is a great tool to develop rich user interfaces, which are also easy to maintain. All code is written in Java, and there is no need to dive into arcane JavaScript details. ZK Studio is a plug-in for Eclipse and NetBeans. This makes it fairly easy and intuitive to create a UI project in Integration Developer.

The basic ZK features have already been introduced in Rich Internet applications using ZK, so we will just cover details that we need for our scenario.

The MVC pattern supported in ZK helps to neatly separate the various application layers. In ZK, UI components are declared with ZUML in ZUL files and the functionality to work with the components is implemented in a Java controller class. The controller class extends GenericForwardComposer so components declared in ZUL can be referenced in Java code. This enables events to be automatically forwarded to the controller class to be handled. Let’s see how this works for the main page. First, let’s build a controller class named IndexController, which extends from the GenericForwardComposer (see Listing 1).

Listing 1. Java code for the main controller
package com.bigbank.trading.ui.controller;
public class IndexController extends GenericForwardComposer

Having created this class, build the UI ZUL file, and then reference the associated controller. Do this by specifying the apply attribute (shown in bold) in Listing 2.

Listing 2. ZUL code for the main controller
<?init class="org.zkoss.zkplus.databind.AnnotateDataBinderInit" ?>
<window id="main" border="normal"apply="com.bigbank.trading.ui.controller.IndexController">
    <borderlayout>
        <north>
            <include src="menu.zul" />
        </north>
        <west title="Lists" size="39%" flex="true" splittable="true" 
             collapsible="true">
            <tabbox>
                <tabs id="listTabHeader" ></tabs>
                <tabpanels id="listTabBody"></tabpanels>
            </tabbox>
        </west>
        <center title="Todos" flex="true">
            <tabbox>
                <tabs id="todoTabHeader" ></tabs>
                <tabpanels id="todoTabBody"></tabpanels>
            </tabbox>
        </center>
    </borderlayout>
</window>

Having applied the GenericForwardComposer, you can declare the components within the controller class as private variables. This will be automatically wired to the equivalent ZUL components, defined by matching ID values shown in Listing 3.

Listing 3. Auto wiring

Click to see code listing

Listing 3. Auto wiring

public class IndexController extends GenericForwardComposer
{
    // Auto wired ZUML elements
    private Tabs		listTabHeader;private Tabpanels	listTabBody;private Tabs		todoTabHeader;private Tabpanels	todoTabBody;
    ...

Another neat ZK feature is data binding. The Annotated Data Binding (the first line in Listing 2) allows bean properties to be declared as component attribute values so that the view and model are automatically kept in synch. Under the hood, the data binder calls a bean's getter and setter methods to retrieve, or set values in the UI components.

The main functionality of the UI is implemented in the following Java classes:

  • The central element IndexController is responsible for the creation and control of other dependent components.
  • CreateController allows you to enter some data used to start a new Process Server process.
  • ListController holds a collection of currently available tasks from the Human Task Manager to display them in a ZK Grid. Alternatively, it runs a query against the database and displays them. Each record in the list is represented by a ListEntry.
  • FormController works with the actual data, which is retrieved and saved via the façade EJB. For our example, this is a straightforward form. In a real application, this would likely be a more complex form with many fields. In such a scenario, you might read only the minimal set of data that is required for the initial screen. As the user goes through other parts of the form, the underlying data can be fetched lazily.
  • The SearchController is a helper class implementing basic search functionality.

In Figure 4, you can see the UI design using ZK’s borderlayout. The various lists will be dynamically created and added on the left side, and the actual forms on the right side.

Figure 4. The basic UI
The basic UI

We will revisit the UI when we integrate all the various parts.

The Process layer

For our example scenario, we use a simple process that orchestrates three basic services and two human tasks to approve and review a trade, as shown in Figure 5.

Figure 5. The business process
The business process

A user in the banking front office proposes a new trade by creating a Process Server process instance, which goes through the following process steps:

  • The Create service inserts the database record(s) holding the business data and returns a status indicating if:
    • Review is needed.
    • No review needed.
  • A person in the back office takes a first look at the trade with the Review human task and decides if:
    • Approval is needed.
    • No approval needed.
  • If an approval is needed, a person in yet another role has to make the final decision:
    • Approved, leading to direct execution (and settlement) of the trade.
    • Declined with a subsequent notification.
    • Needs rework, if the trade looks reasonable, but needs some tweaking by the reviewer.
  • Finally, Execute and Notify are two simple components to carry out the business logic.

What is interesting for our discussion is the business item that is carried through the process. It contains only two attributes, just enough to link up the process and the corresponding business data as shown in Figure 6.

Figure 6. The business item
The business item
  • tid is an integer holding the Trade ID, the unique key created by JPA when the initial record is inserted into the database. In the following service invocations, it is used to retrieve the business data for a particular Process Server process instance.
  • status is a String returned by the services, and also the human tasks. Based on its value, the process orchestration controls the process flow. To increase type safety, this could also be an enumeration.

Once imported into Integration Developer, the corresponding SDO is depicted in Figure 7.

Figure 7. The corresponding SDO
The corresponding SDO

How is this SDO used in the process? Basically, it provides the linkage between the process and the related data when services or human tasks are invoked.

However, you also need a “backwards” reference to associate a BPEL process with the appropriate set of business data. A natural choice for that reference is the Process Server process instance ID (PIID). It is dynamically assigned by Process Server. You can retrieve it through a Java snippet right at the start of the process and store it as an attribute in a database table as shown in Figure 8 (see also Figure 13).

Figure 8. Setting the PIID
Setting the PIID

Figure 8 shows the imported process model in Integration Developer with the “Assign PIID” snippet. Right after the instantiation of the process, it sets the PIID value in the trade SDO, as shown in Listing 4.

Listing 4. Setting the PIID in the SDO
// Set the linkage from the process to the persistent data
com.ibm.bpe.api.ProcessInstanceData pid = processInstance();
String status = Trade.getString( "status" );
Trade.setString("status", "PIID=" + pid.getID().toString() + "|" + status);

This value will be picked up by the Create EJB and stored as an attribute in the Trade table.

The Logic layer

This layer contains the lion’s share of the actual business logic, implemented as Stateless Enterprise JavaBeans (EJB). For invocations from the Process Server process, the beans are exposed as SCA components. Implementing the logic as EJBs allows you to take advantage of dependency injection to get hold of references to other EJBs and the JPA persistence manager. If performance is important, you can access the EJBs via their local interface (call-by-reference), unless your topology requires remote access.

The EJB imports use the Java Naming and Directory Interface (JNDI) remote interfaces for the communication, wired up in the assembly diagram with Java bridges shown in Figure 9.

Figure 9. Wiring up the EJB in the Assembly Diagram
Wiring up the EJB in the Assembly Diagram

The bridges are responsible for the communication between the process and the services. They are used in the following two scenarios shown in Figure 10.

Figure 10. Bridge for the Create EJB Import
Bridge for the Create EJB Import

As one of the first steps in the process, the corresponding business data has to be created in the database. Figure 10 zooms into one of the details of Figure 1 and you can see how the Create EJB is invoked with the parameters. The return value is the trade id (tid), the key generated when inserting the database record. To keep our example simple, we also return the status value that is used to control the process flow.

Listing 5 shows the simplified code for the Create EJB Import.

Listing 5. Java code in the Create EJB Import
package com.bigbank.trading.bridge;

public DataObject Create(DataObject input)
{
    int tid = locateService_CreateEJBRemotePartner().
      createTrade(input.getString("/status"));
    input.setInt("/tid", tid);
    return (input);
}
Figure 11. Bridge for the following EJB imports
Bridge for the following EJB imports

The following services shown in Figure 11 are invoked with the trade id (tid). Figure 11 zooms into another detail of Figure 1: how the Execute EJB is invoked with the tid as a key to retrieve the business data to carry out whichever business logic is required. Again, it returns the status value that is used to control the process flow.

Listing 6 shows the simplified code of the following EJB imports.

Listing 6. Java code in the following EJB Imports
package com.bigbank.trading.bridge;

public DataObject Execute(DataObject input)
{
 String status = locateService_ExecuteEJBRemotePartner().executeTrade(input.getInt
  ("/tid"));
 input.setString("/status", status);
 return (input);
}

Since the business data is persisted through JPA, we use an EntityManager to create and remove persistent entity instances, to find entities by the entity’s primary key, and to run queries. With a container-managed entity manager, the persistence context is automatically propagated by the container to all application components that use the EntityManager instance within a single Java Transaction Architecture (JTA) commit scope. To obtain an EntityManager instance, we simply inject the entity manager into the application component shown in Listing 7.

Listing 7. Dependency injection to obtain a persistence context reference
@Stateless
public class CreateEJB implements CreateEJBRemote
{
    @PersistenceContext(unitName = "Trading-JPA")
    private EntityManager entityManager;
	...

The data source is configured with a right-click on the Trading_LogicEAR project and selecting Java EE > Open WebSphere Application Server Deployment (Figure 12).

Figure 12. WebSphere Application Server deployment
WebSphere Application Server deployment

We use the built-in Derby database for this example, with the physical database file residing in the temporary directory. See the application in the Download section for the detailed configuration.

The Persistence layer

One important piece is missing: the business data, which is the life blood of every application. As you might have experienced, the details of the data models tend to change frequently during the development cycle. Attributes are added, removed, or changed all the time. To make it easier to keep the respective models and code in sync, we borrow some aspects of model-driven development (MDD) to capture and communicate the high level requirements and also as a base to create artifacts that become part of the overall solution.

To this end, we create a Unified Modeling Language (UML) model in Rational® Software Architect for WebSphere Software, V7.5.5 (RSA), and then use a transformation to create the more detailed models and actual code. Figure 13 shows the data model from a conceptual level (also sometimes referred to as a domain model).

Figure 13. The conceptual data model
The conceptual data model

The main entities are:

  • Trade: This represents a physical trade that is being carried out for a customer. The TradeDetails contain more specific details.
  • Customer: For whom the trade will be carried out.
  • Settlement: If the Trade goes through.

Since we want to leverage RSA’s built-in UML-to-JPA transformation to create the actual JPA entities, we enrich this conceptual model with the appropriate details and stereotypes shown in Figure 14.

Figure 14. The logical data model
The logical data model

At this logical level, we have added a couple of UML-to-JPA stereotypes from the transformation profile:

  • A persistence entity is represented by the stereotype <<Entity>>, a lightweight Java class whose state is persisted to a table in a relational database. Instances of such an entity correspond to individual rows in the table.
  • The stereotype <<Id>> declares one or more fields, which together form the persistent identity of an instance. This becomes the primary key in the physical model.
  • To allow the persistence implementation to assign a unique value to your identity fields automatically, use the stereotype <<GeneratedValue>>. Set to GeneratorType.IDENTITY. The database assigns an identity value on an insert operation. For the Trade entity, the trade id (tid) is kept in the process as a reference to retrieve the corresponding data from the database. You will remember that it is also one of the attributes in the business item shown in Figure 6.
  • <<RelationshipOptions>> defines the associations between two classes that have the <<Entity>> stereotype applied, such as Cascade and Fetch details. The fetch relationship between the entities Trade and TradeDetails is set to Lazy, which helps your performance and memory footprint, as you will see in the next section.

The persistence metadata is specified using either the Java 5 annotations defined in the javax.persistence package, XML mapping files, or a mixture of both. For better readability, the relationships in our scenario are expressed through object or relational metadata specified directly in the entity class files. This setting is configured in the transformation. The following UML-to-JPA transformation generates JPA entities and Java code from the UML model elements (Figure 15).

Figure 15. UML-to-JPA transformation
ML-to-JPA transformation

For our application, the transformation creates the JPA entities in a project called “Trading_Persistence”. It contains all the Java classes we have modeled in UML.

The Data layer

For the basic application, the data layer is very simple. You create a physical data model in Integration Developer by using the JPA tools: Right-click on the Trading_Persistence project, select JPA Tools > Generate DDL and the following wizard appears as shown in Figure 16.

Figure 16. The physical data model: Generate DDL
The physical data model: Generate DDL

This creates the physical data model in a file called Table.ddl in the META-INF directory. Switch to the Data perspective in Integration Developer, create a database connection matching the one shown in Figure 11, and run the script against the database with a right-click and select Run SQL. Figure 17 shows the results in the data perspective.

Figure 17. Creating the database schema
Creating the database schema

Such a “lightweight MDD approach” gives you control and consistency over the development cycle. When you have to change the conceptual or logical data model, you can easily recreate the dependent models and have all your JPA entities in sync.

We now have all four main building blocks in Integration Developer as shown in Figure 18.

Figure 18. All required projects
All required projects

Now let’s run the actual application.


Running the application

To get started with the application, deploy the code in Process Server 7.0 and go to http://localhost:9080/Trading_UI/index.zul in your browser (or whatever port you have installed your server on). To be authenticated and then authorized to use the Process Server API, you have to create a user and then assign the security role “TradingUsers” for the UI application in the Integrated Solutions Console.

Creating process instances

When you start the application for the first time, there will be no processes, and hence, no tasks to work on. You need to create a few processes in Process Server. In the web application, click Asset >Create Trade … to open a form that allows you to enter data and create a new process as shown in Figure 19.

Figure 19. Creating process instances
Creating process instances

The create form is opened in a new tab in the “Todos” section. It is managed by a CreateController that is wired to a ZK form, along with the respective ZK data binding. The “Create” button calls the start() method of the CreateController shown in Listing 8 (with error handling removed for clarity).

Listing 8. Starting a new process
public void start()
{
    ...
    binder.saveAll();
    DataObject sdo = getStartDataObject();
    sdo.createDataObject(0);
    sdo.setInt("/Input/tid", 0);
    String input = "name=" + trade.getName() + …
    sdo.setString("/Input/status", input);
    // start business process asynchronously
    PIID piid = bfm.sendMessage(startActivity.getServiceTemplateID(),
                       startActivity.getActivityTemplateID(),
                       new ClientObjectWrapper(sdo));
    alert("New Trade created");
}

As Listing 9 shows, you simply store the field values from the data binding in a concatenated string that is used in the process when invoking the create EJB (in a more robust implementation, this could be a serialized XML file)

Listing 9. The Create EJB
@Stateless
public class CreateEJB implements CreateEJBRemote
{
    @PersistenceContext(unitName = "Trading-JPA")
    private EntityManager entityManager;

    public String createTrade(String status)
    {
        Trade trade = new Trade();
        trade.setTradeDetails(new TradeDetails());
        // set values …

        // Simple business rule
        if (trade.getTradeDetails().getValue() > 1000)
        {
            trade.getTradeDetails().setReview("needs review");
        }
        else
        {
            trade.getTradeDetails().setReview("no review needed");
        }
        entityManager.persist(trade);
        int newKey = trade.getTid();
        return (newKey + "|" + trade.getTradeDetails().getReview());
    }
}

JPA makes your life easy here. You simply call the merge() method of the entityManager to persist the newly created entity. JPA runs all the required JDBC code in the background and commits the changes to the database.

When creating the process, note the simple business rule that decides if a review is necessary. This will be driven by the value of the status field used in the process orchestration. Clearly in a real world scenario, an external business rules engine, such as ILOG Rules, might be used to externalize this business logic.

Implementing the task list

Once you have a created a couple of new processes, refresh the UI to see the tasks, waiting for a human interaction to carry out the “Review” step (Figure 20).

Figure 20. The task list
The task list

Building an efficient task list containing a large number of tasks or attributes (for example, columns in a grid) can be rather challenging. While the HTM provides a versatile query() method to retrieve pertinent technical information, the tricky part is dealing with a potentially large number of tasks or attributes in an efficient way. HTM offers two built-in features to this end: custom properties and query tables.

However, both approaches have their ramifications. The use of custom properties can be detrimental to the performance if you need more than a handful. Query tables in conjunction with materialized views perform well, but they use native database joins to retrieve the related records. This means both sets of tables have to live in the same database instance.

If you keep the technical database completely separate from the business database, your design approach comes to the rescue. It makes it easy to implement an efficient task list. First, use ZK’s Grid component with data binding to build the list as shown in Listing 10.

Listing 10. ZUL code in the UI layer for the task list
<tabpanel apply="com.bigbank.trading.ui.controller.ListController">

<grid id="mainGrid" mold="paging" pageSize="6"
      sizedByContent="true" model="@{controller.gridModel}">
    <columns menupopup="auto" sizable="true">
        <column label="Status" sort="auto(Status)" />
                   ...
    </columns>
    <rows>
        <row self="@{each='list'}">
            <toolbarbutton
                style="font-weight:bold;color:blue;font-style_underline"
                label="@{list.status}" onClick="controller.addTab( self )">
                <custom-attributes listEntry="@{list}" />
            </toolbarbutton>
            <label value="@{list.action}" />
                   ...
        </row>
    </rows>
</grid>

The Grid control provides a few nice features, such as pagination, sorting and hiding of columns, and customer rendering. As you can see in Listing 10 above, the ListController is wired in the ZUL file.

What ListController does is similar to the query table feature in Process Server. However, the difference is that you are carrying out this join in Java code. In addition, you implement a basic pagination mechanism by fetching the data page by page. First, determine the number of human tasks assigned to the current user (Listing 11).

Listing 11. Java code in the UI layer to determine the number of human tasks
public void fillTaskList(int skipTuples)
{
    resultSet = htm.query("COUNT(*)",
            "PROCESS_TEMPLATE.NAME = 'Trade' AND " +
            "(ACTIVITY.STATE = ACTIVITY.STATE.STATE_READY OR " +
            "ACTIVITY.STATE = ACTIVITY.STATE.STATE_CLAIMED) " +
            "AND WORK_ITEM.REASON=WORK_ITEM.REASON.REASON_POTENTIAL_OWNER ",
            null, null, null, null);
    if (resultSet.next())
    {
        taskNumber = resultSet.getInteger(1);
    }
    ...

You need this number to indicate the number of available tasks in the Grid. In the next step, query the HTM related details for the current page. For this purpose, the query call accepts the useful “skipTuples” and “threshold” parameters as shown in Listing 12.

Listing 12. Java code in the UI layer to query the task details
    resultSet =
        htm.query("TASK.TKIID,TASK.STATE,TASK.NAME,PROCESS_INSTANCE.PIID",
          "PROCESS_TEMPLATE.NAME = 'Trade' AND " +
          "(ACTIVITY.STATE = ACTIVITY.STATE.STATE_READY OR " +
          "ACTIVITY.STATE = ACTIVITY.STATE.STATE_CLAIMED) " +
          "AND WORK_ITEM.REASON=WORK_ITEM.REASON.REASON_POTENTIAL_OWNER ",
          // orderByClause
          null,
          // skipTuplesskipTuples,// thresholdthreshold,
          // timeZone
          null);
    int location = skipTuples;
    while (resultSet.next())
    {
        ListEntry listEntry = gridModel.get(location++);
        // Assign the ResultSet details to the listEntry
        ...
        // Add the PIID to the list for the EJB call
        piidCollection.add(listEntry.getPiid());
    }

The last step is to call your façade EJB to retrieve the required business data (for example, columns in the list). These attributes are then assigned to the ListEntries, which serve as the Grid model shown in Listing 13.

Listing 13. Java code in the UI layer to query the business data
    Map<String, Trade> piidMap =
            clientEJBRemote.findTradesByPiid(piidCollection);
    ListIterator<ListEntry> iterator = gridModel.listIterator(skipTuples);
    recordNumber = 0;
    // Set the retrieved trades to the list entries
    while (iterator.hasNext() && recordNumber++ < threshold)
    {
        ListEntry listEntry = iterator.next();
        listEntry.setTrade(piidMap.get(listEntry.getPiid().toString()));
    }
}

The findTradesByPiid method resembles an "outer join", implemented programmatically. To improve performance of this query, an index for the piid attribute has been added as shown in Listing 14.

Listing 14. Java code in the logic layer to query the business data
public Map<String, Trade> findTradesByPiid(Collection<PIID> piidCollection)
{
    Map<String, Trade> piidMap = new HashMap<String, Trade>();
    String queryText = "select trade from Trade trade where trade.piid in ( ";
    for (PIID piid : piidCollection)
    {
        queryText += "'" + piid.toString() + "',";
    }
    queryText += ") order by trade.tid";
    Query queryTrades = entityManager.createQuery(queryText);
    List<Trade> trades = queryTrades.getResultList();

    for (PIID piid : piidCollection)
    {
        for (Trade trade : trades)
        {
            if (trade.getPiid().equals(piid.toString()))
            {
                piidMap.put(trade.getPiid(), trade);
                break;
            }
        }
    }
    return (piidMap);
}

This sequence of steps is triggered initially when the list is empty. When the user scrolls to a Grid page containing an empty com.bigbank.trading.ui.model.ListEntry, it triggers a fetch of the next tasks as shown in Listing 15.

Listing 15. Java code in the UI layer to trigger query the business data
public String getStatus()
{
    if (status == null)
    {
        listController.fetchNextTasks(this);
    }
    return status;
}

public void fetchNextTasks(ListEntry listEntry)
{
    int index = gridModel.indexOf(listEntry);
    fillTaskList(index);
}

This task list implementation is efficient even if you have a large number of attributes (for example, columns in the Grid) or tasks assigned. In a typical scenario, even if a user has several thousand tasks assigned, he or she is likely to work only on a few of them. Our implementation fetches the process and business data lazily on demand. This can significantly reduce the response time and memory footprint.

Opening the Todo form

By clicking on the first column, a new form is opened in the “Todos” section as shown in Figure 21.

Figure 21. The Todo form
The Todo form

The form is managed by FormController, which uses the façade EJB to retrieve the initial set of data via the detached JPA entities. It populates the fields with ZK’s data binding as shown in Listing 16.

Listing 16. ZUL code in the UI layer for the Todo form
<tabpanel apply="com.bigbank.trading.ui.controller.FormController">
    <grid fixedLayout="false" width="98%" >
        <rows>
            <row>
                <label value="Name" />
                <textbox
                    constraint="no negative: please enter positive value"
                    value="@{controller.asset.name}"
                    style="font-weight:bold" />
                    …
            </row>
        </rows>
    </grid>
    <hbox width="100%" pack="center" spacing="10px">
        <button id="claimButton" label="Claim"
            onClick="controller.claim()" image="/images/cog.png"
            tooltiptext="Claims the Task" />
            …
    </hbox>
</tabpanel>

In most business applications, this form contains much more information than our small application. Some of it might only be required if the user selects a tab or opens another window, therefore, we load it lazily via the logic and JPA layer as shown in Listing 17.

Listing 17. Java code in the logic layer for the Todo form
public TradeDetails findTradeDetails(Trade trade)
{
    trade = (Trade) entityManager.find(Trade.class, tid);
    return (trade.getTradeDetails());
}

For more complex navigation, the Java Persistence Query Language also supports path expressions that can navigate using multiple single valued relationship fields, such as td.trade.tid (Listing 18).

Listing 18. Java code in the logic layer using path expressions
Query query = entityManager.createQuery("select td from TradeDetails td where 
 td.trade.tid = " + tid);
tradeDetails = (TradeDetails)query.getSingleResult();

Working with the Todo form

Initially, all fields in the form are read only. The users can browse the data and decide if they want to work on the task. To make this happen, FormController calls HTM through a couple of methods:

  • claim(): To claim a ready task instance for user processing.
  • cancelClaim(): To cancel the claim of an task instance.
  • complete(): To complete a claimed task instance.

Once the user claims a task, the fields are enabled and the business data can be worked on. The “State” column in the task list will be updated to reflect the “Claimed” state as shown in Figure 22.

Figure 22. The claimed Todo form
The claimed Todo form

At this point, the user can also save any pending changes without completing the task. Again, this happens by sending the detached entities as DTOs to the façade EJB to persist them as managed JPA entities. Pressing the Complete button tells HTM that you are done with task. The associated data is saved and the form closed as shown in Listing 19.

Listing 19. Calling HTM complete() in the UI layer
public void complete()
{
    ...
    ClientObjectWrapper output = lhtm.createOutputMessage(tkiid);
    DataObject sdo = (DataObject) output.getObject();
    sdo.createDataObject(0);
    sdo.setInt("/Output/tid", listEntry.getAsset().getId());
    sdo.setString("/Output/status", "yes"); // asset.getStatus() );
    lhtm.complete(tkiid, output);
    System.out.println("# UI # FormController::complete successful");
    saveAsset();
    closeTab();
}

The business process will then go through the next steps of the process logic, and might require an additional approval step, typically by a person in another role.

Figure 23 recaps the main steps of creating a task list, opening a form, working with the business data, and then completing the task (with some details removed for clarity).

Figure 23. Sequence diagram of the main steps
Sequence diagram of the main steps

Searching for business data

One of the advantages we discussed at the beginning is the ability to leverage database functionality to search for business data. This section gives you an example of how the database search facilities can be leveraged. Click on Asset >Search Trade to open a search panel and click on Search as shown in Figure 24.

Figure 24. Searching for business data
Searching for business data

The search criteria are used to build a database query in the façade EJB (again, simplified for readability) as shown in Listing 20.

Listing 20. Java code in the logic layer for the search functionality
public Collection<Trade> searchTrade(Trade trade)
{
    Collection<Trade> trades = null;
    String queryText = "select trade from Trade trade where ";
    if (trade.getTid() != 0)
    {
        queryText += "trade.tid = " + trade.getTid();
    }
    if (trade.getName() != null && !trade.getName().equals(""))
    {
        queryText += " and trade.name like '" + trade.getName() + "'";
    }
    Query query = entityManager.createQuery(queryText);
    trades = query.getResultList();
    return (trades);
}

The collection of records is then used as the model for a new search list. It is displayed within a new tab in the “Lists” section of the interface (with the “Refresh” button hidden). When you open a Trade from this list, the Todo form is re-used with a “Save” button now available as shown in Figure 25.

Figure 25. Working with the business data, independent of the process
Working with the business data independent of the process

This shows another important advantage of the separation of process and business data.


Conclusion

In this article, you have learned how to separate a WebSphere Process Server business process from its business data. This approach provides strong decoupling of the respective life cycles, which can be conducive to performance and takes full advantage of the Java Persistence API with both managed and detached entities. The approach leverages a few MDD concepts to expedite your development process. Starting from one common UML model down to the physical JPA classes provides control and consistency over your solution artifacts, accelerating your overall development cycle.


Download

DescriptionNameSize
Sample scenarioTrading.zip3.4KB

Resources

Learn

Discuss

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. Select information in your profile (name, country/region, and company) is displayed to the public and will accompany any content you post. 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=665189
ArticleTitle=Decoupling business process and business data in WebSphere Process Server: A new twist to the MVC pattern
publish-date=06012011