Ajax Dynamic Content with Struts2, JQuery and JSON plugin

This guide explains how to create a simple web application that dynamically populates a page through AJAX, using both Struts2 and the JSON features of JQuery.
First of all it’s required the Json Plugin (available at http://jsonplugin.googlecode.com) that should be placed in the /WEB-INF/lib/ directory (where obviously are placed all the Struts2 jar as explained in other tutorials of this site).

The plugin adds (through its struts-plugin.xml) a new result type defined this way:

<package name="json-default" extends="struts-default">
 <result-types>
  <result-type name="json" class="com.googlecode.jsonplugin.JSONResult"/>
 </result-types>
 ...
</package>

Since it’s defined in the json-default package, in order to use that result inside custom action mappings, there are two choices:

  • the packages containing actions with json result-type have to extend the package json-default and not, as usual, the struts-default package
  • in the packages where json result-type is used, it’s to possible to add the previous <result-types>...</result-types> lines that simply refers to a class contained in the json plugin .jar added to the application.

It’s now possible to define action mappings using json as result type, like the following one:

<package name="testPackage" extends="json-default" namespace="/test">
 <action name="giveMeJsonData" class="testAction" method="giveMe">
  <result type="json">
   <param name="root">jsonData</param>
  </result>
 </action>
...
</package>

The above definition states that the url /test/giveMeJsonData.action will cause the execution of the method public String giveMe() defined inside the class testAction (in this case it’s a Spring managed bean, but it can be even a qualified name of a Struts2 action class, obviously extending ActionSupport class).

The result of that action (with a SUCCESS result code) is the json data structure stored in the jsonData property of the action class, and so available through its getter getJsonData().

An example of the behavior for giveMe() method:

public String giveMe() {
 jsonData = new LinkedHashMap<String, Object>();
 jsonData.put("shoppingCartId", getCartId());
 jsonData.put("datetime", new Date());
 Set<Map<String, Object>> items = new HashSet<Map<String, Object>>();
 for (Item item : businessMethod.findItemsForCart(getCartId())) {
  HashMap<String, Object> itemMap = new HashMap<String, Object>();
  itemMap.put("id", item.getId());
  itemMap.put("quantity", item.getQuantity());
  itemMap.put("price", item.getPrice);
  items.add(itemMap);
 }
 jsonData.put("items", items);
 return SUCCESS;
}

The final step is to use JQuery to call (on a specific event) through AJAX the URL where the action has been defined, and obviously to use the returned data to dynamically populate the page HTML.

function testingJsonAndAjax(cartId) {
 $.getJSON(
  /test/giveMeJsonData.action ,
  {
   cartId: cartId
  },
  function(json) {
   $('#cartId').html(json.shoppingCartId);
   $('#cartCreation').html(json.datetime);
   itemsHtml = "<table>";
   for (i in json.items) {
    itemsHtml += “<tr>”;
    itemsHtml += “<td>” + json.items[i].id + “</td>”;
    itemsHtml += “<td>” + json.items[i].quantity + “</td>”;
    itemsHtml += “<td>” + json.items[i].price + “</td>”;
    itemsHtml += “</tr>”;
   }
   itemsHtml += “</table>”;
   $('#cartItems').html(itemsHtml);
  }
 );
 return false;
}

A sample HTML would look like this

Cart 32233 <a href=”#” onclick="return testingJsonAndAjax(32233)>Refresh</a> <br />
Cart 82382 <a href=”#” onclick="return testingJsonAndAjax(82382)>Refresh</a> <br />

<div id=”cartId”>JQuery will replace this text with the Cart Id returned by the json action</div>
<div id=”cartCreation”>JQuery will replace this text with the Cart creation date returned by the json action</div>
<div id=”cartItems”>Jquery wil replace this text with a HTML table containg all the items of the selected cart</div>

The id will be used by the JQuery selector to determine in which of them the data returned by the json action will be written in.

I hope this tutorial has been useful for a simple introduction to AJAX and JSON using JQuery and Struts2.

EDIT 1: on the Struts User Mailing List, Wes Wannemacher suggested that it would be better to directly put the item object inside the root object returned through JSON.

This is absolutely right and would lead to cleaner code. But I didn’t used that technique for a security reason, i.e. if the Item object is a JPA entity, it may contain some properties that is better not to show to the end users. In the case of a User entity, it would be no good to return in the json data its hashed password.

So I created that ugly workaround, defining some HashMaps and putting there just the specific properties I wish to return in the Json result (and maybe this will save too some HTTP traffic 🙂 )

EDIT 2: on the Struts User Mailing List, Nils-Helge Garli Hegvik suggested that it’s even possible to use the “includeProperties” or “excludeProperties” parameters (as described here) in the result configuration to simply return some objects and the JSON plugin will do the trick of filtering just the specific properties to show.

in the packages where “json” result-type is used, it’s to possibile to add the previous &lt;result-types&gt;…&lt;/result-types&gt; lines that simply refers to a class contained in the .jar added to the application (contained in the json-plugin jar).

Java EE Load Balancing with Tomcat and Apache

This tutorial explains how to configure an Apache HTTPD server to map a specific path on a series of load-balanced Apache Tomcat.
The first step is to define the Virtual Host in the Apache configuration files.
In this case the root directory (on file system) of the site is located in /path/to/your/site/, the name of the site is www.yoursite.com and the path where the Tomcat servers may be reached is /javaee.

In few words, an URL like http://www.yoursite.com/home.html is mapped on the file /path/to/your/site/home.html.

An URL like http://www.yoursite.com/javaee/hello.jsp is mapped to the hello.jsp file contained in javaee.war application deployed on all the Tomcat servers defined in the load balanced cluster.

The configuration of the Apache virtual host:

<VirtualHost *>
	ServerAdmin webmaster@localhost
	ServerName www.yoursite.com
	DocumentRoot /path/to/your/site/
	<Directory /path/to/your/site/>
		Options MultiViews
		AllowOverride All
		Order allow,deny
		allow from all
	</Directory>

	ErrorLog /var/log/yoursite-error.log

	LogLevel warn

	CustomLog /var/log/yoursite-access.log combined

    <Proxy balancer://tomcatservers>
	BalancerMember ajp://tomcatserver.yoursite.com:8009 route=tomcatA retry=60
        BalancerMember ajp://tomcatserver.yoursite.com:8010 route=tomcatB retry=60
	BalancerMember ajp://tomcatserver.yoursite.com:8011 route=tomcatC retry=60
    </Proxy>

    <Location /javaee>
	Allow From All
        ProxyPass balancer://tomcatservers/javaee stickysession=JSESSIONID nofailover=off
    </Location>

</VirtualHost>

The most important settings are Proxy and Location.
In Proxy it’s defined a load balancer made with 3 tomcat servers and an URL is assigned to the balancer, in this case balancer://tomcatservers.

The balancer has three members, everyone with its own URL based on the ajp protocol. In this case Apache will connect to the Tomcat servers on their AJP connectors (an alternative would be to use their HTTP connectors).

The Tomcat servers run on the tomcatserver.yoursite.com hostname and each of them opens its own AJP connector on a different port: the first on 8009 (the default one), the second on 8010, the third on 8011 (obviously if they run on the same hostname/IP they must bind to different ports).

Each Tomcat is identified by a route name: tomcatA, tomcatB and tomcatC. The importance of it will be explained later.

In the Location section, a specific path /javaee of the virtual host is mapped on the previously defined balancer balancer://tomcatservers/javaee. So when someone asks for http://www.yoursite.com/javaee/hello.jsp the virtual host will request that JSP to a randomly chosen Tomcat in the balancer members.

What’s the stickysession attribute? It’s a very useful configuration parameter used in conjunction with the route attributes, defined before.

As probably every Java EE (or Web) developer should know, while browsing on a server, it keeps trace of some data about the browsing session in a server-side HttpSession object. For example an ecommerce web application needs to store somewhere the information about the shopping cart of non registered users.

How the server can associate the remote session data with the specific navigation session? This is done through a cookie (or via a GET parameter in the URL) that gives to the server the session ID value.

In Java EE applications, the cookie name to identify the sessions is JSESSIONID.

This is closely related to the management of the load balancing between the Tomcat servers.

If Apache picked randomly one of the Tomcat to handle a single request and if the next request from the same user/browser was forwarded by the balancer to another Tomcat in that battery, things wouldn’t work correctly.

Each Tomcat doesn’t know anything of the existence of other Tomcat in that balancer configuration and especially a single Tomcat server cannot access the information of http sessions handled by another Tomcat.

In few words, when a Tomcat is chosen to handle the first request from a user/browser, it’s absolutely required that, to keep valid session data, the same Tomcat must be used to handle the following requests coming from that browser/user.

If not, on each request, the session data would be lost and simple tasks, such as building a shopping cart would result impossible.

So, it’s required to tell to Apache what is the session cookie name: JSESSIONID and which is the identifier of the routes to each single tomcat Server: tomcatA, tomcatB, tomcatC.

In this way, Apache will append to the end of the cookie value the information about the route to the specific Tomcat.

Java EE Tomcat Load Balancing

Java EE Tomcat Load Balancing

Finally, the last thing to set-up Apache, is obviously to add to it the modules required by the previous configuration:

  • proxy
  • proxy_balancer
  • proxy_ajp

About Tomcat configuration, there are just few changes to apply to the default configuration, in the Engine section it’s required to add the jvmRoute attribute.

<Engine name="Catalina" defaultHost="localhost" jvmRoute="tomcatA">

This is related to the route parameter defined in the Apache load balancer. So, tomcatA should be set in the configuration of the first Tomcat server, tomcatB in the second and tomcatC in the third one.

Then, the port where AJP connector listens, has to be set accordingly to the apache configuration, so 8009, 8010, 8011 respectively on the first, second and third Tomcat.

The following is the the configuration of the AJP connector:

<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />

It’s not directly related to the setup of the load-balancer, but since they run on the same host, each Tomcat should have its own set of ports.

To prevent any conflict you should change the following settings on the second and third servers: Server port="8005" and Connector port="8080".

I hope this tutorial has given a complete overview about every step required to setup Apache and Tomcat to create a simple load-balanced cluster.

Java EE Web Application with Eclipse Ganymede

First of all setup Eclipse Ganymede for Java EE and add to it a Tomcat6 server, as described in my previous tutorial.

Choose, from the File menu New, Dynamic Web Project in the option panel, give a name to the project (i.e. Struts2-Rest), choose a Target Runtime (that you should have already defined, in my case, Apache Tomcat v.6.0) and click Next.

New Dynamic Web Project

New Dynamic Web Project

Here you can define the web application context root, that’s the relative path where it will be accessibile, if you choose Struts2-Rest as context root, your application will be accessible on http://www.yourserver.com/Struts2-Rest or http://localhost:8080/Struts2-Rest if you use the default Tomcat configuration provided by Eclipse (with the Tomcat HTTP Connector on the port 8080)

The Content Directory parameters defines the directory inside your project where is located the “root” of the .war you’re going to generate, use the default WebContent directory.

And, obviously, Java Source Directory defines the directory where your Java sources will be stored. Just for your information, after compilation, the generated .class files are automatically moved by Eclipse into WebContent/WEB-INF/classes.

New Dynamic Web Project - Web Module

New Dynamic Web Project - Web Module

Just click finish. Your brand new project will appear in the Project Explorer

Project Explorer - New Web Project Created

Project Explorer - New Web Project Created

The Deployment Descriptor is obviously associated with the web.xml file. The Java Resources (src) contains your source code, and WebContent contains all the .jsp files, the static contents (images, css, javascripts) and the WEB-INF directory of your .war that will contain the compiled classes (in /WEB-INF/classes) and the libraries (in /WEB-INF/lib).