Thursday, May 5, 2011

Struts 2 and Annotations

Googling around i saw there wasnt any concise article explaining how to integrate Struts 2 with annotations. I will keep this as simple as possible because,well the struts developers have done some awesome work to make it as simple as they can. I will create a sample maven project showing you how to integrate struts 2.

Ok first off lets go ahead and create our maven project.Im going with a maven project because its just easier to create a war and dependencies required for struts as well. So below is my project root pom;

<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.myexamples</groupId>

 <artifactId>my-struts-sample</artifactId>

 <packaging>war</packaging>

 <version>1.0-SNAPSHOT</version>

 <name>My Struts Example</name>

 <dependencies>



  





  <dependency>

   <groupId>javax</groupId>

   <artifactId>javaee-api</artifactId>

   <version>6.0</version>

   <scope>provided</scope>

  </dependency>





  



  <dependency>

   <groupId>commons-collections</groupId>

   <artifactId>commons-collections</artifactId>

   <version>3.2.1</version>

  </dependency>



  <dependency>

   <groupId>commons-logging</groupId>

   <artifactId>commons-logging</artifactId>

   <version>1.1.1</version>

  </dependency>



  <dependency>

   <groupId>commons-configuration</groupId>

   <artifactId>commons-configuration</artifactId>

   <version>1.6</version>

  </dependency>



  <dependency>

   <groupId>commons-lang</groupId>

   <artifactId>commons-lang</artifactId>

   <version>2.5</version>

  </dependency>









  <dependency>

   <groupId>org.slf4j</groupId>

   <artifactId>slf4j-log4j12</artifactId>

   <version>1.5.2</version>

   <scope>provided</scope>

  </dependency>

  <dependency>

   <groupId>log4j</groupId>

   <artifactId>log4j</artifactId>

   <version>1.2.16</version>

   <scope>provided</scope>

  </dependency>



  

  <dependency>

   <groupId>junit</groupId>

   <artifactId>junit</artifactId>

   <version>4.8.1</version>

   <scope>test</scope>

  </dependency>

  







  <dependency>

   <groupId>com.sun</groupId>

   <artifactId>tools</artifactId>

   <version>1.4.2</version>

   <scope>system</scope>

   <systemPath>${java.home}/../lib/tools.jar

   </systemPath>

  </dependency>



  <dependency>

   <groupId>org.apache.struts</groupId>

   <artifactId>struts2-core</artifactId>

   <version>2.0.6</version>

  </dependency>





  



  

  <!-- standard.jar -->

  <dependency>

   <groupId>taglibs</groupId>

   <artifactId>standard</artifactId>

   <version>1.1.2</version>

  </dependency>



  <!-- JSTL -->

  <dependency>

   <groupId>javax.servlet</groupId>

   <artifactId>jstl</artifactId>

   <version>1.1.2</version>

  </dependency>

  

 



 </dependencies>



 <build>

  <plugins>

   <plugin>

    <groupId>org.apache.maven.plugins</groupId>

    <artifactId>maven-war-plugin</artifactId>

    <version>2.0</version>

    <configuration>



     <archive>

      <manifest>

       <addClasspath>true</addClasspath>

       <classpathPrefix>lib/</classpathPrefix>

      </manifest>

     </archive>

     <webResources>

      <resource>

       <!-- this is relative to the pom.xml directory -->

       <directory>${project.basedir}/resources

       </directory>



      </resource>

     </webResources>

     <outputDirectory>${env.JBOSS_HOME}/server/default/deploy</outputDirectory>

     <warName>my-struts-sample</warName>

    </configuration>

   </plugin>

  </plugins>

 </build>



</project>


The structure of the project will be as follows;

    my-struts-sample
            |
            |
            --------->pom.xml
            |
            |
            ----->resources
            |        |
            |        |
            |        ----->js
            |
            ----->src
                   |
                   |
                   ------->main
                             |
                             |
                             ----->webapp
                                     |
                                     |
                                     ------->index.html
                                     |
                                     |
                                     ------->WEB-INF
                                                |
                                                |
                                                ------>web.xml
                                                |
                                                |
                                                ------>jsp
                                                        |
                                                        |
                                                        ------>test
                                                                |
                                                                |
                                                                ---->test.html

That is the normal structure of any maven project. This will create the war file and deploy it within your jboss as i have mentioned to get it from JBOSS_HOME in my pom.xml. You can change it as you need depending on your server.

Afterwards lets create our first action class given below;

package com.test..myexample;
import javax.servlet.http.HttpServletRequest;

import org.apache.struts2.config.Result;
import org.apache.struts2.config.Results;
import org.apache.struts2.interceptor.ServletRequestAware;

@Results({
    @Result(name="success", value="/public/showTest")
})

public class ShowTestAction implements ServletRequestAware{
 
 private HttpServletRequest request;
 
 public void setServletRequest(HttpServletRequest request) {
  this.request = request;
 }
 
 public String  execute(){
  return "success";
 }
}


This is a simple action class. Few things to note here. Always your Action class name should end with the name Action, else it wont get recognized as an action class during package scan phase which i will explain later. For now i am only returning success. You can add more Results as you deem appropriate to handle error situations and others. I have injected the HttpServletRequest instance here as well because almost always you will require this to read your parameters or attributes. Now that you have your action class lets see how we can map out our web.xml and configure struts 2.



<?xml version="1.0" encoding="utf-8"?>

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

 


 <filter>
  <filter-name>struts2</filter-name>
  <filter-class>
   org.apache.struts2.dispatcher.FilterDispatcher
  </filter-class>
  <init-param>
   <param-name>actionPackages</param-name>
   <param-value>
      com.test..myexample
          </param-value>
  </init-param>
 </filter>

 

 <filter-mapping>
  <filter-name>struts2</filter-name>
  <url-pattern>*.action</url-pattern>
 </filter-mapping>

 <servlet>
  <servlet-name>mytest</servlet-name>
  <jsp-file>/WEB-INF/jsp/test/test.html</jsp-file>
 </servlet>

 <servlet-mapping>
  <servlet-name>mytest</servlet-name>
  <url-pattern>/public/showTest</url-pattern>
 </servlet-mapping>

</web-app>

There are a few things to note here. One is that we have defined a filter called struts 2. Within that we have specified an attribute called actionPackages. This is the place where we define the package name in which our action classes reside in. If you have many packages then you can have comma separated package names specified within the param-value attribute.

Next we have specified a filter-mapping attribute. This says that all urls ending with .action should go through the org.apache.struts2.dispatcher.FilterDispatcher which in turn will search in all packages defined to see which action should be called. Hope that is clear to you guys. The rest is just normal servlet mapping which all of you should be familiar with.

Now i will show you the index.html which just does a simple redirect calling our action class which in turn will display our test.html according the success mapping name defined within the action class.


<html>
<head>My Home Page</head>
<body>


<script type="text/javascript">
location.replace('showTest.action');
</script>
</body>
</html>

Here we are saying to redirect to showTest.action. Note that your action name was ShowTestAction. But when you call it you should always make sure the name starts with a simple letter and ends with .action or else it would not be recognized by the struts Filter Dispatcher.

Thats about it guys. I did not give the implementation of the test.html because that can be anything you want. Please feel free to leave a comment if you have any doubts or clarification regarding the tutorial provided. mvn clean install and your good to go with struts 2 with annotations :)

Cheers guys

4 comments:

  1. You have used the 2.0.6 struts version. At the time of writing the latest release of S2 is 2.2.1.1

    ReplyDelete
  2. Hi Maurizo,

    yea you are correct. This is the last version i was working on. Did not update. But as you suggested we can upgrade to that version as well without any issues. Thx for leaving by a comment.

    ReplyDelete
  3. Dinuka - thanks for posting this information. There are a couple of items you may want to research further and clarify.

    This statement:

    I have injected the HttpServletRequest instance here as well because almost always you will require this to read your parameters or attributes.

    Most of the time you do not need to implement the HttpServletRequest interface. Struts 2 has interceptors that take the request attributes and place them on your Action's instance fields.

    This statement:

    Always your Action class name should end with the name Action, else it wont get recognized as an action class during package scan phase.

    is not correct. Struts 2 allows you to name and package your Action classes in several different ways in order for Struts 2 to find the Action classes.

    See http://struts.apache.org/2.2.3/docs/struts-2-annotations.html

    ReplyDelete
  4. Thx alot for this info. I did not know about it. I did know about injecting your your instance variables in struts 1 but did not know it existed in struts 2 with annotations. And the link you provided gave alot of insight too. Thank you very much. your comment is highly appreciated. Thank you taking the time to write this comprehensive comment. Cheers

    ReplyDelete