Using the Java Persistence API to initialize HTML-Dojo forms for WebSphere Business Space

Using the WebSphere® Process Server Business Space, you can quickly create a web 2.0 user interface to a business process, and WebSphere Integration Developer tooling actually generates a user interface for you from the definition of your business process. However, you may need to enhance this generated user interface to create things like drop-down lists from data in a database and to pre-populate fields where possible. In this article you'll learn how to use the embedded Java™ Persistence API (JPA) of WebSphere Process Server V7 to gather data from a DB2® database in order to initialize HTML-Dojo fields. This content is part of the IBM Business Process Management Journal.

Share:

Bill Griffith (wgriffith@us.ibm.com), Senior IT Architect, IBM

Bill Griffith photoBill Griffith is a Senior IT Architect in the Worldwide WebSphere Business Partner organization. In this role, Bill works with IBM business partners to architect and design business partner solutions that leverage IBM middleware as a way to decrease time-to-market and decrease costs.



08 September 2010

Introduction

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
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
Desired DOJO-HTML form

Architecture overview

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
Architecture deployment diagram

Prerequisites

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

  1. Create a DB2 database called DEVWORKS to hold the initialization data.
    Figure 4. Create DB2 database
    Create DB2 database
  2. 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.

  3. 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
    Create a new database connection
  4. 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
    New SQL Script
  5. 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 SQL
    drop 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!

Create JPA entities

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!

  1. Create a new JPA project in Integration Developer.
  2. Specify DevWorksJPA as 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.
  3. Ensure that Generic is selected as the platform then select DEVWORKS as the connection.
  4. Check Override default schema from connection and select DB2ADMIN as the schema.
  5. Leave Server runtime for JPA implementation as is, but change persistent class management to Annotated classes must be listed in\ persistence.xml.
  6. Click Yes when prompted to change to the JPA perspective.
  7. Right-click the DevWorksJPA project and select JPA Tools => Generate Entities.
  8. Ensure that the DEVWORKS connection and DB2ADMIN schema are selected, then click Next.
  9. Specify com.ibm.dwexample.entities as the package name and check Synchronize Classes in persistence.xml, as shown in Figure 7.
  10. Select all of the tables and click Finish.
    Figure 7. Generate JPA entities from DB2 tables
    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.

  1. Select the DevWorksJPA project and choose JPA Tools => Configure Project for JDBC Deployment.
  2. Select the DEVWORKS connection, then check Schema and select DB2ADMIN.
  3. Uncheck Deploy JDBC Connection information to server and click OK, as shown in Figure 8.
    Figure 8. JPA deployment options
    JPA deployment options
  4. 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
  5. 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
    JPA data source type
  6. Open the Jobreq.java file and select the reqid field.
  7. 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
    JPA entity primary key strategy

    You may have noticed that the DB2 SQL (see Listing 1) has the primary key for the Jobreq table and the PickListItem table 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.

  8. Now repeat this step for the Picklistitem.java entity.
  9. Leave the Userlist.java entity 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.

Create business objects

  1. 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.
  2. 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.
  3. Override the default namespace and specify http://com.ibm.dwexample.bo.
  4. 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
Business objects

Create business interfaces

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

Create the business process

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.

  1. Drag a Human Task component onto the assembly diagram and rename it OpenJobReq.
  2. Drag a Process component onto the assembly diagram and name it HiringProcess.
  3. Drag a Java component onto the assembly diagram and name it JPAManager.
  4. Drag the OpenJobReq interface onto the HiringProcess component.
  5. Wire the OpenJobReq component to the HiringProcess component and choose the corresponding reference to be created.
  6. Wire the HiringProcess component to the JPAManager component and choose OpenJobReq as the corresponding interface, as shown in Figure 14.
    Figure 14. Business process
    Business process
  7. Double-click the OpenJobReq human task to implement it.
  8. Add an HTML-DOJO user interface, as shown in Figure 15,
    Figure 15. HTML-Dojo user Interface
    HTML-Dojo user Interface
  9. 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.

  10. Name the new web project DevWorksUI and unselect Add project to an EAR.
  11. 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
    HTML page for capturing human task information
  12. Double-click the HiringProcess component to implement the BPEL process.
  13. Choose OpenJobReq as the interface to start the process.
  14. Drag the OpenJobReqPartner onto the BPEL flow between the Receive and Reply task, as shown in Figure 17.
    Figure 17. BPEL flow
    BPEL flow
  15. Select Invoke, then click the Details tab of the Properties view.
  16. Choose jobReq as the variable for input and output, as shown in Figure 18.
    Figure 18. JPAManager invoke attributes
    JPAManager invoke attributes
  17. Double-click the JPAManager component and enter a new package name of com.ibm.dwexample.sca.
  18. Replace the generated code with the code from Listing 2.
    Listing 2. JPAManager code as SCA component
    package 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;
    
    publicclass 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;
     }
    }
  19. Left-click the error icon next to the Jobreq line of code and choose Fix project setup, then add DevWorksJPA to the build path of DevWorks.

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.

  1. Double-click the Dependencies file under the DevWorks integration project.
  2. 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).
  3. Check Deploy with Module.
  4. 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.

Set up a business space

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.

  1. Start the embedded Process Server server from Integration Developer.
  2. Add the DevWorksApp project to the server. (You can expand the contents to verify that the JPA and UI projects are included.)
  3. Login to Business Space (for example, http://localhost:9081/BusinessSpace) as an administrator so you can customize the spaces.
  4. Create a new space called DevWorks and 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,

  1. Select the new space then click the Create Tasks page of the space. (Note the OpenJobReq task definition in the Task Definition List widget.)
  2. Click the OpenJobReq task definition to start a new task.
  3. Enter test values in the JobReq form then click Submit.
    Figure 20. Default HTML-Dojo form in Business Space
    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.

  4. 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
    Process Server console log showing form data received
  5. 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
    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.

Customize the HTML

Changing the layout and the HTML is very simple using the Page Designer editor in Integration Developer.

  1. Switch to the Web perspective in Integration Developer, then open OpenJobReq.html with the Page Designer editor.
  2. Change jobReq to Job Requisition Form, then click the pencil icon in the Properties view and specify a thin bottom border.
  3. Change the other field labels in a similar fashion.
  4. 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 DIV element when rearranging the fields.
    Figure 23. Page Designer to Rearrange Fields
  5. Since the reqID field will be generated from DB2, you should make it a read-only field. Likewise, the datePosted field 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.

  1. 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.

Change the Dojo widgets

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.

  1. 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
  2. Add a store attribute to each of the drop-downs. The filteringSelect will read this store for selection choices.
  3. Change the Job Description field to dijit.form.Textarea and set a style property of min-height to 200px.

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.

  1. Switch back to the Business Integration perspective and create a new interface named UIInitializeService with operations as shown in Figure 24 to receive Dojo requests for fetching drop-down values.
    Figure 24. SCA interface for fetching DB2 data
    SCA interface for fetching DB2 data
  2. Switch to the Assembly Diagram and drag the UIInitializeService interface onto the canvas and select Export with no Binding.
  3. Rename the export to UIInitializeService.
  4. Link the UIInititializeService export to the JPAManager component.
    Figure 25. Assembly diagram with UIInitializeService
  5. Click the UIInitializeService component and select Generate Binding => HTTP Binding.
  6. 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.
  7. Right-click the JPAManager component and choose Synchronize Interfaces and References => to Implementation. This stubs in new methods in the JPAManager component.
  8. Replace the code for the getDivisions method of the JPAManager class 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).

  9. Do the same thing for the getJobLevels method and the getJobTypes method ensuring that you change the JPQL to get the correct picklist type.
  10. Go back to the Assembly Diagram and select the UIInitializeService export, then select the Binding > Method Bindings tab in the Properties view.
  11. For each bound method, change the data serialization of the output format to be JSON.
    Figure 26. JSON data serialization selections
    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.

  1. Replace the getUserDetails method of the JPAManager class with the code in Listing 4.
    Listing 4. getUserDetails method of JPAManager
    public 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
Secure context path
  1. Change Context path to /secure/getUserDetails.
  2. Add a new Native method with a value of /UIInitializeService/secure/getUserDetails@GET and remove the other.
  3. Save everything and publish to the Process Server test environment.
  4. 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.

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.

  1. Edit OpenJobReq.html and 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 JPAManager component 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.

  1. Create a text file named usstates.txt in 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 format
    State,Abbreviation
    Alabama,AL
    Alaska,AK
    Arizona,AZ
    Arkansas,AR
    California,CA
    ...
  2. Create a text file named worldcountries.txt in the DevWorksUI web project under the WebContent folder to contain country names, as shown in Listing 7.
    Listing 6. World Countries in CSV Format
    Name, FIPS
    Afghanistan, AF
    Albania, AL
    Algeria, AG
    United Kingdom, UK
    United States, US
    Zimbabwe, ZI
    ...
  3. Edit OpenJobReq.html and 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>
  4. 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.CsvStore is such a store and works similarly to the dojo.data.ItemFileReadStore. Also note the identifier attribute to specify which column should be used as the value for the drop-down list.
  5. Jump down to the Country drop-down field and add a new attribute to the input field named searchAttr with a value of Name, as shown in Listing 9.
  6. Do the same thing for the States drop-down field, but use a value of State instead.
    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.

  1. 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>
  2. Copy the contents of Listing 11 into a text file named MyPageInit.js under WebContent/com/ibm/dwexample.
    Listing 11. MyPageInit.js custom Dojo widget
    dojo.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
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
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
JobReq data in DB2

Conclusion

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.


Acknowledgement

The author would like to thank Anandnatraj Chandramohan for his valuable technical review and verification of this article.


Download

DescriptionNameSize
Sample codewps_jpa_pi.zip46KB

Resources

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

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

 


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

All information submitted is secure.

Choose your display name



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

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

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

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

 


All information submitted is secure.

Dig deeper into WebSphere on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=WebSphere
ArticleID=516802
ArticleTitle=Using the Java Persistence API to initialize HTML-Dojo forms for WebSphere Business Space
publish-date=09082010