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.
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
- 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
- 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 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
- 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.
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
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
IndexControlleris responsible for the creation and control of other dependent components. -
CreateControllerallows you to enter some data used to start a new Process Server process. -
ListControllerholds a collection of currently available tasks from the Human Task Manager to display them in a ZKGrid. Alternatively, it runs a query against the database and displays them. Each record in the list is represented by aListEntry. -
FormControllerworks 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
SearchControlleris 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
We will revisit the UI when we integrate all the various parts.
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
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
- 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
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
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.
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
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
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
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
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.
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 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
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 toGeneratorType.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 asCascadeandFetchdetails. The fetch relationship between the entitiesTradeandTradeDetailsis set toLazy, 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
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.
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
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
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
Now let’s run the actual 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.
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
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.
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
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,
// skipTuples
skipTuples,
// threshold
threshold,
// 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.
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 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(); |
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
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
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
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
This shows another important advantage of the separation of process and business data.
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.
| Description | Name | Size | Download method |
|---|---|---|---|
| Sample scenario | Trading.zip | 3.4KB | HTTP |
Information about download methods
Learn
-
Improving application efficiency with the Claim Check pattern in
WebSphere Integration Developer V6.2.0.1: developerWorks article
describing the Claim Check pattern in the context of the Service Component
Architecture.
-
Rich
Internet applications using ZK: developerWorks article introducing
ZK with a real-world example of its use running on Apache Tomcat and
connecting to a MySQL database.
-
Leveraging OpenJPA with WebSphere Application Server V6.1 (Updated
for JPA 2.0): developerWorks article introducing OpenJPA with a
complete example of how to take advantage of some of these features using
WebSphere Application Server V6.1.
-
Developing JPA Applications with WebSphere Application Server
Community Edition: developerWorks tutorial walking you through all
the nuances of JPA, developing a sample application using Eclipse and
WebSphere Application Server Community Edition.
-
Developing web applications with the Java Persistence API and
JavaServer Faces: developerWorks article showing how you can use
JPA directly with web applications.
-
Create and configure a JPA project: Information Center tutorial
showing how to create and configure a Java Persistence API project.
-
Apache OpenJPA: The homepage of
the Java persistence project that you can use as a standalone POJO
persistence layer or integrated into any Java EE compliant container and
many other lightweight frameworks, such as Tomcat and Spring.
-
JPA 2.0 Road
Map: This is being developed in trunk and will be a JPA 2.0
certified release of the JSR-317 specification.
-
OpenJPA
Documentation: The online documentation covering the latest
releases.
-
JSR 317 Java Persistence 2.0: The final release of the
specification.
-
JSR 303 Bean Validation
specification: This defines a metadata model and API for JavaBean
validation based on annotations, with overrides and extended metadata
through the use of XML validation descriptors.
-
Comment lines by Kevin Sutter: An update on Java Persistence API
2.0: developerWorks article introducing some of the new concepts
and features of the updated specification.
-
JSR 220 Enterprise JavaBeans 3.0: The final release of the
specification.
-
Lean service architectures with Java EE 6: Interesting JavaWorld
article covering elements and patterns of a lean SOA.
-
Business
Process management Samples & Tutorials: EJB Invocation: Sample
showing how you can invoke an EJB from your BPEL process.
-
Using the Query Table Builder in WebSphere Process Server V6.2:
developerWorks article showing you how to use the query table feature
introduced in Process Server V6.2.
-
Tips and tricks when using query tables in WebSphere Process Server
V7: developerWorks article providing development and
troubleshooting tips when working with query tables in WebSphere Process
Server V7 and WebSphere Business Space.
-
PA71:
WebSphere Process Server - Query Table Builder: SupportPac
providing the tooling (called Query Table Builder) and the documentation
that is necessary to develop and work with Business Process Choreographer
query tables.
-
Selecting a human task with custom properties using WebSphere Process
Server: developerWorks article discussing how to use tasks in
metadata called properties.
Discuss





