Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / JSON

Struts2, JSON, JQGrid With Annotations

0.00/5 (No votes)
21 Nov 2015Apache4 min read 8.2K  
Struts2, JSON, JQGrid with annotations

This article explains my hardships while trying to get the example to work. It is still not a fully working solution but a solution which takes you through steps any new person goes 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 will be a 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 frameworks profess (It would be difficult to reuse/migrate if at all we have migrate in future). I never support people/frameworks who push UI development in the hands of developers rather than to designers.

Saying this here is an example of a sample struts2 application where I have an html named index.jsp which calls a Struts2 Action in back-end to get data and presentation as defined in 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.

Image 1

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.

HTML
<!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.

Java
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 depdendency.

XML
<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 if we need to define an Action which returns JSON String, we need to define that action implements json-convention which is defined this way.

Java
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 sample response for the above class.

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

XML
{"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.

Java
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).

Image 2

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

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

So play with this as you want.

Image 3 Image 4

License

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