Click here to Skip to main content
Click here to Skip to main content

Struts2, JSON, JQGrid with Annotations

By , 26 May 2011
Rate this:
Please Sign up or sign in to vote.

This article explains my hardships while trying to get the example work. It is still not a fully working solution but a solution which takes you through steps any new guys go through while trying to configure struts2 using annotation.

With introduction of annotation concept in Java5, many frameworks tried to exploit it very well to make themselves developer friendly. With respect to Web Framework's Spring MVC is the leader as well as frontrunner in using this. Struts2 has tried to implement the same concepts but for any new developer it be little hard to get it to work as there are not many working articles over the web. I never suggest using any framework tags in building UI elements which these framework's profess (It would be difficult to reuse/migrate if at all we have to migrate in future). I never support people/frameworks that push UI development in hands of developers rather than to designers.

Having said this, here is an example of a sample struts2 application where I have an HTML named index.jsp which calls a Struts2 action in the back-end to get data and presentation as defined in the HTML.

First of all, I'll define by build structure and dependencies. As I'm planning to use Maven, my build structure is default maven build structure. Here is my pom.xml.

<project xmlns=http://maven.apache.org/POM/4.0.0 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
	http://maven.apache.org/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.linkwithweb.struts2</groupId>
	<artifactId>Struts2Example</artifactId>
	<packaging>war</packaging>
	<version>1.0</version>
	<name>Struts2Example Maven Webapp</name>
	<url>http://maven.apache.org</url>
	<dependencies>

		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>3.8.1</version>
			<scope>test</scope>
		</dependency>

		<dependency>
			<groupId>org.apache.struts</groupId>
			<artifactId>struts2-core</artifactId>
			<version>2.1.8</version>
		</dependency>

		<dependency>
			<groupId>org.apache.struts</groupId>
			<artifactId>struts2-convention-plugin</artifactId>
			<version>2.1.8</version>
		</dependency>

		<dependency>
			<groupId>org.apache.struts</groupId>
			<artifactId>struts2-json-plugin</artifactId>
			<version>2.1.8</version>
		</dependency>

		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>servlet-api</artifactId>
			<version>2.5</version>
			<scope>provided</scope>
		</dependency>
	</dependencies>
	<build>
		<finalName>Struts2Example</finalName>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>2.3.1</version>
				<configuration>
					<source>1.5</source>
					<target>1.5</target>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.codehaus.mojo</groupId>
				<artifactId>tomcat-maven-plugin</artifactId>
				<version>1.0</version>
				<configuration></configuration>
			</plugin>

			<plugin>
				<groupId>org.mortbay.jetty</groupId>
				<artifactId>jetty-maven-plugin</artifactId>
				<version>7.1.4.v20100610</version>
				<configuration>
					<webApp>${basedir}/target/medihits-admin
				</webApp>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>

The struts2-convention-plugin-version jar file is needed if you are using annotations.

Struts2 has made life a little simple by avoiding all the Configuration files it needed before this version by providing configuration by annotation if we just see our web.xml, it's pretty straightforward where we configure Struts2 Filter to route all requests to corresponding actions.

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
	<display-name>Struts 2 Sample Web Application</display-name>

	<filter>
		<filter-name>struts2</filter-name>
		<filter-class>org.apache.struts2.dispatcher.FilterDispatcher
		</filter-class>
	</filter>

	<filter-mapping>
		<filter-name>struts2</filter-name>
		<url-pattern>/struts/*</url-pattern>
	</filter-mapping>

</web-app>

If you observe web.xml, I'm filtering all requests that are starting with /struts/* using struts filter. This is the entry point of struts application. Now let me describe how we define an Action in Struts2.

package com.linkwithweb.user.action;

import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.Namespace;
import org.apache.struts2.convention.annotation.Result;
import org.apache.struts2.convention.annotation.ResultPath;

import com.opensymphony.xwork2.ActionSupport;
/**
*Example : http://localhost:8080/struts/User
*/
@Namespace("/struts/User")
@ResultPath(value="/")
public class WelcomeUserAction extends ActionSupport{

	private String username;

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

        // Example URL : http://localhost:8080/struts/User/Welcome
	@Action(value="Welcome", results={
			@Result(name="success",location="pages/welcome_user.jsp")
	})
	public String execute() {

		return SUCCESS;	}
}

Looking at the above code, by now you might have understood how URLs are mapped in Struts2.

Now let me describe my effort to add JSON-Plugin. If you have previously observed my pom, I have added JSON plugin dependency.

<dependency>
    <groupId>org.apache.struts</groupId>
    <artifactId>struts2-json-plugin</artifactId>
    <version>2.1.8</version>
</dependency>

Here is some background on how struts2 works:

The Convention plug-in is the one which does everything in the background. The Convention plug-in does the following things:

  • By default, the Convention plug-in looks for the action classes inside the following packages strut, struts2, action or actions. Here our package name is com.vaannila.action. Any package that matches these names will be considered as the root package for the Convention plug-in.
  • The action class should either implement com.opensymphony.xwork2.Action interface or the name of the action class should end with Action. Here, we extend our WelcomeUser class from com.opensymphony.xwork2.ActionSupport which in turn implements com.opensymphony.xwork2.Action.
  • The Convention plug-in uses the action class name to map the action URL. Here our action class name is WelcomeUser and the URL is welcome-user. The plug-in converts the camel case class name to dashes to get the request URL.
  • Now the Convention plug-in knows which Action class to call for a particular request. The next step is to find which result to forward based on the return value of the execute method. By default, the Convention plug-in will look for result pages in WEB-INF/content directory.
  • Now the Convention plug-in knows where to look for results, but it doesn't know which file to display inside the content directory. The Convention plug-in finds this with the help of the result code returned by the Action class. If “success” is returned, then the Convention plug-in will look for a file name welcome-user-success.jsp or welcome-user.jsp . It need not be a jsp file, it can be even a velocity or freemaker files. If the result value is “input”, it will look for a file name welcome-user-input.jsp or welcome-user-input.vm or welcome-user-input.ftl.

Now id we need to define a Action which returns JSON String, we need to define that action implements json-convention which is defined this way:

package com.linkwithweb.ajax.action;

import java.util.ArrayList;
import java.util.List;

import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.Namespace;
import org.apache.struts2.convention.annotation.ParentPackage;
import org.apache.struts2.convention.annotation.Result;

import com.opensymphony.xwork2.ActionSupport;

/**
 * @author Ashwin Kumar
 *
 */
@Namespace("/struts/Ajax")
@Result(name = "success", type = "json")
@ParentPackage("json-default")
public class AjaxAction extends ActionSupport {

	@Action(
		value = "grid",
			results = { @Result(name = "success", type = "json") })
	public String execute() {
		return "success";
	}
/**
 * To directly write to response and skip all Struts process flows,
 * use the below 2 methods
 */
/*	public HttpServletResponse getResponse() {
		return ServletActionContext.getResponse();
	}*/

	/**
	 * Rename to execute to override
	 *
	 * @return
	 * @throws Exception
	 *//*
	public String executeToOverrride() throws Exception {
		PrintWriter printWriter = null;
		getResponse().setContentType("text / xml;charset = UTF-8");
		getResponse().setHeader("Cache - Control", "no - cache");

		StringBuffer sb = new StringBuffer("<movie>");
		// let's say you have A Movie object "movie"
                  // that needs to be represented as XML
		try {
			printWriter = getResponse().getWriter();
			sb.append("<director>dir</director>");
			sb.append("<language>lang</language>");
			sb.append("<year>year</year>");
			sb.append("</movie>");
			printWriter.print(sb.toString());
		} catch (IOException e) {
			e.printStackTrace();
			throw e;
		} finally {
			printWriter.close();
			printWriter = null;
		}
		return NONE;
	}*/

	/**
	 * @return
	 */
	public String getPage() {
		return "1";
	}

	public int getTotal() {
		return 2;
	}

	public String getRecords() {
		return "13";
	}

	public List<String> getRows() {
		List<String> arrayList = new ArrayList<String>();
		arrayList.add("{'id':'13','cell':
		['13','2007-10-06','Client 3','1000.00','0.00','1000.00',null]}");
		arrayList.add("{'id':'12','cell':
		['12','2007-10-06','Client 2','700.00','140.00','840.00',null]}");
		arrayList.add("{'id':'11','cell':
		['11','2007-10-06','Client 1','600.00','120.00','720.00',null]}");
		arrayList.add("{'id':'10','cell':
		['10','2007-10-06','Client 2','100.00','20.00','120.00',null]}");
		arrayList.add("{'id':'9','cell':
		['9','2007-10-06','Client 1','200.00','40.00','240.00',null]}");
		arrayList.add("{'id':'8','cell':
		['8','2007-10-06','Client 3','200.00','0.00','200.00',null]}");
		arrayList.add("{'id':'7','cell':
		['7','2007-10-05','Client 2','120.00','12.00','134.00',null]}");
		arrayList.add("{'id':'6','cell':
		['6','2007-10-05','Client 1','50.00','10.00','60.00','']}");
		arrayList.add("{'id':'5','cell':
		['5','2007-10-05','Client 3','100.00','0.00',
		'100.00','no tax at all']}");
		arrayList.add("{'id':'4','cell':
		['4','2007-10-04','Client 3','150.00','0.00','150.00','no tax']}");

		return arrayList;
	}
}

If you observe the above code, any URL request of form /struts/Ajax/grid will come to this Action class and as we defined it follows json-default convention and so this class acts as bean and this Java bean is converted to json string in response. Here is a sample response for the above class:

http://localhost:8080/struts/Ajax/grid

{"page":"1","records":"13","rows":["{'id':'13',
'cell':['13','2007-10-06','Client 3','1000.00','0.00','1000.00',null]}",
"{'id':'12','cell':['12','2007-10-06','Client 2','700.00','140.00','840.00',null]}",
"{'id':'11','cell':['11','2007-10-06','Client 1','600.00','120.00','720.00',null]}",
"{'id':'10','cell':['10','2007-10-06','Client 2','100.00','20.00','120.00',null]}",
"{'id':'9','cell':['9','2007-10-06','Client 1','200.00','40.00','240.00',null]}",
"{'id':'8','cell':['8','2007-10-06','Client 3','200.00','0.00','200.00',null]}",
"{'id':'7','cell':['7','2007-10-05','Client 2','120.00','12.00','134.00',null]}",
"{'id':'6','cell':['6','2007-10-05','Client 1','50.00','10.00','60.00','']}",
"{'id':'5','cell':['5','2007-10-05','Client 3','100.00','0.00','100.00',
'no tax at all']}","{'id':'4','cell':['4','2007-10-04','Client 3','150.00',
'0.00','150.00','no tax']}"],"total":2}

I have integrated JQGrid multiselect functionality in index.jsp. Here is the sample code which configures jqgrid multiselect:

jQuery("#list9").jqGrid({
   	url:'response.jsp?time='+new Date().getTime(),
	datatype: "json",
   	colNames:['Inv No','Date', 'Client', 'Amount','Tax','Total','Notes'],
   	colModel:[
   		{name:'id',index:'id', width:55},
   		{name:'invdate',index:'invdate', width:90},
   		{name:'name',index:'name', width:100},
   		{name:'amount',index:'amount', width:80, align:"right"},
   		{name:'tax',index:'tax', width:80, align:"right"},
   		{name:'total',index:'total', width:80,align:"right"},
   		{name:'note',index:'note', width:150, sortable:false}
   	],
   	rowNum:10,
   	rowList:[10,20,30],
   	pager: '#pager9',
   	sortname: 'id',
	recordpos: 'left',
    viewrecords: true,
    sortorder: "desc",
	multiselect: true,
	caption: "Multi Select Example"
});
jQuery("#list9").jqGrid('navGrid','#pager9',
{add:false,del:false,edit:false,position:'right'});
jQuery("#m1").click( function() {
	var s;
	s = jQuery("#list9").jqGrid('getGridParam','selarrrow');
	alert(s);
});
jQuery("#m1s").click( function() {
	jQuery("#list9").jqGrid('setSelection',"13");
});

Here is how index.jsp looks like after running (you can use mvn -Djetty.port=9090 jetty:run or mvn tomcat:run):

Will make it more professional and fix all issues along with the article in the coming week. Got to work on something else, so leaving it here.

The code has been checked into the following svn location: https://linkwithweb.googlecode.com/svn/trunk/Struts

So play with this as you want.

License

This article, along with any associated source code and files, is licensed under The Apache License, Version 2.0

About the Author

AshwinRayaprolu
Chief Technology Officer Northalley
United States United States
A Technology evangelist with no technical language barriers. A strong believer that Simple Sofware Is Perfect Software. A staunch proponent of software / documentation automation in all domain's. And finally a true diciple of Google Search.

Comments and Discussions

 
QuestionNot able to run your code PinmemberMember 946026718-Mar-13 3:52 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.140415.2 | Last Updated 26 May 2011
Article Copyright 2011 by AshwinRayaprolu
Everything else Copyright © CodeProject, 1999-2014
Terms of Use
Layout: fixed | fluid