Struts 2 Action Object-backed and ModelDriven Example

Filed Under: Struts 2

In my earlier posts for Struts 2 for Beginners and Struts 2 Annotation Example, you will notice that the java bean properties are part of Action classes. Actually this is not a good design because most of the times, we would want to have bean classes to hold the application elements data and we want to use them across the application.

Struts 2 provide two different approaches through which we can separate java bean properties from action classes and thus reuse the same java bean with different action classes, business logic or in different JSP pages. These approaches are – Object-backed action classes and ModelDriven action classes.

We will understand both the approaches with simple struts 2 web application. Our final project will look like below image.

Struts2 action modeldriven project

I am using annotations to create action classes, that’s why there is no struts.xml file. Also I have converted the dynamic web project to maven and added struts 2 dependencies in pom.xml, that’s why you don’t see any struts jar in lib directory.

Before we move on to action classes for both approaches, we will look into the common components of the web application.

web.xml


<?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>Struts2FormDataExample</display-name>
  
  <filter>
		<filter-name>struts2</filter-name>
		<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
	</filter>

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

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/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>Struts2FormDataExample</groupId>
	<artifactId>Struts2FormDataExample</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>

Common Java Bean with some properties and getter-setter methods.


package com.journaldev.struts2.beans;

public class User {

	private String userID;
	private String password;
	private String userName;

	public String getUserID() {
		return userID;
	}

	public void setUserID(String userID) {
		this.userID = userID;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public String getUserName() {
		return userName;
	}

	public void setUserName(String userName) {
		this.userName = userName;
	}

}

Now let’s move to the implementation of Object-backed and ModelDriven action classes.

  1. Object-backed Action Class

    For object-backed action classes, we need change in action classes as well as request and response HTML/JSP pages. In action class, we have to add a class level variable for the java bean.

    
    package com.journaldev.struts2.actions;
    
    import org.apache.struts2.convention.annotation.Namespace;
    import org.apache.struts2.convention.annotation.Result;
    import org.apache.struts2.convention.annotation.ResultPath;
    import org.apache.struts2.convention.annotation.Results;
    
    import com.journaldev.struts2.beans.User;
    import com.opensymphony.xwork2.Action;
    
    @org.apache.struts2.convention.annotation.Action("loginObject")
    @Namespace("/")
    @ResultPath("/")
    @Results({ @Result(name = "success", location = "homeObject.jsp"),
    		@Result(name = "input", location = "loginObject.jsp") })
    public class LoginObjectBackedAction implements Action {
    
    	@Override
    	public String execute() throws Exception {
    		if("pankaj".equals(getUser().getUserID()) && "admin".equals(getUser().getPassword())){
    			getUser().setUserName("Pankaj Kumar");
    			return SUCCESS;
    		}else{
    			return INPUT;
    		}
    	}
    
    	private User user;
    
    	public User getUser() {
    		return user;
    	}
    
    	public void setUser(User user) {
    		this.user = user;
    	}
    
    }
    

    Above action class is self explanatory, the only point to note is the variable name of User bean “user”. We have to use the same name in request and response pages. Let’s look at the request and response JSP pages.

    loginObject.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">
    <%@ 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="loginObject">
    		<s:textfield name="user.userID" label="User Name"></s:textfield>
    		<s:textfield name="user.password" label="Password" type="password"></s:textfield>
    		<s:submit value="Login"></s:submit>
    	</s:form>
    </body>
    </html>
    

    Notice the textfield names are user.userID and user.password. It’s clear that the first part is same as User variable in action class and second part is same as User class variables.

    Did you noticed that in action class execute() method, we are setting userName variable value, we will use it in response JSP page.

    homeObject.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="user.userName"></s:property>
    	</h3>
    </body>
    </html>
    

    Now when we execute above action, we get following response pages.

    Struts2-action-object-backed-jsp

    Struts2-action-object-backed-home

    That’s all for object-driven action classes implementation, let’s move to the implementation of Modeldriven action classes.

  2. ModelDriven Action Class

    For ModelDriven action classes, we have to implement com.opensymphony.xwork2.ModelDriven interface. This interface is type-parameterized using Java Generics and we need to provide the type as our bean class. ModelDriven interface contains only one method getModel() that we need to implement and return the bean object.

    The other important point to note is that action class should have a class level variable for the bean and we need to instantiate it. Our ModelDriven action class looks like below.

    
    package com.journaldev.struts2.actions;
    
    import org.apache.struts2.convention.annotation.Namespace;
    import org.apache.struts2.convention.annotation.Result;
    import org.apache.struts2.convention.annotation.ResultPath;
    import org.apache.struts2.convention.annotation.Results;
    
    import com.journaldev.struts2.beans.User;
    import com.opensymphony.xwork2.Action;
    import com.opensymphony.xwork2.ModelDriven;
    
    @org.apache.struts2.convention.annotation.Action("loginModelDriven")
    @Namespace("/")
    @ResultPath("/")
    @Results({ @Result(name = "success", location = "homeModelDriven.jsp"),
    		@Result(name = "input", location = "loginModelDriven.jsp") })
    public class LoginModelDrivenAction implements Action, ModelDriven<User> {
    
    	@Override
    	public String execute() throws Exception {
    		if("pankaj".equals(user.getUserID()) && "admin".equals(user.getPassword())){
    			user.setUserName("Pankaj Kumar");
    			return SUCCESS;
    		}else{
    			return INPUT;
    		}
    	}
    
    	@Override
    	public User getModel() {
    		return user;
    	}
    	
    	private User user = new User();
    
    }
    

    Our request and result pages look like below code.

    loginModelDriven.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="loginModelDriven">
    	<s:textfield name="userID" label="User Name"></s:textfield>
    	<s:textfield name="password" label="Password" type="password"></s:textfield>
    	<s:submit value="Login"></s:submit>
    </s:form>
    </body>
    </html>
    

    homeModelDriven.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="userName"></s:property></h3>
    </body>
    </html>
    

    Notice that in JSP pages, we don’t need to prefix the HTML tags name with action class bean variable name. However we need to make sure that names match with the java bean class variable name.

    When we execute this action, we get following response pages.

    Struts2-action-modeldriven-jsp

    Struts2-action-modeldriven-home

    Thats all for implementation of ModelDriven action classes.

If you look into both the approaches, ModelDriven approach is easy to implement and maintain as it doesn’t require change in the JSP pages.

Further Reading: You know there are four different ways to create Action classes in Struts 2, learn about them as Struts 2 Action.

Comments

  1. Chandu says:

    I have multiple action classes mapped with one ModelDriven object. We retrieved the data when first action class executed and set to the ModelDriven object like as Form and populate in result jsp page. In struts2 every time we are creating the ModelDriven object and data is reset to every action class and unable to populate in jsp pages. Any best way to do this using struts2? Thanks.

  2. Binh Thanh Nguyen says:

    Thanks, nice comparision

  3. pesari says:

    How to implement Model Driven concept if i have multiple objects to access in action class ……

    Thanks in advance ….

  4. Shivang Agarwal says:

    nice …thank you

  5. anu says:

    good explanations.:)

  6. Aditya C says:

    How can we access that model object ( which have come from form we submit ) to anather action class or java class in our application.?

    Thank you for every tutorial you upload .

    1. Pankaj says:

      Action classes handle each request and gets the form data in model objects. You can pass that object to any other helper methods as required.

  7. Radhoo says:

    best explanation

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