Ajax CRUD with Struts2 and Tibco GI

In this article you will learn how to create a new Ajax RIA front end to an existing Apache Struts2 .jsp application using TIBCO General Interface (GI), an open source Ajax toolkit with a MVC architecture similar to that of Java Swing. GI is optimized for creating business productivity applications and communicating with XML, SOAP, JSON and other types of services in a SOA.

Summary

In this article you will learn how to create a new Ajax RIA front end to an existing Apache Struts2 .jsp application using TIBCO General Interface (GI), an open source Ajax toolkit with a MVC architecture similar to that of Java Swing. GI is optimized for creating business productivity applications and communicating with XML, SOAP, JSON and other types of services in a SOA.

Specifically we’ll extend the Struts2 CRUD (create, retrieve, update, delete) sample that comes with its installation to expose XML data services optimized for GI interoperation and create the Ajax application that connects the end user with those services though a rich graphical user interface. The intent behind this approach is to demonstrate how you can make incremental changes to existing applications rather than having to rewrite from scratch.

Existing multi-page, HTML GUI

To-Be Ajax GUI with rich editable data grids from GI Ajax Toolkit

 

 

 

 

 

 

 

 


Figure 1: Existing HTML GUI and the To-Be Ajax GUI

What follows is a step by step approach to developing the General Interface application, Struts2 components and configuring Struts to support the resulting Ajax view.

You will see how the JSP view is bypassed to expose an XML service and the relative ease with which you can use GI to create an Ajax RIA that communicates with Struts to provide a feature rich, high productivity, graphical user interface.

Getting Started

We won’t go into incredible hand holding detail on installing prerequisite products here. In order to try this live, you will need to be familiar with Java, Tomcat etc. If not, no worries, what follows is self contained

If you’re not already using Struts2, you will first need to download and configure Struts2 (see Resources section immediately below). Ensure that you can bring up the “Struts Showcase” sample applications. The CRUD case is one of these.

You will also need the latest GI distribution. If you are not familiar with GI, download it under the open source BSD license at the TIBCO Developer Network and run through the video tutorials provided.

Resources

Struts 2

If you have worked with classic Struts, you will notice many changes. In fact, Struts2 looks like the “after” images from reality makeover shows—If Struts has been your significant other, you’ll find it looks a lot better after the makeover. The old work horse now has all the bells and whistles, so many that the latest features may be overwhelming at first. The two new features key to our use case are the Value Stack and Interceptors. The Value Stack comes from Strut2’s integration of XWork and OGNL. All values from the client are contained in the value stack and are accessible to the action and results via the expression language (EL). Our particular strategy to integrate GI and Struts will implement a custom interceptor to read values from the CDF document and populate the value stack. We will leverage these features to re-implement the struts showcase CRUD example using GI.

CRUD

The CRUD pattern (Create, Retrieve, Update and Delete) lies at the heart of many business applications. No matter how you slice it, your app will need to create new entities, validate, store and manage them. The smart folks behind Struts2 knew this and provided a CRUD application within the Struts Showcase examples. While their motivation was to illustrate proper layering for data access and dependency injection, we will extend this sample to show how an Ajax RIA and XML service interfaces can be added into the existing solution.

Take a second to familiarize yourself by using the sample CRUD application that came with your Sturs2 installation. The default location on your local system is http://localhost:8080/struts2-showcase-2.0.1/empmanager/index.jsp and learn more about this app at http://struts.apache.org/2.x/docs/crud-demo-i.html.

Now look at the configuration file “struts.xml” at line 68 and 90. You are looking for the two packages named “skills” and “employees”.

Here we can see the urls, controller classes and views are related to each other.

Let’s translate the following excerpt:

        <action name="list" class="org.apache.struts2.showcase.action.SkillAction" method="list">
            <result>/empmanager/listSkills.jsp</result>
            <interceptor-ref name="basicStack"/>
        </action>

The controller for the list action is the list method in the SkillAction class. Before the controller method is executed, the client’s request is passed into an interceptor chain defined by “basicStack” Any result will be sent to the listSkills.jsp for rendering.

Now, the meat of this application lies in the SkillAction class’s list method. Lots of good work happens in here: data access, mapping into POJOs, perhaps some business rules. The controller acts as a conductor, orchestrating the work or Model and Service classes that ideally should do the heavy lifting. This is the area of the code we want to leave untouched. We will use these controller classes in our Ajax application by working with interceptors and results.

Value Stack and Interceptors

Interceptors are key to understanding Struts2. Simply stated each request coming from the client triggers a defined set of interceptors that each independently interrogates the request either before or after the controller Action class executes. Struts2 comes with many pre-built interceptors including file upload, chaining, xslt, logging, exception handling etc. One of the interceptors, ParametersInterceptor, sets all parameters on the value stack.

The Value Stack is at the heart of Struts2. By leveraging the ognl value stack, the framework provides sophisticated, automatic type conversion. The Action Forms paradigm and laborious transfer of data from strings to domain objects so common to Struts1 application is effectively deprecated.

We will take advantage of the Interceptor and Value Stack later on when we introduce our own GI friendly interceptor.

It is worth pointing out that GI can handle data streams in a number of message formats including XML, SOAP, JSON, etc. GI includes powerful visual design time tools and a runtime environment for mapping messages to instances of its data and GUI objects.

CDF – the Common Data Format of GI’s GUI controls

By GI friendly, we mean that we will deliver the data to the Ajax client in the xml format used by the Ajax framework.

The Common Data Format (CDF) format is a schema that defines the client side data used by GI’s components. All data aware GI elements use the CDF format. Briefly XML adhering to the CDF schema is in the form:

<data jsxid="X" >
 <record jsxid="A" .... />
 <record jsxid="B" .... />
 <record jsxid="C" .... />
            ...
</data>

In addition <record> nodes can be nested to represent hierarchical relationships. Therefore a <record> can represent a table row, a node in a tree, a menu item, and so on.

In our work here, we’ll create an XML service in Struts2 that returns CDF. In cases where a service returns something other than CDF, GI’s XML mapping utilities provide visual tools for client-side transformations to and from non-CDF formats, to and from CDF. In this way you can also use GI with existing XML, SOAP or other HTTP services.

Overview of the extended server side processes

Figure 2 shows the “CDF aware” Interceptor and Result types we’ll add to the system. The addition of these two elements enables us to bypass the view Template and instead expose an XML service that conforms to the CDF schema used by GI.

Figure 2: Struts2 application modifications to expose XML service conforming to GI’s CDF schema.

Create a CDF Interceptor type

We create a CDFInterceptor, a ServletRequestAware interceptor that captures the POX (plain old xml) message in CDF format and inserts the values into the ActionContext. This interceptor is responsible for mapping the attribute names on the CDF record to the parameter names used by the action.

<!--definition of the interceptor -->
<interceptor name="fromCDF"     class="org.apache.struts2.showcase.gi.CDFInterceptor"/>
<!--use of the interceptor in a stack -->
<interceptor-stack name="crudStackGI">
 <interceptor-ref name="fromCDF" />
 <interceptor-ref name="params" />
 <interceptor-ref name="static-params" />
 <interceptor-ref name="defaultStack" />
</interceptor-stack>

<!--reference of the stack by an action, specifying parameter request name mappings -->
<action name="delete" class="org.apache.struts2.showcase.action.EmployeeAction" method="delete">


<interceptor-ref name="cdfStackDefault">
 <param name="fromCDF.alternateNames">#{ "empId" : "toDelete" }</param>
 </interceptor-ref>
</action>
Create a CDF Result type

We create a CDFResult, a POJO that implements the Result interface. It replaces the JSP and renders element(s) from the invocation stack as CDF compliant XML. It also sets HTTP status codes and renders validation errors.

<result-types>
 <result-type name="cdf" class="org.apache.struts2.showcase.gi.CDFResult" />
</result-types>
           ...
      <action name="save" class="org.apache.struts2.showcase.action.SkillAction" method="save">
            <result name="success" type="cdf" >
             <param name="inputName">currentSkill
            </result>
            <interceptor-ref name="crudStackGI"/>
        </action>
Final Struts2 Configuration

We’ve included the GI version of the skill package below. See the struts.xml for the complete version. You can see how the original actions are wrapped in CDF specific interceptors and result.

<package name="skillGI" extends="default" namespace="/skillGI">
 <action name="list" class="org.apache.struts2.showcase.action.SkillAction" method="list">
     <result type="cdf" >
  <param name="inputName">availableItems
  <param name="propertyNameJSXId">id
  <param name="propertyNameJSXText">description
     </result>
     <interceptor-ref name="cdfStackDefault"/>
 </action>
 <action name="save" class="org.apache.struts2.showcase.action.SkillAction" method="save">
     <result name="input" type="cdf" >
  <param name="inputName">currentSkill
  <param name="propertyNameJSXId">id
  <param name="propertyNameJSXText">description
     </result>
     <result name="success" type="cdf" >
  <param name="inputName">currentSkill
  <param name="propertyNameJSXId">id
  <param name="propertyNameJSXText">description
     </result>
     <interceptor-ref name="crudStackGI"/>
 </action>
 <action name="delete" class="org.apache.struts2.showcase.action.SkillAction" method="delete">
     <result name="success" type="cdf" >
  <param name="inputName">currentSkill
  <param name="propertyNameJSXId">id
  <param name="propertyNameJSXText">description
     </result>
     <result name="input" type="cdf" >
  <param name="inputName">currentSkill
  <param name="propertyNameJSXId">id
  <param name="propertyNameJSXText">description
     </result>
     <interceptor-ref name="cdfStackDefault"/>
 </action>
</package>

General Interface

Ajax requirements

Well we are getting a little ahead of ourselves here. Let’s take a step back and look at the use case requirements and elements in the client-side environment that will provide the Ajax GUI.

Figure 3: CLIENT-SIDE SYSTEM DIAGRAM showing matrix, cache, mappings, communications and messages to/from CDF service in struts environment.

Ajax Editable data grid

Our design calls for an editable data grid UI very similar to the MS Access where the last row in the grid will operate as an input row. The user can also click, arrow or tab their way from cell to cell. When the focus leaves a cell, validation will be run. If validation is successful, the row will be marked as dirty. When focus leaves a row, a dirty row will be saved to the server. A failure in local validation or a server error message will result in the focus returning to the cell in error.

The GI Ajax toolkit has a component class called Matrix that in addition to being configurable into multi-column lists, trees, and tree-tables, also supports the editable data grid use case. A look in the source code for this project will show the logic used to handle the validation, dirty row, create and update record processes.

GI XML Mapping rules

Even though we have configured the server to return CDF records we’ll still use GI’s XML Mapping utilities to perform a few tasks for us including:

  • Mapping of model names to request parameter names i.e. GI’s empId attribute for a CDF record representing an employee becomes Struts’ expected currentEmployee.id parameter.
  • Resolving URLs since the mapping files allow us to store URLs independent of code
  • Formatting data as its flows in and out of messages between the Ajax RIA client and the server.
Reuse of resources

Messages

Struts

GI

Figure 4: Struts message constants re-implemented as GI resources.

Validation

Struts

GI

Figure 5: Struts validations and their GI counterpart.

To a greater or lesser degree most business Ajax implementations will require detailed information about the server model. In order to perform local, client side validation for a given property, the framework needs to know the description of the property; it’s type, parent, label and validation messages. In the package org.apache.struts2.showcase.gi.util we provide two “rough and ready” utilities to generate the GI xml components for matrixes, columns, validation and messages from the struts model objects and validation configuration.

Testing 

Tibco GI provides a logging class that provides the client side JavaScript equivalent to loj4j/log4net. Within your JavaScript code, you can instantiate the logger and call myLog.debug(), myLog.error(), myLog.fatal() the logger will dispatch the message to its handler based on its effectiveLevel. The mapping tool uses logging to manage messages generated from the mapping rules test tool.

Figure 6: Error logging in the mapping utility.

Deployment

From the web server’s point of view, our Ajax application is deployed as static files in the following directory structure:

WebContent/
 /gi
  /JSX
  /JSXAPPS
   /struts2

We provide a single index.html file with the following div:

<div style="width:100%;height:400px;">
  <script type"text/javascript"
     src="JSX/js/JSX30.js"
     jsxapppath="JSXAPPS/struts2"
     jsxlt="true">
  </script>
</div>

Final Product

More To Explore:

Dig Deeper on Front-end, back-end and middle-tier frameworks