Struts 2 Hello World Example with Annotations and without struts.xml file

Filed Under: Struts 2

This is the second article in the series of Struts 2 Tutorials. If you have directly come here, I would recommend to check out earlier post too.

Struts 2 Beginners Tutorial

In last tutorial, we looked into the Struts 2 architecture, it’s components and build a simple Struts 2 web application with XML based configuration (struts.xml). In this tutorial we will see how we can avoid struts configuration file completely using annotations or naming conventions.

Struts 2 Convention Concept

Struts 2 uses two methodologies to find out the action classes and result classes. We need to use struts2-convention-plugin API to use any of these methodologies. If you have a normal web application, you can download it’s jar file and put it in the web application lib directory. For maven projects, you can simply add it’s dependency like below.


<dependency>
	<groupId>org.apache.struts</groupId>
	<artifactId>struts2-convention-plugin</artifactId>
	<version>2.3.15.1</version>
</dependency>
  1. Scanning: In this method, we specify package which needs to be scanned for action classes. The configuration needs to be done in web.xml for Struts 2 filter, like below.
    
    <filter>
    	<filter-name>struts2</filter-name>
    	<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
    	<init-param>
    		<param-name>actionPackages</param-name>
    		<param-value>com.journaldev.struts2.actions</param-value>
    	</init-param>
    </filter>
    

    Struts 2 will find action classes by following methods.

    • Any class annotated with @Action or @Actions annotations.
    • Any class implementing Action interface or extending ActionSupport class.
    • Any class whose name ends with Action and contains execute() method. For these classes, naming convention is used to determine action and results.
  2. Naming Convention: Struts 2 will automatically create action for classes name ending with Action. The action name is determined by removing the Action suffix and converting first letter to lowercase. So if class name is HomeAction, then action will be “home”.

    If these classes are not annotated with @Result to provide the result, then result pages are looked into WEB-INF/content directory and name should be {action}-{return_string}.jsp. So if HomeAction action class is returning “success”, the request will be forwarded to WEB-INF/content/home-success.jsp page.

    Using naming convention alone can be very confusing and we can’t use same JSP page for other action classes. So we should try to avoid this and use annotation based configuration.

Now we are ready to create our Hello World struts 2 application using annotations and we won’t have struts 2 configuration file.

Create a dynamic web project in Eclipse Struts2AnnotationHelloWorld and convert it to maven project. The final project looks like below image.

Struts2-Hello-World-Annotation-Project

Maven Configuration

We have added struts2-core and struts2-convention-plugin dependencies in the pom.xml, final pom.xml code is:


<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>Struts2AnnotationHelloWorld</groupId>
	<artifactId>Struts2AnnotationHelloWorld</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>war</packaging>

	<dependencies>
		<dependency>
			<groupId>org.apache.struts</groupId>
			<artifactId>struts2-core</artifactId>
			<version>2.3.15.1</version>
		</dependency>
		<dependency>
			<groupId>org.apache.struts</groupId>
			<artifactId>struts2-convention-plugin</artifactId>
			<version>2.3.15.1</version>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>3.1</version>
				<configuration>
					<source>1.6</source>
					<target>1.6</target>
				</configuration>
			</plugin>
			<plugin>
				<artifactId>maven-war-plugin</artifactId>
				<version>2.3</version>
				<configuration>
					<warSourceDirectory>WebContent</warSourceDirectory>
					<failOnMissingWebXml>false</failOnMissingWebXml>
				</configuration>
			</plugin>
		</plugins>
		<finalName>${project.artifactId}</finalName>
	</build>
</project>

Deployment Descriptor Configuration


<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://java.sun.com/xml/ns/javaee"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
	id="WebApp_ID" version="3.0">
	<display-name>Struts2AnnotationHelloWorld</display-name>

	<filter>
		<filter-name>struts2</filter-name>
		<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
		<init-param>
			<param-name>actionPackages</param-name>
			<param-value>com.journaldev.struts2.actions</param-value>
		</init-param>
	</filter>

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

</web-app>

Notice the init-param element where we are providing action classes package that will be scanned by struts 2.

Result Pages

We have three result pages in our application.

login.jsp


<%@ page language="java" contentType="text/html; charset=US-ASCII"
    pageEncoding="US-ASCII"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%-- Using Struts2 Tags in JSP --%>
<%@ taglib uri="/struts-tags" prefix="s"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=US-ASCII">
<title>Login Page</title>
</head>
<body>
<h3>Welcome User, please login below</h3>
<s:form action="login">
	<s:textfield name="name" label="User Name"></s:textfield>
	<s:textfield name="pwd" label="Password" type="password"></s:textfield>
	<s:submit value="Login"></s:submit>
</s:form>
</body>
</html>

error.jsp


<%@ page language="java" contentType="text/html; charset=US-ASCII"
    pageEncoding="US-ASCII"%>
<%@ taglib uri="/struts-tags" prefix="s"%>
    
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=US-ASCII">
<title>Error Page</title>
</head>
<body>
<h4>User Name or Password is wrong</h4>
<s:include value="login.jsp"></s:include>
</body>
</html>

welcome.jsp


<%@ page language="java" contentType="text/html; charset=US-ASCII"
    pageEncoding="US-ASCII"%>
<%@ taglib uri="/struts-tags" prefix="s"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=US-ASCII">
<title>Welcome Page</title>
</head>
<body>
<h3>Welcome <s:property value="name"></s:property></h3>
</body>
</html>

Now let’s create our Action classes that we will annotate to configure action and result pages.

Action Classes with Annotations


package com.journaldev.struts2.actions;

import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.Actions;
import org.apache.struts2.convention.annotation.Namespace;
import org.apache.struts2.convention.annotation.Namespaces;
import org.apache.struts2.convention.annotation.Result;

import com.opensymphony.xwork2.ActionSupport;

/**
 * An empty class for default Action implementation for:
 * 
 *  <action name="home">
 *		<result>/login.jsp</result>
 *	</action>
 * HomeAction class will be automatically mapped for home.action
 * Default page is login.jsp which will be served to client
 * @author pankaj
 *
 */

@Namespaces(value={@Namespace("/User"),@Namespace("/")})
@Result(location="/login.jsp")
@Actions(value={@Action(""),@Action("home")})
public class HomeAction extends ActionSupport {
}

Notice that HomeAction is an empty class with only purpose to forward the request to login.jsp page.


package com.journaldev.struts2.actions;

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

/**
 * Notice the @Action annotation where action and result pages are declared
 * Also notice that we don't need to implement Action interface or extend ActionSupport
 * class, only we need is an execute() method with same signature
 * @author pankaj
 *
 */
@Action(value = "login", results = {
		@Result(name = "SUCCESS", location = "/welcome.jsp"),
		@Result(name = "ERROR", location = "/error.jsp") })
@Namespaces(value={@Namespace("/User"),@Namespace("/")})
public class LoginAction {

	public String execute() throws Exception {
		if("pankaj".equals(getName()) && "admin".equals(getPwd()))
		return "SUCCESS";
		else return "ERROR";
	}
	
	//Java Bean to hold the form parameters
	private String name;
	private String pwd;
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getPwd() {
		return pwd;
	}
	public void setPwd(String pwd) {
		this.pwd = pwd;
	}
}

Notice the use of @Action, @Actions, @Result, @Namespace and @Namespaces annotations, the usage is self explanatory.

Now when we run our application, we get following response pages.

Struts-Hello-World-Annotation-login

Struts-Hello-World-Annotation-home

Struts-Hello-World-Annotation-error

If you have read the last post where we have developed the same application with struts.xml configuration, you will notice that it’s almost same. The only change is the way we wire our application action classes and result pages.

Comments

  1. Deepak says:

    I am getting the below message while running struts2 hello world web application using annotations:

    SEVERE: Dispatcher initialization failed
    java.lang.RuntimeException: java.lang.RuntimeException: java.lang.RuntimeException: java.lang.RuntimeException: java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
    at com.opensymphony.xwork2.inject.ContainerBuilder$4.create(ContainerBuilder.java:132)
    at com.opensymphony.xwork2.inject.Scope$2$1.create(Scope.java:51)
    at com.opensymphony.xwork2.inject.ContainerImpl.getInstance(ContainerImpl.java:507)
    at com.opensymphony.xwork2.inject.ContainerImpl$8.call(ContainerImpl.java:540)
    at com.opensymphony.xwork2.inject.ContainerImpl.callInContext(ContainerImpl.java:574)
    at com.opensymphony.xwork2.inject.ContainerImpl.getInstance(ContainerImpl.java:538)

    is it because my struts2-core jar file is incomaptible with struts2-convention-plugin?

  2. Sanjay Pal says:

    I am doing an application with Struts2+Hibernate with annotation. It works fine when DAO is directly accessed in Action. I am getting hibernate session null when service layer is introduced between Action and DAO…
    I have used @ParentPackage(“hibernate-default”)
    @InterceptorRef(“basicStackHibernate”)
    @Action(value = “savebook”, interceptorRefs = @InterceptorRef(“defaultStackHibernate”))

    Any help would highly appreciated.

  3. Jose says:

    Thanks so much for this tutorial! Well explained!
    I run your project on Wildfly 9.x application server and it works like a charm.

    Thanks

  4. Laxman says:

    hi Pankaj,

    I tried to follow your annotations based struts2 example later on you follow the examples based on this only and I am getting error in annotations example. The error is as follows:
    May 19, 2015 4:13:22 PM com.opensymphony.xwork2.util.logging.jdk.JdkLogger error
    SEVERE: Exception occurred during processing request: There is no Action mapped for namespace [/] and action name [login] associated with context path [/Struts2Annotations].
    There is no Action mapped for namespace [/] and action name [login] associated with context path [/Struts2Annotations].

    regards,
    Laxman

  5. Getting exception when using struts2 annotations says:

    Hi

    I am getting the exception below

    Severe: Exception occurred during processing request: null
    java.lang.NullPointerException
    at org.apache.struts2.impl.StrutsActionProxy.getErrorMessage(StrutsActionProxy.java:69)
    at com.opensymphony.xwork2.DefaultActionProxy.prepare(DefaultActionProxy.java:185)
    at org.apache.struts2.impl.StrutsActionProxy.prepare(StrutsActionProxy.java:63)
    at org.apache.struts2.impl.StrutsActionProxyFactory.createActionProxy(StrutsActionProxyFactory.java:37)
    at com.opensymphony.xwork2.DefaultActionProxyFactory.createActionProxy(DefaultActionProxyFactory.java:58)
    at org.apache.struts2.dispatcher.Dispatcher.serviceAction(Dispatcher.java:554)
    at org.apache.struts2.dispatcher.FilterDispatcher.doFilter(FilterDispatcher.java:434)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:256)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:214)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:316)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:160)
    at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:734)
    at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:673)
    at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:99)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:174)
    at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:357)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:260)
    at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:188)
    at org.glassfish.grizzly.http.server.HttpHandler.runService(HttpHandler.java:191)
    at org.glassfish.grizzly.http.server.HttpHandler.doHandle(HttpHandler.java:168)
    at org.glassfish.grizzly.http.server.HttpServerFilter.handleRead(HttpServerFilter.java:189)
    at org.glassfish.grizzly.filterchain.ExecutorResolver$9.execute(ExecutorResolver.java:119)
    at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeFilter(DefaultFilterChain.java:288)
    at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeChainPart(DefaultFilterChain.java:206)
    at org.glassfish.grizzly.filterchain.DefaultFilterChain.execute(DefaultFilterChain.java:136)
    at org.glassfish.grizzly.filterchain.DefaultFilterChain.process(DefaultFilterChain.java:114)
    at org.glassfish.grizzly.ProcessorExecutor.execute(ProcessorExecutor.java:77)
    at org.glassfish.grizzly.nio.transport.TCPNIOTransport.fireIOEvent(TCPNIOTransport.java:838)
    at org.glassfish.grizzly.strategies.AbstractIOStrategy.fireIOEvent(AbstractIOStrategy.java:113)
    at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.run0(WorkerThreadIOStrategy.java:115)
    at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.access$100(WorkerThreadIOStrategy.java:55)
    at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy$WorkerThreadRunnable.run(WorkerThreadIOStrategy.java:135)
    at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:564)
    at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.run(AbstractThreadPool.java:544)
    at java.lang.Thread.run(Thread.java:724)

    My annotate action is

    @Actions(value={@Action(“Display”)})
    @Result(name=”success”, location=”/DisplayPerson.jsp”)
    public class StrutsUIAction extends ActionSupport {

    /**
    *
    */
    private static final long serialVersionUID = 1L;

    private String name;

    private int age;
    /*
    @Action(value=”Display”,
    results={
    @Result(name=”success”, location=”/DisplayPerson.jsp”)
    })
    */

    public String execute(){
    return SUCCESS;
    }

    public String getName() {
    return this.name;
    }

    public int getAge() {
    return this.age;
    }

    public void setAge(int age) {
    this.age = age;
    }

    public void setName(String name) {
    this.name = name;
    }
    }

    When I debug I get to see the ActionPropxy proxy object details that is retrieved from the configuration. Where namespace and Action seems to be correct but method is null. I have been trying different versions of maven dependencies for struts2 2.3.15 to 2.3.20 but no luck. I have given actionpackages and all correctly. I am trying it on Glassfish server. Could you please guide me

    1. sarath says:

      Hey try to follow the maven dependencies. You can escape a lot of errors. Thats the trick.

  6. niraj says:

    CoreException: Could not get the value for parameter compilerId for plugin execution default-compile: PluginResolutionException: Plugin org.apache.maven.plugins:maven-compiler-plugin:3.1 or one of its dependencies could not be resolved: The following artifacts could not be resolved: org.codehaus.plexus:plexus-compiler-manager:jar:2.2, org.codehaus.plexus:plexus-compiler-javac:jar:2.2, org.apache.xbean:xbean-reflect:jar:3.4, log4j:log4j:jar:1.2.12, commons-logging:commons-logging-api:jar:1.1, com.google.collections:google-collections:jar:1.0, junit:junit:jar:3.8.2: Failure to transfer org.codehaus.plexus:plexus-compiler-manager:jar:2.2 from http://repo.maven.apache.org/maven2 was cached in the local repository, resolution will not be reattempted until the update interval of central has elapsed or updates are forced. Original error: Could not transfer artifact org.codehaus.plexus:plexus-compiler-manager:jar:2.2 from/to central (http://repo.maven.apache.org/maven2): The operation was cancelled.

    1. Pankaj says:

      Are you behind some firewall? This error has nothing to do with project, it’s because the dependencies are not getting downloaded from maven repository.

  7. Krishna says:

    You need to give extends ActionSupport for the LoginAction to make this work!!!

    1. Pankaj says:

      Please check the comments in the class.

  8. sunil says:

    i am running this code i got the problem after submitting values the error is the requested resource is not available and
    There is no Action mapped for namespace [/] and action name [login] associated with context path [/Struts2XMLHelloWorld].)

    like this is came what should i do

  9. Jewey Dim says:

    This toturial is not working.

  10. Siva Prakash says:

    Hi,
    I am working with Struts 2 annotations.

    May 18, 2014 11:21:29 AM com.opensymphony.xwork2.util.logging.jdk.JdkLogger warn
    WARNING: Could not find action or result
    There is no Action mapped for namespace [/User] and action name [loginAnno] associated with context path [/StrutsDemo]. – [unknown location]

    ————————————————-
    web.xml- action
    action is package..

    Please guide me

    1. Pankaj says:

      First of all check if your application is deployed properly, u can check it through a simple HTML welcome file.

      Then check the URIs and any error messages in deployment, there must be something missing here, may be a typo.

  11. manju says:

    i want to know where are the possibilities of using plugin class in struts l

  12. shekhar says:

    how to map servlet in Struts2.xml

  13. ismail says:

    02-Jan-2014 19:14:27.716 SEVERE [localhost-startStop-1] com.opensymphony.xwork2.
    util.logging.commons.CommonsLogger.error Unable to scan named packages
    java.lang.NullPointerException
    at com.opensymphony.xwork2.util.finder.UrlSet.includeClassesUrl(UrlSet.j
    ava:193)
    at org.apache.struts2.convention.PackageBasedActionConfigBuilder.buildUr
    lSet(PackageBasedActionConfigBuilder.java:442)
    at org.apache.struts2.convention.PackageBasedActionConfigBuilder.readUrl
    s(PackageBasedActionConfigBuilder.java:410)
    at org.apache.struts2.convention.PackageBasedActionConfigBuilder.findAct
    ions(PackageBasedActionConfigBuilder.java:389)
    at org.apache.struts2.convention.PackageBasedActionConfigBuilder.buildAc
    tionConfigs(PackageBasedActionConfigBuilder.java:347)
    at org.apache.struts2.convention.ClasspathPackageProvider.loadPackages(C
    lasspathPackageProvider.java:53)
    at com.opensymphony.xwork2.config.impl.DefaultConfiguration.reloadContai
    ner(DefaultConfiguration.java:260)
    at com.opensymphony.xwork2.config.ConfigurationManager.getConfiguration(
    ConfigurationManager.java:67)
    at org.apache.struts2.dispatcher.Dispatcher.init_PreloadConfiguration(Di
    spatcher.java:446)
    at org.apache.struts2.dispatcher.Dispatcher.init(Dispatcher.java:490)
    at org.apache.struts2.dispatcher.ng.InitOperations.initDispatcher(InitOp
    erations.java:74)
    at org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter
    .init(StrutsPrepareAndExecuteFilter.java:57)

    1. Pankaj says:

      Please provide your environment details. Suggesting to use Apache Tomcat 7

      1. ismail says:

        I am using Tomcat8 RC. I havent tried the above tutorial in 7.

        Let me check and let you know the status in 7.

        Meanwhile can you pls try in 8 ?

  14. kumar says:

    I am unable to execute the following code.Kindly help me to solve the problem.I am getting following error while execution ” java.lang.NoSuchMethodError: com.opensymphony.xwork2.util.finder.UrlSet.(Lcom/opensymphony/xwork2/util/finder/ClassLoaderInterface;Ljava/util/Set;)V”

    1. Pankaj says:

      check ur struts version and tomcat version.

Leave a Reply

Your email address will not be published. Required fields are marked *

close
Generic selectors
Exact matches only
Search in title
Search in content
Search in posts
Search in pages