Software development projects need to demonstrate value more rapidly than ever before. As a result, developers continue to seek ways to quickly develop end-to-end applications in an iterative approach in order to garner project support early and to adapt the design of the software as required by the stakeholders. By leveraging middleware such as WebSphere Process Server (hereafter called Process Server), organizations can more quickly develop and deploy applications, complete with a web 2.0 user interface, transactional backend support for business logic, and connectivity to persistent repositories such as relational databases. And this is just the tip of the iceberg of capabilities that are provided by WebSphere Process Server!
To address organizational requirements to quickly develop usable web interfaces for developed applications, Process Server includes Business Space. Business Space is web 2.0 browser-based user interface that includes a number of out-of-the-box widgets that can be customized to meet your application needs. One such out-of-the-box widget is the Task Information widget, which provides a form entry screen for capturing data needed by steps in your application. By simply creating a business interface, you can generate an HTML form that includes web 2.0 Dojo widgets for capturing data from users in order to send to Process Server for processing. The Task Information widget parses and formats the user-entered data from HTML into an XML message that gets sent to Process Server. From there this data can kick off a business process, invoke business rules, or be saved in a relational database or many other scenarios. And best of all, this is an out-of-the-box capability that enables you to rapidly build applications that demonstrate value early. Yet when you need more advanced capabilities, the industrial-strength middleware is there ready to scale to the most demanding of application requirements.
Enhance the generated HTML forms
Figure 1 below shows an example of the Task Information widget running in Business Space with HTML fields for capturing data as part of a Job Hiring Process. This generated form is fully functional and can be used as is to collect data from users in order to kick off the business process; but with a few modifications, you can make Figure 1 look like Figure 2.
Figure 1. Default generated HTML-Dojo form
Figure 2 captures the same HTML form data, but is organized more naturally and provides drop-down lists to make data entry easier for the end users. Additionally, information like current date is gathered and added to the HTML form to reduce the data entry burden on the end users.
Figure 2. Desired DOJO-HTML form
The code snippets and source code included for download with this article are taken from the beginning steps of a Human Resource's hiring process. In this process, the first step is job requisition, where a hiring manager posts a job requisition to be filled. In this article, you'll see how to build this business process using WebSphere Integration Developer V7 (hereafter called Integration Developer). Figure 3 below shows the high-level architecture of the system used in this article.
Figure 3. Architecture deployment diagram
While the concepts and steps in this article should apply to different databases and different versions of Process Server, the system described in this article was developed with Integration Developer V7 with an embedded WebSphere Process Server V7 test environment. Additionally, the database used was DB2 V9.7.
Developing the data tier using JPA
In general, it's usually a good idea to sketch out the user interface of your solution by meeting with the users of the system (that is, storyboarding). These user interface (UI) mock-ups help to identify fields to be saved in the database, data to be fetched from other places, security roles, and so on. Therefore, it's often best to design in a top-down fashion. However, once the design is in place, it is often convenient to program in a bottom-up fashion. In many cases, the upper tiers in a layered architecture depend on artifacts from the lower tiers and by programming from the bottom up, these dependencies are reduced, which makes writing a linear document more straightforward.
We begin by seeding the database with initialization data from our UI mock-ups.
Creating a DB2 database with initialization data
- Create a DB2 database called
DEVWORKSto hold the initialization data.
Figure 4. Create DB2 database
- Start Integration Developer and switch to the Data perspective.
While you could certainly seed the database using the DB2 command interface or whatever database tooling you chose for other databases, using the Integration Developer Data perspective provides this capability, too. Additionally, by setting up a connection from Integration Developer to DB2 allows easier auto-discovery of the JPA entities.
- Create a new connection to the database you created by selecting the
Database Connections folder in the Data Source Explorer
view and clicking New. Fill in the information in the New
Connection wizard as appropriate for your database. Figure 5 shows an
example.
Figure 5. Create a new database connection
- From this new database connection, now displayed in the Data Source
Explorer view, right-click the database (below the connection name)
and select New SQL Script , as shown in Figure 6.
Figure 6. New SQL Script
- Copy the contents of Listing 1 into the Script1.sql file, then
right-click in the file and select Run SQL.
Listing 1. Database Initialization SQLdrop TABLE DB2ADMIN.JobReq; create TABLE DB2ADMIN.JobReq ( reqID integer primary key generated always as identity, datePosted timestamp, jobTitle varchar(100), jobType varchar(100), jobLevel varchar(100), country varchar(100), city varchar(100), state varchar(100), zipcode varchar(100), hiringMgr varchar(100), hiringEmail varchar(100), division varchar(100), applyByDate date, jobDesc varchar(1024) ); drop TABLE DB2ADMIN.UserList; create TABLE DB2ADMIN.UserList ( userid varchar(100) primary key not null, fullname varchar(100), email varchar(100) ); insert INTO DB2ADMIN.UserList (userid, fullname, email) VALUES ('admin','Bill Griffith','wgriffith@us.ibm.com'); insert INTO DB2ADMIN.UserList (userid, fullname, email) VALUES ('joe','Joe Blow','joe@acme.com'); drop TABLE DB2ADMIN.PickListItem; create TABLE DB2ADMIN.PickListItem ( id integer primary key generated always as identity, name varchar(100), type varchar(100) ); insert INTO DB2ADMIN.PickListItem(name, type)VALUES ('IT Specialist', 'JobType'); insert INTO DB2ADMIN.PickListItem(name, type)VALUES ('IT Architect', 'JobType'); insert INTO DB2ADMIN.PickListItem(name, type)VALUES ('Manager', 'JobType'); insert INTO DB2ADMIN.PickListItem(name, type)VALUES ('Executive', 'JobType'); insert INTO DB2ADMIN.PickListItem(name, type)VALUES ('Software', 'Division'); insert INTO DB2ADMIN.PickListItem(name, type)VALUES ('Hardware', 'Division'); insert INTO DB2ADMIN.PickListItem(name, type)VALUES ('Sales', 'Division'); insert INTO DB2ADMIN.PickListItem(name, type)VALUES ('Services', 'Division'); insert INTO DB2ADMIN.PickListItem(name, type)VALUES ('Band 6', 'JobLevel'); insert INTO DB2ADMIN.PickListItem(name, type)VALUES ('Band 7', 'JobLevel'); insert INTO DB2ADMIN.PickListItem(name, type)VALUES ('Band 8', 'JobLevel'); insert INTO DB2ADMIN.PickListItem(name, type)VALUES ('Band 9', 'JobLevel');
Note: This database design was chosen for simplicity in explaining the topics of this article and is not indicative of what a good normalized database design should look like.
You could use JDBC to connect to the database and retrieve records, but with Process Server V7, a Java Persistence API (JPA) provider is embedded and free to use. JPA provides an object-relational mapping (ORM) layer where developers can work with Java objects instead of having to write lots of SQL code. JPA really starts to shine when objects have a parent-child relationship or an inheritance hierarchy!
WebSphere Integration Developer V7 includes a JPA perspective that simplifies JPA configuration. One nice feature is the ability to automatically generate Java objects as JPA entities from the table structure of a relational database. Since you created the database structure in Listing 1, you can use this as the foundation for your Java objects and, best of all, the tooling generates the Java code for you!
- Create a new JPA project in Integration Developer.
- Specify
DevWorksJPAas the project name, then deselect Add project to an EAR because you'll add the project to the integration project later. Ensure that the target runtime is WebSphere Process Server V7.0 and the configuration is Utility JPA project with Java 5.0, then click Next. - Ensure that Generic is selected as the platform then select
DEVWORKSas the connection. - Check Override default schema from connection and select DB2ADMIN as the schema.
- Leave Server runtime for JPA implementation as is, but change persistent class management to Annotated classes must be listed in\ persistence.xml.
- Click Yes when prompted to change to the JPA perspective.
- Right-click the
DevWorksJPAproject and select JPA Tools => Generate Entities. - Ensure that the DEVWORKS connection and DB2ADMIN schema are selected, then click Next.
- Specify
com.ibm.dwexample.entitiesas the package name and check Synchronize Classes in persistence.xml, as shown in Figure 7. - Select all of the tables and click Finish.
Figure 7. Generate JPA entities from DB2 tables
You can examine and even modify the generated Java code from this wizard.
Configure JPA for Process Server runtime
JPA can run on an application container like Process Server or in a standalone fashion for simplified testing. However, in this article JPA is instantiated within the Process Server business process and thus participates in the Process Server transactional capabilities. Therefore, you need to use a transactional data source running on Process Server, as well as configure the persistence context to use a Java Transaction API (JTA) transaction type.
Process Server has extensive transactional capabilities that, in many cases, can be declaratively specified using the qualifiers on the interfaces, references, and implementation components. JPA can hook into these global transactions or run in a local transaction. Furthermore, Process Server provides a compensation capability that allows you to commit transactions that you can later undo (for example, cancel a purchase after it was made successfully.). This article won't go into depth about the transactional settings of Process Server; but you can check Resources for links to articles on this subject. To simplify this article, you'll just use the default transactional qualifiers and behavior.
- Select the DevWorksJPA project and choose JPA Tools => Configure Project for JDBC Deployment.
- Select the
DEVWORKSconnection, then check Schema and selectDB2ADMIN. - Uncheck Deploy JDBC Connection information to server and click
OK, as shown in Figure 8.
Figure 8. JPA deployment options
- The value you enter for the data source must match the name of the
data source running on Process Server. Figure 9 shows a screenshot of
the DB2 data source used in this article.
Figure 9. Data Source on WPS
- Open persistence.xml and select JTA as the Transaction
Type, because you want JPA to use the data source managed by
Process Server, as shown in Figure 10.
Figure 10. JPA data source type
- Open the Jobreq.java file and select the
reqidfield. - In the JPA Details view, check Primary Key Generation and
select Identity as the strategy, as shown in Figure 11.
Figure 11. JPA entity primary key strategy
You may have noticed that the DB2 SQL (see Listing 1) has the primary key for the
Jobreqtable and thePickListItemtable set to a generated Identity by DB2. This allows DB2 to automatically create the primary keys with nice sequential numbers like 100 for the job requisition form that users often use to identify these entities in conversation (for example, "Sue, I have a great candidate for job req #121").Figure 11 just configures JPA to retrieve this value from DB2 when DB2 creates this key.
- Now repeat this step for the
Picklistitem.javaentity. - Leave the
Userlist.javaentity alone, because you won't automatically generate keys from the database since the login ID will be used as the primary key.
You've now generated Java objects that can retrieve records from DB2 and persist records back to DB2, and you haven’t written a single line of Java code or a single line of SQL code (assuming a seeded database)! Now you'll build a simple business process to coordinate the steps of the workflow.
Building a Process Server project
Process Server is often used as an integration server for connecting disparate systems together into a business process, but it can also be used to coordinate human workflow running on one system. With the generated user interface represented in Business Space and the backend capabilities of a full JEE engine, the value of designing workflow systems using Process Server is pretty compelling.
- Begin by creating a new Business Integration Module project from the
File menu of Integration Developer. Name the project
DevWorks, then click Yes to switch to the Business Integration perspective. - Use the New Business Object wizard to create the JobReq, PicklistItem, User, and UserDataStore business objects. Alternatively, you can simply copy the XSD files from the Downloads section into the DevWorks project.
- Override the default namespace and specify
http://com.ibm.dwexample.bo. - Add fields that correspond to each column of the database.
Figure 12 shows the business objects that are used for this article. The Picklist business object is designed to simplify interfacing with DOJO data stores, as you'll see later in the article.
Figure 12. Business objects
Use the Integration wizard to create an interface named
OpenJobReq with a two-way operation named
openJobReq that sends and receives a
JobReq argument, as shown in Figure 13.
Figure 13. Business Interfaces
The business process will coordinate the steps of the process. Additionally, the workflow can span multiple days and thus will require persistence to ensure that, if a server goes down during the life of the workflow process, no data is lost. This is done by simply making the process “long-running;” Process Server takes care of persisting the state of the workflow to a relational database so you don't have to. The server could then be rebooted and when users come back to the process, they can pick up where they left off.
- Drag a Human Task component onto the assembly diagram and
rename it
OpenJobReq. - Drag a Process component onto the assembly diagram and name it
HiringProcess. - Drag a Java component onto the assembly diagram and name it
JPAManager. - Drag the OpenJobReq interface onto the
HiringProcesscomponent. - Wire the OpenJobReq component to the HiringProcess component and choose the corresponding reference to be created.
- Wire the HiringProcess component to the JPAManager
component and choose OpenJobReq as the corresponding interface,
as shown in Figure 14.
Figure 14. Business process
- Double-click the OpenJobReq human task to implement it.
- Add an HTML-DOJO user interface, as shown in Figure 15,
Figure 15. HTML-Dojo user Interface
- In the Properties view, click New to create a new web project.
It's generally better to put custom HTML pages in a different web project than the one that gets generated by Process Server. This ensures that your custom pages don't get overwritten by things like menu actions, such as Close Project.
- Name the new web project
DevWorksUIand unselect Add project to an EAR. - Select No to stay in the Business Integration perspective.
The integration developer IDE creates an HTML-DOJO page based on the business interface and business objects that the human task uses. This is the foundation of the user interface, because the generated HTML page is designed to work with Business Space. You'll customize this HTML page as you proceed through this article.
Figure 16. HTML page for capturing human task information
- Double-click the
HiringProcesscomponent to implement the BPEL process. - Choose OpenJobReq as the interface to start the process.
- Drag the OpenJobReqPartner onto the BPEL flow between the
Receive and Reply task, as shown in Figure 17.
Figure 17. BPEL flow
- Select Invoke, then click the Details tab of the Properties view.
- Choose jobReq as the variable for input and output, as shown
in Figure 18.
Figure 18. JPAManager invoke attributes
- Double-click the
JPAManagercomponent and enter a new package name ofcom.ibm.dwexample.sca. - Replace the generated code with the code from Listing 2.
Listing 2. JPAManager code as SCA componentpackage com.ibm.dwexample.sca; import java.sql.Timestamp; import java.sql.Date; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.Persistence; import javax.xml.bind.DatatypeConverter; import commonj.sdo.DataObject; import com.ibm.dwexample.entities.Jobreq; import com.ibm.websphere.bo.BOXMLSerializer; import com.ibm.websphere.sca.ServiceManager; public class JPAManagerImpl { EntityManagerFactory emf = Persistence.createEntityManagerFactory("DevWorksJPA"); public DataObject openJobReq(DataObject jobReq) { // convert SDO to XML and output to system.out BOXMLSerializer bos = (BOXMLSerializer) new ServiceManager(). locateService("com/ibm/websphere/bo/BOXMLSerializer"); EntityManager em = emf.createEntityManager(); try { bos.writeDataObject(jobReq, jobReq.getType().getURI(), jobReq.getType().getName(), System.out); // map SDO to Java object Jobreq req = new Jobreq(); req.setCity(jobReq.getString("city")); req.setDateposted(new Timestamp(DatatypeConverter.parseDateTime( jobReq.getString("datePosted")).getTimeInMillis())); req.setApplybydate(Date.valueOf(jobReq.getString("applyByDate"))); req.setDivision(jobReq.getString("division")); req.setHiringmgr(jobReq.getString("hiringMgr")); req.setHiringemail(jobReq.getString("hiringEmail")); req.setJobdesc(jobReq.getString("jobDesc")); req.setJoblevel(jobReq.getString("jobLevel")); req.setJobtype(jobReq.getString("jobType")); req.setState(jobReq.getString("state")); req.setCountry(jobReq.getString("country")); req.setZipcode(jobReq.getString("zipCode")); req.setJobtitle(jobReq.getString("jobTitle")); System.out.println("em = " + em); em.persist(req); System.out.println("req: " + req.getReqid()); jobReq.setInt("id", req.getReqid()); } catch (Exception e) { e.printStackTrace(System.out); } finally { em.close(); } return jobReq; } }
- Left-click the error icon next to the
Jobreqline of code and choose Fix project setup, then addDevWorksJPAto the build path ofDevWorks.
You could use the JDBC adapter to retrieve and persist data to a relational database, but JPA works well and is familiar to many developers. The code in Listing 2 is essentially converting the Service Data Object (SDO) to a Java object in order to invoke the JPA entity manager for persistence.
Note that since the SCA component is not an EJB, you can't inject the
PersistenceContext into the SCA component and
thus need to use the EntityManagerFactory. This
was done to simplify the article; a better design would be to put the JPA
aspects behind an EJB session bean with a container-managed entity manager
so that entity management would be done for you. One other interesting
thing about the code in Listing 2 is the conversion of an XSD datetime
datatype into an SQL timestamp using the JAXB
DatatypeConverter class.
Add projects to the Process Server project
You could segregate the user interface tier, the business tier, and the data tier into separate EAR files and even onto separate hardware servers if your scalability requirements dictate that. However, in other cases, it's easier to join these tiers together into one EAR file for simplicity in deployment and management.
- Double-click the Dependencies file under the DevWorks integration project.
- Expand the Java projects section and ensure that DevWorksJPA is listed (this was the result of the quickfix that added DevWorksJPA to the build path).
- Check Deploy with Module.
- Expand the J2EE projects section, click Add and select
the DevWorksUI web project.
This enables you to deploy the Process Server EAR with the DevWorksUI web project and the DevWorksJPA project as part of the same enterprise application.
Business Space provides a variety of templates to start with as well as the ability to create and customize your own pages. You can even add your own custom iWidgets to Business Space. However, for the purposes of this article, you can just use the provided template as is.
- Start the embedded Process Server server from Integration Developer.
- Add the DevWorksApp project to the server. (You can expand the contents to verify that the JPA and UI projects are included.)
- Login to Business Space (for example, http://localhost:9081/BusinessSpace) as an administrator so you can customize the spaces.
- Create a new space called
DevWorksand choose Advanced Managing of Human Tasks and Workflow as the template.
Figure 19. New Space in Business Space
Test the generated HTML form on Process Server
Testing your code often as you develop the application helps you find problems quickly with less time debugging. This testing is very easy to do with the embedded test environment that comes with Integration Developer,
- Select the new space then click the Create Tasks page of the space. (Note the OpenJobReq task definition in the Task Definition List widget.)
- Click the OpenJobReq task definition to start a new task.
- Enter test values in the JobReq form then click Submit.
Figure 20. Default HTML-Dojo form in Business Space
Notice in Figure 20 that the integration developer did a pretty good job of generating default fields. For example, ApplyByDate is a Date type in the XSD, therefore the generated form uses the DateTextBox DOJO widget which provides a nice calendar drop-down. However, you'll usually want to customize this HTML form to be more aesthetically pleasing as well as to ease data entry.
- After submitting the form, check the Process Server console to see
what was received, as shown in Figure 21. Essentially, Integration
Developer generated, out of the box, an HTML form that uses Dojo
widgets to collect data for the human task. Business Space then
provides a framework to embed this HTML page into an iWidget that will
process the HTML data and create Process Server business objects that
you can then pass off to BPEL flows, mediation modules, JRules rules
engines, and so on.
Figure 21. Process Server console log showing form data received
- Or in this case, you can invoke a JPA entity manager to persist the
data to DB2, as shown in Figure 22. Note that this was done for
demonstration purposes only--because the state of the process is
managed by Process Server; you don't have to manually save state
information to the database.
Figure 22. HTML data inserted into DB2
Customizing the HTML-Dojo form
As shown in Figure 2, you usually want to customize the generated form to simplify data entry by providing drop-down lists to choose from, by pre-populating fields from system information, and by organizing the layout of the data fields to improve the user experience.
Changing the layout and the HTML is very simple using the Page Designer editor in Integration Developer.
- Switch to the Web perspective in Integration Developer, then open OpenJobReq.html with the Page Designer editor.
- Change jobReq to Job Requisition Form, then click the
icon in the Properties view and specify a thin bottom
border. - Change the other field labels in a similar fashion.
- Use an HTML table (eight rows by two columns) to rearrange the fields
in the more common web site pattern of today, as shown in Figure 23.
Be sure to select the whole
DIVelement when rearranging the fields.
Figure 23. Page Designer to Rearrange Fields
- Since the
reqIDfield will be generated from DB2, you should make it a read-only field. Likewise, thedatePostedfield will be generated from JavaScript.
To make the fields stretch across the table, since the Title and Job Description fields span columns, you can set the stylesheet rule associated with the input fields to use 100% as the width instead of 150px.
- Select one of the input fields then click the style icon next to the fieldInput class of the Properties view. This brings up the style editor for the matching style rule that was generated. Change the width to 100%.
The style editor makes it easy to customize the CSS by simply picking values from a list of valid choices instead of having to remember the syntax of the CSS rules.
Integration Developer selects Dojo widgets based on the type of each field, but in some cases that's not what you want. For example, easing data entry with drop-down lists and lookup fields improves usability.
- On the Source tab, change the Dojo type from dijit.form.ValidationTextBox to dijit.form.FilteringSelect for all fields for which you want to use a drop-down style control (such as job type, job level, country, state, division).Y
- Add a
storeattribute to each of the drop-downs. ThefilteringSelectwill read this store for selection choices. - Change the Job Description field to
dijit.form.Textareaand set a style property of min-height to 200px.
Fetch the drop-down values from DB2
By providing users a list of choices to choose from, you'll get better data from the users, as well as make their user experience better. Often, however, the data choices are dynamic and based on data in other locations, such as a relational database. Things like countries and states may be pretty static and could simply be added to the HTML page, but things like job titles, department names, product names, and so on may change more frequently. By retrieving this information dynamically, your application is more flexible and adaptable.
- Switch back to the Business Integration perspective and create a new
interface named
UIInitializeServicewith operations as shown in Figure 24 to receive Dojo requests for fetching drop-down values.
Figure 24. SCA interface for fetching DB2 data
- Switch to the Assembly Diagram and drag the UIInitializeService interface onto the canvas and select Export with no Binding.
- Rename the export to
UIInitializeService. - Link the UIInititializeService export to the JPAManager
component.
Figure 25. Assembly diagram with UIInitializeService
- Click the UIInitializeService component and select Generate Binding => HTTP Binding.
- Change the function selector to HTTP function selector based on URL and HTTP method, since you'ill use a REST style invocation pattern, then click OK.
- Right-click the JPAManager component and choose Synchronize Interfaces and References => to Implementation. This stubs in new methods in the JPAManager component.
- Replace the code for the
getDivisionsmethod of theJPAManagerclass with the code in Listing 3.
Listing 3. getDivisions method of JPAManager\ public DataObject getDivisions() { BOFactory bofactory = (BOFactory) new ServiceManager(). locateService("com/ibm/websphere/bo/BOFactory"); EntityManager em = emf.createEntityManager(); DataObject pickList=bofactory.create("http://com.ibm.dwexample.bo","Picklist"); pickList.setString("identifier","value"); int i=0; for (Picklistitem p : (List<Picklistitem>)em.createQuery( "select p from Picklistitem p where p.type='Division'").getResultList()){ ++i; pickList.setString("items["+i+"]/name", p.getName()); } BOXMLSerializer bos = (BOXMLSerializer) new ServiceManager(). locateService("com/ibm/websphere/bo/BOXMLSerializer"); try { bos.writeDataObject(pickList, pickList.getType().getURI(), pickList.getType().getName(), System.out); }catch(Exception e) { e.printStackTrace(System.out); } finally { em.close(); } return pickList; }
The code in Listing 3 uses the JPA entity manager in order to query the database for a list of company divisions using JPQL. From the list of JPA entities returned, the code creates an SDO with this data and returns it to the requestor. The format of the SDO is designed to match the expected format of the
dojo.data.ItemFileStore. For this article, we simply use the drop-down choice as the value, but you can easily choose a different value to be stored than what is displayed (for example, for internationalization or project codes, and so on). - Do the same thing for the
getJobLevelsmethod and thegetJobTypesmethod ensuring that you change the JPQL to get the correct picklist type. - Go back to the Assembly Diagram and select the UIInitializeService export, then select the Binding > Method Bindings tab in the Properties view.
- For each bound method, change the data serialization of the output
format to be JSON.
Figure 26. JSON data serialization selections
To populate the HTML form with information about the user, you can use the JEE principal as the key to a user repository.
- Replace the
getUserDetailsmethod of theJPAManagerclass with the code in Listing 4.
Listing 4. getUserDetails method of JPAManagerpublic DataObject getUserDetails() { BOFactory bofactory = (BOFactory) new ServiceManager(). locateService("com/ibm/websphere/bo/BOFactory"); DataObject userStore = bofactory.create("http://com.ibm.dwexample.bo", "UserDataStore"); EntityManager em = emf.createEntityManager(); try { Userlist currentUser=em.find(Userlist.class,WSSubject.getCallerPrincipal()); userStore.setString("label", "fullname"); userStore.setString("identifier", "email"); userStore.setString("items[1]/fullname", currentUser.getFullname()); userStore.setString("items[1]/email", currentUser.getEmail()); }catch(Exception e) { e.printStackTrace(System.out); } finally { em.close(); } return userStore; }
The code in Listing 4 uses the JPA entity manager to look for a user with a
primary key that matches the JEE caller principal. Once this user is
retrieved, the full name and email are added to an SDO, which gets
converted into JSON in order to populate the appropriate HTML fields.
However, to ensure that the JEE caller principal is populated with the
authenticated userID, you need to secure the URL by which this method gets
invoked to force authentication. Figure 28 shows where this security
constraint is added, but you now need to segregate the
getUserDetails method from the other
non-security-constrained methods. You can do this by specifying a
different context path (for example, secure/getUserDetails), as shown in
Figure 27.
Figure 27. Secure context path
- Change Context path to
/secure/getUserDetails. - Add a new Native method with a value of
/UIInitializeService/secure/getUserDetails@GETand remove the other. - Save everything and publish to the Process Server test environment.
- Open your browser to http://localhost:9081/DevWorksWeb/UIInitializeService/getDivisions to test that the service is working and returning JSON.
Many of the Dojo widgets are designed to work more easily with JSON data than other data formats. Therefore, you tell Process Server to convert the SDO (XML) data into JSON format by specifying the data handler to use, and Process Server does this for you without your having to write any JSON conversion code.
Bind the data store drop-down lists
Dojo includes objects for retrieving data from a variety of sources. The
dojo.data.ItemFileReadStore is particularly
good at retrieving JSON data from a URL. The name you give to this store
can then be set in the
dijit.form.FilteringSelect Dojo widget as a
store attribute in order to specify the choice
list for this drop-down widget.
- Edit
OpenJobReq.htmland add the HTML from Listing 5 just above the HTML table.
Listing 5. Dojo ItemFileReadStore to fetch drop-down data<div dojoType="dojo.data.ItemFileReadStore" jsId="divisions" url="/DevWorksWeb/UIInitializeService/getDivisions"></div> <div dojoType="dojo.data.ItemFileReadStore" jsId="jobtypes" url="/DevWorksWeb/UIInitializeService/getJobTypes"></div> <div dojoType="dojo.data.ItemFileReadStore" jsId="joblevels" url="/DevWorksWeb/UIInitializeService/getJobLevels"></div>
The HTML code in Listing 5 tells Dojo how to retrieve dynamic drop-down data from Process Server by specifying the URL to the HTTP Export Binding, shown in Figure 25. Once this URL is invoked, Process Server will call the
JPAManagercomponent to fetch the data from DB2 using JPA. This data will then be converted to JSON and returned to the Dojo store.
While some data is best stored in a relational database, other data is simply stored as text files. For example, the list of US states is a relatively static list that could be a simple comma-separated-value list as shown in Listing 6. Retrieving this information from a static text file also reduces the load on the database.
- Create a text file named
usstates.txtin the DevWorksUI web project under the WebContent folder. This file will contain US States in a comma separated format that looks like Listing 6. Notice the first line heading.
Listing 6. US states in CSV formatState,Abbreviation Alabama,AL Alaska,AK Arizona,AZ Arkansas,AR California,CA ...
- Create a text file named
worldcountries.txtin the DevWorksUI web project under the WebContent folder to contain country names, as shown in Listing 7.
Listing 6. World Countries in CSV FormatName, FIPS Afghanistan, AF Albania, AL Algeria, AG United Kingdom, UK United States, US Zimbabwe, ZI ...
- Edit
OpenJobReq.htmland add the HTML from Listing 8 below the HTML from Listing 5.
Listing 8. Dojo CSV store to fetch drop-down data<div dojoType="dojox.data.CsvStore" jsId="states" identifier="State" url="/DevWorksUI/usstates.txt"></div> <div dojoType="dojox.data.CsvStore" jsId="countries" identifier="Name" url="/DevWorksUI/worldcountries.txt"></div>
- Since the data format of the US states and world countries is in CSV
format, you need a Dojo data store that can parse that format.
dojox.data.CsvStoreis such a store and works similarly to thedojo.data.ItemFileReadStore. Also note the identifier attribute to specify which column should be used as the value for the drop-down list. - Jump down to the
Countrydrop-down field and add a new attribute to the input field namedsearchAttrwith a value ofName, as shown in Listing 9. - Do the same thing for the
Statesdrop-down field, but use a value ofStateinstead.
Listing 9. Dojo drop-down using CSV<input type="text" name="/jobReq/country" value="" sdoposition="5" dojotype= "dijit.form.FilteringSelect" store="countries" searchAttr="Name" regexp=".*" id= "http___com.ibm.dwexample.interfaceopenJobReqRequestMsg_input5_HTMUniqueWidgetID" sdomessagetype="input" sdoprepopulation="" required="false" class="fieldInput" />
Pre-populate fields with JavaScript
Things are looking a lot better now, but oftentimes you need to initialize fields with things like the current date or the currently logged in user. To do this, you'll add a custom Dojo widget that will use JavaScript to initialize fields.
The way that the Task Information widget pulls in the generated HTML-Dojo page and parses it in order to insert it into the framework for processing can cause some issues with trying to just add your JavaScript code to the HTML page. By creating a custom Dojo widget, you can ensure that your initialization code gets invoked after the Task Information widget has modified the generated HTML page.
- Copy the contents of Listing 10 into the OpenJobReq.html file just
below the code from Listing 9.
Listing 10. Invoking a custom DOJO widget to initialize fields<script type="text/javascript"> dojo.require("dojo.parser"); dojo.registerModulePath("com.ibm.dwexample","com/ibm/dwexample"); dojo.require("com.ibm.dwexample.MyPageInit"); </script> <div dojoType="com.ibm.dwexample.MyPageInit"></div>
- Copy the contents of Listing 11 into a text file named MyPageInit.js
under WebContent/com/ibm/dwexample.
Listing 11. MyPageInit.js custom Dojo widgetdojo.provide("com.ibm.dwexample.MyPageInit"); dojo.require("dojo.date.locale"); dojo.require("dojo.data.ItemFileReadStore"); // Declare new widget to initialize fields dojo.declare("com.ibm.dwexample.MyPageInit",dijit._Widget,{ startup: function(){ console.log("Initializing DOJO page."); dojo.query("[name$='/jobReq/datePosted']")[0].value = dojo.date.locale.format( new Date(),{datePattern: "yyyy-MM-dd",timePattern: "HH:mm:ss"}); dojo.query("[name$='/jobReq/jobType']")[0].previousSibling.value = "--Select One--"; dojo.query("[name$='/jobReq/jobLevel']")[0].previousSibling.value = "--Select One--"; dojo.query("[name$='/jobReq/country']")[0].previousSibling.value = "--Select One--"; dojo.query("[name$='/jobReq/state']")[0].previousSibling.value = "--Select One--"; dojo.query("[name$='/jobReq/division']")[0].previousSibling.value = "--Select One--"; var userData = new dojo.data.ItemFileReadStore({url: "/DevWorksWeb/UIInitializeService/secure/getUserDetails"}); userData.fetch({ onItem: function(item) { dojo.query("[name$='/jobReq/hiringMgr']")[0].value = userData.getValue(item, 'fullname'); dojo.query("[name$='/jobReq/hiringEmail']")[0].value = userData.getValue(item, 'email'); } }); } });
The code in Listing 11 is one way to hook into the initialization process
that the Business Space widget uses in parsing the human task HTML page.
In this code, you use the dojo.query function
to locate the input fields to be initialized. Since the Business Space
widget will re-ID the input fields to ensure uniqueness, you need to use a
different method of identifying the input fields. Also recall that there
is a corresponding output field with the same name. Therefore, using CSS
selectors, you'll query any element that has a name attribute that ends
with /jobReq/datePosted, for example. Using
dojo.query actually returns an array, which is
why you use the first item of the array, which should be the input
field.
Each dijit.form.FilteringSelect gets translated
into two input fields, thus you need to refer to the sibling of the input
field that the dojo.query finds.
Recall from Listing 5 that the
dijit.form.ItemFileReadStore can be used for
retrieving drop-down values declaratively; you can also use this object
programmatically to fetch data as is done for the user name and email.
However, the code in Listing 11 that retrieves the user name and email
uses the WSSubject.getCallerPrincipal() as the
key by which to retrieve the full name and email of the user requesting
the HTML page. In this article, the user information is in the DB2
database, but you could easily adapt the code to query an LDAP system.
Tip: If you have Java 2 Security enabled, you'll need to have a was.policy file in the META-INF directory of the EAR file with contents to the effect of:
grant codeBase "file:${application}" {
permission java.security.SecurityPermission "printIdentity";
permission java.lang.RuntimePermission "getClassLoader";
};
|
To ensure that the HTML page request requires authentication, you need to add a security constraint to the HTTP Export binding path. This can be done by opening the Deployment editor from the business integration project, as shown in Figure 28.
Figure 28. Deployment editor
Listing 12 shows the results of creating the appropriate security roles and
constraints to force an authentication check by Process Server when the
userDetails URL is requested.
Listing 12. ibm-deploy.scaj2ee security constraints
<?xml version="1.0" encoding="UTF-8"?>
<scaj2ee:IntegrationModuleDeploymentConfiguration
xmlns:scaj2ee="http://www.ibm.com/xmlns/prod/websphere/sca/j2ee/6.0.2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:com.ibm.ejs.models.base.bindings.applicationbnd=
"applicationbnd.xmi">
<appProject>
<authorizationTable>
<authorizations role="//@appProject/@securityrole.0">
<specialSubjects name="AllAuthenticatedUsers" xsi:type=
"com.ibm.ejs.models.base.bindings.applicationbnd:AllAuthenticatedUsers"/>
</authorizations>
</authorizationTable>
<securityrole roleName="Authenticated"></securityrole>
</appProject>
<ejbProject></ejbProject>
<wsImports></wsImports>
<wsExports></wsExports>
<webProject>
<securityconstraint displayName="HTTPBindings">
<webResourceCollections webResourceName="HTTPBinding Methods">
<httpMethod>GET</httpMethod>
<urlPattern>/UIInitializeService/secure/*</urlPattern>
</webResourceCollections>
<authConstraint>
<roles>Authenticated</roles>
</authConstraint>
</securityconstraint>
<securityrole roleName="Authenticated"></securityrole>
</webProject>
</scaj2ee:IntegrationModuleDeploymentConfiguration>
|
Now when you deploy the EAR file and open the OpenJobReq.html page from within the Task Information widget of Business Space, you'll have a nicely organized HTML form with pre-populated fields and dynamically chosen drop-down lists to pick from, as shown in Figure 29.
Figure 29. Initialized HTML-Dojo page
Additionally, when you click Submit to send the data to Process Server, the JPAManager component will use JPA to persist the data to DB2, as shown in Figure 30.
Figure 30. JobReq data in DB2
We set out to demonstrate how rapidly you could build an application using WebSphere Process Server with a web 2.0 user interface embedded in the included Business Space framework. In this process, we leveraged the natively supported JPA provider in order to retrieve and persist data to DB2. Along the way, we showed you how to create custom Dojo widgets that could retrieve JSON data and CSV data from a URL by which the Dojo widgets could be modified to use this data. Finally, we secured one of the HTTP export binding URLs to demonstrate how to use the JEE security credential to retrieve information about the user accessing the web site. The end result is the foundation of an application architecture that can be used to develop a myriad of user-centric applications that requires less programming and code to maintain, while still providing a flexible, responsive, and nice-looking user interface. This application architecture has the full power of enterprise-class software and will scale to the largest requirements when and if the need arises.
The author would like to thank Anandnatraj Chandramohan for his valuable technical review and verification of this article.
| Description | Name | Size | Download method |
|---|---|---|---|
| Sample code | wps_jpa_pi.zip | 46KB | HTTP |
Information about download methods
-
Using Business Space to manage business process applications,
(Rajiv Madassery, developerWorks 2010)
-
Building IBM Business Process Management Solutions Using WebSphere V7
and Business Space (IBM Redbooks, 2010)
-
Experience Java EE! Using Rational Application Developer V7.5
(IBM Redbooks, 2010)
-
Integrating a Dojo client with an SCA application via SCA HTTP
binding (Kharlamov, Senaratne, Pacholski, developerWorks
2009)
-
Make SOA transactional (Ramachandran, DeveloperWorks
2008)
-
Customizing HTML-Dojo forms for Business Space powered by
WebSphere (Schaefers & Kreise, developerWorks WebSphere
Developer Technical Journal, 2009).
-
Use transaction qualifiers in SCA mediation modules (Schofield,
developerWorks 2009)
-
Transactionally integrate Web services with BPEL process in WebSphere
Process Server (Xu, developerWorks 2007)
-
developerWorks BPM zone: Get the latest technical resources on
IBM BPM solutions, including downloads, demos, articles, tutorials,
events, webcasts, and more.





