Newsletter sign-up
View all newsletters

Enterprise Java Newsletter
Stay up to date on the latest tutorials and Java community news posted on JavaWorld

Sponsored Links

Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs

Ajax programming with Struts 2

Five steps to dynamic tables using Struts 2, Dojo and JSON

  • Print
  • Feedback

Page 3 of 5

Setting up the JSP page

Your server side is ready to go and it's time to prepare the display to render all of your gathered data into a table. The first thing you need to do is import the required Dojo components, as shown in Listing 2.

Listing 2. Initializing required Dojo components

<script type="text/javascript">
dojo.require("dojo.rpc.*");
dojo.require("dojo.widget.*");
dojo.require("dojo.widget.FilteringTable");
dojo.hostenv.writeIncludes();
</script>

Dojo's FilteringTable component is capable of displaying data in JSON (JavaScript Object Notation) format. Of course its look and feel is completely customizable with CSS. Your table definition should include headers only, with the field property set to the property to display, as shown in Listing 3.

Listing 3. Table definition

<table class="filterable" dojoType="filteringTable" id="yourTableId"
multiple="false" alternateRows="true" valueField="id">

<thead>
<tr>
<th field="id" dataType="String">ID</th>
<th field="firstName" dataType="String">First Name</th>

<th field="secondName" dataType="String">Second Name</th>
</tr>

</thead>
</table>

Note that FilteringTable was created to filter and sort data on the client side. You'll need to disable this functionality in order to proceed with filtering and sorting on the server side, as shown here:

var yourTable = dojo.widget.byId('yourTableId'); 
yourTable.createSorter = function() { return null;};

You now have two pieces of information (first name and second name) on the client side formatted as JSON strings. It's a good time to think about how you will transport the data between server and client.

Using Dojo and JSON for data transport

Many JavaScript-to-Java RPC solutions are available, most notably DWR. Struts 2 actually cuts out the need to for third-party libraries because it comes bundled with Dojo, which has RPC built in. What's more, the JSON plugin for Struts 2 automatically converts JSON-formatted strings to Java objects.

Preparing for JSON RPC calls

You need to do two things to expose your server-side Action methods to JavaScript on the client. The first is to edit your Struts 2 configuration XML file, as shown in Listing 4.

Listing 4. Struts 2 configuration for RPC calls

<package name="RPC" namespace="/nodecorate" extends="json-default">
<action name="SomeListRpc" class="some.ActionClass"
method="execute">
<interceptor-ref
name="json">

<param name="enableSMD">true</param>

</interceptor-ref>
<result type="json">
<param name="enableSMD">true</param>
</result>
</action>

</package>

The second thing you need to do is mark the ActionClass methods to be exposed as SMD (Standard Method Description format), as shown here:

@SMDMethod public List<Some> getRows(SortedPagedFilter filter)
{...}
@SMDMethod public long getRowCount(SortedPagedFilter filter) {...} 

Calling a Java method from JavaScript

Calling a Java method from JavaScript is as easy as calling a JavaScript function, but you need to have JsonService defined on the page:

<s:url id="smdUrl" namespace="/nodecorate" action="SomeListRpc"/>
var service = new dojo.rpc.JsonService("${smdUrl}");

With the JSON plugin, passing a parameter to a Java method is as easy as creating a JSON string and passing it into a JavaScript function. Dojo RPC calls a callback function once the return value is received from the Java method, and the JSON plugin transparently converts the return value of any type (including collections) into a JSON string. Of course, if an object hierarchy exists it converts the whole hierarchy of objects.

If you had a currentPage JavaScript variable on a page, the JavaScript code to pass a filter to getRows and get a JSON-formatted response would look like what you see in Listing 5.

Listing 5. Preparing contextual info and calling getRows and getRowCount

// define a callback function that will be called once a response is
//received
var rowsCallback = function(bean) {
var yourTable = dojo.widget.byId('yourTableId');
yourTable.store.setData(bean);
};
// construct a filter based on the current FilteringTable sort information
var sortInfo = yourTable.sortInformation[0];
var filter = {field:yourTable
.columns[sortInfo.index].field,direction:sortInfo.direction,page:current
Page};
// add criteria to the filter, dynamically construct your own one
// instead of the hardcoded one below
filter['criteria'] = {firstName:'Ivan',secondName:'Smith'};
var deferred = service.getRows(filter);
// start the RPC process
deferred.addCallback(rowsCallback);

var rowNumCallback = function(rowNumber) {
totalPages = Math.ceil(rowNumber / 20);
// call your fillPagination function rendering pagination info
fillPagination(totalPages);
}
// you don't need sort and page information to count rows, just filtering
//criteria
var filter = {field:'',direction:1,page:0};
filter['criteria'] = {firstName:'Ivan',secondName:'Smith'};
var deferred = service.getRowCount(filter);
// start the RPC process
deferred.addCallback(rowNumCallback);

In this case getRows returns a list of entities, which are Java beans. Sometimes you don't want to transfer all of your bean properties to the client side, however. For example, when a table has fewer columns than the bean has, or when you are transferring a Hibernate entity but you don't want to receive the service fields Hibernate attaches to its entities. You can control all this by adding the next line to the result part of the original action configuration shown in Listing 4. Keep in mind that the comma-separated strings are Java regex patterns.

<param name="excludeProperties">prop1,moreProp</param>


  • Print
  • Feedback

Resources
  • Download the source code for this article.
  • "Web apps in a snap" (Erik Swenson, JavaWorld, March 2003) introduces OpenSymphony WebWork's Action interface and teaches you how to create a login page using WebWork, JSP, and the Velocity template engine.
  • "Dynamic Web pages with JSON" (Ajay Raina and John Jimenez, JavaWorld, November 2006) introduces JavaScript Object Notation and shows how JSON facilitates asynchronous, Ajax-style calls from clients to servers across different domains.
  • "Ajax made simple with DWR" (Cloves Carneiro Jr., JavaWorld, June 2005) introduces Direct Web Remoting and demonstrates its role in a typical Ajax implementation. (Note that Struts 2 comes bundled with RPC support and does not require the use of DWR.)
  • Struts 2 is an extensible framework for creating enterprise-ready Java Web applications. It incorporates features from OpenSymphony WebWork/XWork, Guice, and the Dojo toolkit to facilitate Ajax development.
  • The Struts 2 JSON plugin provides a json result type that serializes actions into JSON, facilitating JavaScript-to-Java interactions in Struts 2 applications.
  • Google Web Toolkit, Echo 2, and IT Mill Toolkit are three Web application frameworks that ease Ajax development by translating Java code to JavaScript.
  • Visit the JavaWorld Development tools research center for more articles about tools and frameworks for Java development.
  • Also check out the JavaWorld developer forums for discussions and Q&A related to Ajax development and Struts 2.