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.
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xmlns="https://java.sun.com/xml/ns/javaee" xsi:schemaLocation="https://java.sun.com/xml/ns/javaee https://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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
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.
-
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.
1234567891011121314151617181920212223242526272829303132package 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 {@Overridepublic 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
1234567891011121314151617181920<%@ page language="java" contentType="text/html; charset=US-ASCII"pageEncoding="US-ASCII"%><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "https://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
1234567891011121314151617<%@ 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" "https://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.
-
That’s all for object-driven action classes implementation, let’s move to the implementation of Modeldriven action classes.
-
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.
12345678910111213141516171819202122232425262728293031package 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> {@Overridepublic String execute() throws Exception {if("pankaj".equals(user.getUserID()) && "admin".equals(user.getPassword())){user.setUserName("Pankaj Kumar");return SUCCESS;}else{return INPUT;}}@Overridepublic User getModel() {return user;}private User user = new User();}Our request and result pages look like below code.
loginModelDriven.jsp
123456789101112131415161718192021<%@ page language="java" contentType="text/html; charset=US-ASCII"pageEncoding="US-ASCII"%><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "https://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
123456789101112131415<%@ 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" "https://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.
-
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.