Liferay Build From Source and Deploy to Local Maven Repository

First of all download source from

http://www.liferay.com/downloads/liferay-portal/available-releases or you can download from svn using following command

svn –username guest co svn://svn.liferay.com/repos/public/portal/trunk portal-trunk

Once you download source extract to one Folder.(Dont have long path it might cross maximum path length in windows if you store this deep inside some path)

Prerequisites

———————–

ANT: Download apache ant and place it in path

Below one is needed for deployment to maven repository

GPG : http://www.gpg4win.org/download.html  (You might have to get older version as build searches for gpg.exe )

Older version can be downloaded from http://ftp.gpg4win.org/

If you want to setup maven repository on your machine then do the following

  1. Download nexus(Opensource Maven Repository) from http://nexus.sonatype.org/downloads/nexus-oss-webapp-1.9.2-bundle.tar.gz
  2. Follow following instructions for Installation http://www.sonatype.com/books/nexus-book/reference/install.html
  3. Start nexus and Open your browser to your newly created nexus (http://localhost:8081/nexus)
  4. Login as administrator (default login is admin / admin123) and Change your password
  5. Go to Repositories and click Add -> Hosted Repository
  6. Create Hosted Repository
  • Repository ID: liferay-ce-snapshots
  • Repository Name: Liferay Snapshot Repository
  • Provider: Maven2 Repository
  • Repository Policy: Snapshot
Now configure your maven to point to local repository
<?xml version="1.0" encoding="UTF-8"?> 
<settings> 
	<mirrors> 
		<mirror> 
			<id>local</id> 
			<name>Local mirror repository</name> 
			<url>http://localhost:8081/nexus/content/groups/public</url> 
			<mirrorOf>*</mirrorOf> 
		</mirror> 
	</mirrors>
	<servers> 
		<server> 
			<id>liferay</id> 
			<username>admin</username> 
			<password>ashwin@123</password> 
		</server> 
	</servers> 
</settings>

Now Goto to folder where you have liferay source

Open build.properties  and paste following in bottom  ( You can use other property file names . Just look at build-common.xml for more property filenames you can use)

 

maven.url=http://localhost:8081/nexus/content/repositories/liferay-ce-snapshots
gpg.passphrase=ashwin
gpg.keyname=Ashwin
lp.version=6.0.6
maven.repository.id=liferay
maven.version=${lp.version}-SNAPSHOT

 

Now Lets generate Key File using GPG which we downloaded before

LiferayGPG
LiferayGPG

 

Once you generate the key. you can now use it in deployment.

Now follow these steps

  1. C:\liferay-portal-src-6.0.6>set ANT_OPTS=”-Xmx1024m -XX:MaxPermSize=256m”
  2. ant jar
  3. ant -f build-maven.xml deploy-artifacts ( This will deploy all the Liferay Artifacs to maven repository you have just configured using nexus)
Now njoy using maven with liferay. Next articles would be to create portlets in Liferay and Using liferay to create portals

     

     

     

     

     

     

     

     

    Advertisements

    OSGI for Beginners Using Maven with Equinox(HowTo)

    I have struggled to understand what OSGI really means for a long time. It has been around since a very long time but not many people are aware of it. It has been hyped as a very complex technology to understand. Here is my attempt to make it simple for any Java Developer. In my view if you understand concept of Interface , JNDI or EJB( Or registering some service in some form)  you will understand OSGI. In short OSGI is a Container where you can register your services through interfaces and those can be accessed any time. Another benefit of OSGI is that all these services can be Installed/Uninstalled/Started/Stopped at runtime (i.e Code can be hot deployed at runtime ) rather than normal requirement we have where we have restart J2EE server. Similar to J2ee containers (Tomcat, WebSphere, Jboss , Weblogic ) OSGI also has container like Equinox( Which is base for Eclipse), Apache Felix …etc

    In this article i’m going to explain OSGI with Eclipse Equinox container. Anyone who has Eclipse IDE installed on their machine has OSGI container also installed in Eclipse plugin’s folder.

    Name of OSGI container jar file looks like   org.eclipse.osgi_<version>.jar

    You can start OSGI like this

    java -jar org.eclipse.osgi_3.5.2.R35x_v20100126.jar -console

    Attached is sample screenshot of how i started my OSGI container ( Its analogous to starting tomcat)

    Now that we started OSGI , let me start creation a HelloWorld OSGI Application using Maven. Lemme show you my project structure first and then display my pom.xml


    Now i’ll display my pom.xml. My pom.xml has 2 more profiles added to create 2 more new modules(MathService and MathServiceClient) which will be later explained in this article

    <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/xsd/maven-4.0.0.xsd">
    	<modelVersion>4.0.0</modelVersion>
    	<groupId>com.linkwithweb.osgi</groupId>
    	<artifactId>HelloWorld</artifactId>
    	<version>0.0.1-SNAPSHOT</version>
    	<name>HelloWorld</name>
    	<dependencies>
    		<dependency>
    			<groupId>org.osgi</groupId>
    			<artifactId>org.osgi.core</artifactId>
    			<version>4.2.0</version>
    		</dependency>
    	</dependencies>
    
    	<build>
    		<finalName>HelloWorld-${version}</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.apache.maven.plugins</groupId>
    				<artifactId>maven-jar-plugin</artifactId>
    				<configuration>
    					<archive>
    						<manifestFile>src/main/resources/META-INF/MANIFEST.MF</manifestFile>
    					</archive>
    				</configuration>
    			</plugin>
    		</plugins>
    	</build>
    
    	<profiles>
    		<profile>
    			<id>MathService</id>
    			<build>
    				<finalName>MathService-${version}</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.apache.maven.plugins</groupId>
    						<artifactId>maven-jar-plugin</artifactId>
    						<configuration>
    
    							<excludes>
    								<exclude>**/*.xml</exclude>
    								<exclude>**/*.bsh</exclude>
    								<exclude>**/*.properties</exclude>
    							</excludes>
    							<archive>
    								<manifestFile>src/main/resources/MathService/META-INF/MANIFEST.MF</manifestFile>
    							</archive>
    						</configuration>
    					</plugin>
    
    				</plugins>
    			</build>
    		</profile>
    		<profile>
    			<id>MathServiceClient</id>
    			<build>
    				<finalName>MathServiceClient-${version}</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.apache.maven.plugins</groupId>
    						<artifactId>maven-jar-plugin</artifactId>
    						<configuration>
    
    							<excludes>
    								<exclude>**/*.xml</exclude>
    								<exclude>**/*.bsh</exclude>
    								<exclude>**/*.properties</exclude>
    							</excludes>
    							<archive>
    								<manifestFile>src/main/resources/MathServiceClient/META-INF/MANIFEST.MF</manifestFile>
    							</archive>
    						</configuration>
    					</plugin>
    
    				</plugins>
    			</build>
    		</profile>
    	</profiles>
    
    </project>

    Now if you observe pom.xml there are 3 different MANIFEST.MF defined for 3 different OSGI bundle’s we create.  Saying so lemme explain you that OSGI bundles are same as Java jar file with its configuration defined in Manifest file of standard jar. OSGI defined few entries in manifest file which are related to OSGi which are read by container to activate the bundle, thus avoiding learning of any new metadata format we generally have for other frameworks

    Here is sample Manifest.MF i have defined for MathServiceClient

    Manifest-Version: 1.0
    Bundle-Name: MathServiceClient
    Bundle-Activator: com.linkwithweb.osgi.service.client.MathServiceClientActivator
    Bundle-SymbolicName: MathServiceClient
    Bundle-Version: 1.0.0
    Import-Package: org.osgi.framework,com.linkwithweb.osgi.service

    If you observe above manifest file you can observer that all the entries except Manifest-Version are OSGI specific entries. These are the entries which define how to deploy a bundles what all it is dependent on and what are extension points it exposes for other services to consume.. etc

    Having explained this lemme first explain a HelloWorld bundle with its MANIFEST.MF and Activator class and its installation into Equinox OSGI Container

    /**
     * 
     */
    package com.linkwithweb.osgi;
    
    import org.osgi.framework.BundleActivator;
    import org.osgi.framework.BundleContext;
    
    /**
     * @author Ashwin Kumar
     * 
     */
    public class HelloActivator implements BundleActivator {
    	public void start(BundleContext context) {
    		System.out.println("Hello World");
    	}
    
    	public void stop(BundleContext context) {
    		System.out.println("Goodbye All");
    	}
    }
    Manifest-Version: 1.0
    Bundle-Name: HelloWorld
    Bundle-Activator: com.linkwithweb.osgi.HelloActivator
    Bundle-SymbolicName: HelloWorld
    Bundle-Version: 1.0.0
    Import-Package: org.osgi.framework

    Now run “mvn clean package” to build our bundle

    It will create HelloWorld-0.0.1-SNAPSHOT.jar in target folder and we can install that into Equinox to test. Here is the image which shows how to install and start our HelloWorld into Equinox

    install file:K:\Ashwin\OSGI\MavenProject\target\HelloWorld-0.0.1-SNAPSHOT.jar

    If you observe above screenshot you can use install command to install the bundle and use the start command on bundle id to start the bundle

    On start of Bundle start method of Activator class is called and while stopping bundle stop method of Activator is called and so you are seeing HelloWorld

    Congratulations you have learned basics of OSGI and you have just deployed your first bundle.

    Now lemme explain my second part of article which explains how to Expose and Consume services of modules

    Exposing and Consuming Services

    To explain this i’ll take a very simple example where i’ll publish a service which can add 2 numbers

    Here is the code which does that

    First we need to define an Interface which we are thinking of exposing to external bundles

    /**
     * 
     */
    package com.linkwithweb.osgi.service;
    
    /**
     * @author Ashwin Kumar
     * 
     */
    public interface MathService {
    
    	/**
    	 * @param a
    	 * @param b
    	 * @return
    	 */
    	public int add(int a, int b);
    }

    Now the implementation Class

    /**
     * 
     */
    package com.linkwithweb.osgi.service;
    
    /**
     * @author Ashwin Kumar
     *
     */
    public class MathServiceImpl implements MathService {
    
    	/* (non-Javadoc)
    	 * @see com.linkwithweb.osgi.service.MathService#add(int, int)
    	 */
    	public int add(int a, int b) {
    		// TODO Auto-generated method stub
    		return a+b;
    	}
    
    }

    Now Activator class which register’s this service with OSGI container

    /**
     * 
     */
    package com.linkwithweb.osgi.service;
    
    import org.osgi.framework.BundleActivator;
    import org.osgi.framework.BundleContext;
    
    /**
     * @author Ashwin Kumar
     * 
     */
    public class MathServiceActivator implements BundleActivator {
    	/*
    	 * (non-Javadoc)
    	 * 
    	 * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)
    	 */
    	public void start(BundleContext context) {
    		MathService service = new MathServiceImpl();
    		// Third parameter is a hashmap which allows to configure the service
    		// Not required in this example
    		context.registerService(MathService.class.getName(), service, null);
    		System.out.println("Math Service Registered");
    	}
    
    	/*
    	 * (non-Javadoc)
    	 * 
    	 * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
    	 */
    	public void stop(BundleContext context) {
    		System.out.println("Goodbye From math service");
    	}
    }

    Here is the manifest file which exposes service

    Manifest-Version: 1.0
    Bundle-Name: MathService
    Bundle-Activator: com.linkwithweb.osgi.service.MathServiceActivator
    Bundle-SymbolicName: MathService
    Bundle-Version: 1.0.0
    Import-Package: org.osgi.framework
    Export-Package: com.linkwithweb.osgi.service

    If you observe this we are Exporting some packages so that they can be consumed later. Also all the package that we are thinking of importign have to be defined here

    Use the following command to build the jar file

    mvn -PMathService package

    and Here is image which displays how to Install and Run the service

    Now lemme explain how to implement consumer

    /**
     * 
     */
    package com.linkwithweb.osgi.service.client;
    
    import org.osgi.framework.BundleActivator;
    import org.osgi.framework.BundleContext;
    import org.osgi.framework.ServiceReference;
    
    import com.linkwithweb.osgi.service.MathService;
    
    /**
     * @author Ashwin Kumar
     * 
     */
    public class MathServiceClientActivator implements BundleActivator {
    	MathService service;
    	private BundleContext context;
    
    	/*
    	 * (non-Javadoc)
    	 * 
    	 * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)
    	 */
    	public void start(BundleContext context) {
    		this.context = context;
    		// Register directly with the service
    		ServiceReference reference = context
    				.getServiceReference(MathService.class.getName());
    		service = (MathService) context.getService(reference);
    		System.out.println(service.add(1, 2));
    	}	/*
    	 * (non-Javadoc)
    	 * 
    	 * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
    	 */
    	public void stop(BundleContext context) {
    		System.out.println(service.add(5, 6));
    	}
    }

    And now manifest file

    Manifest-Version: 1.0
    Bundle-Name: MathServiceClient
    Bundle-Activator: com.linkwithweb.osgi.service.client.MathServiceClientActivator
    Bundle-SymbolicName: MathServiceClient
    Bundle-Version: 1.0.0
    Import-Package: org.osgi.framework,com.linkwithweb.osgi.service

    Now here is how we create package and install

    mvn -PMathServiceClient package

    Source code have been checkedin to following location

    https://linkwithweb.googlecode.com/svn/trunk/osgi-tutorials/MavenProject

     

    Njoy creating OSGI bundles . Main benefit is you can redeploy your bundles/services at runtime.

    Mail me incase you have any doubts

    Struts2 , JSON , JQGrid with annotations

    This article explains my hardships which trying to get the example work. It is still not fully working solution but a solution which takes you through steps any new guys goes through while trying to configure struts2 using annotation. With introduction of annotation concept in Java5 , many framework’s 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 fremawork tags in building UI elements which these framework’s profess ( It would be difficult to reuse/migrate if at all we have migrate in future). I never support people/framework’s who push UI development in hands of developers rather then to designer’s.

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

    <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 your are using annotations.

    Struts2 has made life 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 observer web.xml i’m filtering all requests that are starting with /struts/* using struts filter. This is 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 above code by now you might have understood how URL 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

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

    Here is some background 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 packagesstrut, 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 inturn 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/contentdirectory.
    • 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 return’s 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 methofs
     */
    /*	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 above class
    {"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 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 article in coming week. Got to work on something else so leave it here
    Code has been checked in to following svn location. https://linkwithweb.googlecode.com/svn/trunk/Struts
    So play with this as you want