Struts 2.0 is the popular Open Source Presentation Tier framework developed by Apache Group. It is based on MVC Model 2 design pattern. Dispatcher Filter is the front controller for the struts2 based applications. Struts 2.0 has simplified web development for its users by introducing POJO based actions, interceptors, flexible validation and support for many different result types.
also read:
Struts can be used to build the user interface tier of the enterprise application. Whereas, any of the popular ORMs like Hibernate, JPA, iBatis can be used for the persistence tier of the application. Struts2 provides easy integration with these persistence tier frameworks.
This article demonstrates the integration of Struts 2.0 applications with the Java Persistence API (JPA). The concept is explained with the help of a sample application. Knowledge of JPA and Struts 2.0 is the prerequisite for this article.
Development Environment
- NetBeans IDE 6.8
- GlassFish V2.x
- Apache Derby Database
Project Structure
The sample application developed in this article is “StrutsJPADemo” where an employee details are persisted to the database with the help of JPA.
Libraries/Jar Files Required
- Struts 2.0 jar files
- JPA jar files
- Jar file for the database driver (derbyclient.jar, in our case) The complete application structure is shown below:
- The User Interface (JSP pages) is created in the “Web Pages” directory. The java classes (Actions, Entities, Service classes, Resource Bundles, struts.xml) are created in the “Source Packages” directory. The required jar files are present in the “Libraries” directory. The web application deployment descriptor “web.xml” is created by the IDE in the “WEB-INF” subdirectory of “Web Pages”. This sample application stores the employee details into the Derby database.
Environment Set Up
Once the web application is created and all the required libraries are added to the classpath, we can start working on the different components of the application.
Before we start working on the different application components like JSP pages and Java classes, we must first configure the following resources in the application server:
- javax.transaction.UserTransaction
- In our application, we have created a UserTransaction and bound the same in JNDI with the name “UserTransaction”. The snapshot given below shows the same.
- Connection Pool & DSN
- For the database, we have created a connection pool in the application server to connect to ScrambleDatabase in the local machine running at port number 1527. The connection pool is bound to the name “scramblePool” as shown in the snapshot below.
- The next step is to configure the DSN in the application server. Our DSN is bound to the name “jdbc/scramdleDSN” in JNDI. This is shown below in the snapshot. This DSN will use “scramblePool”created in the previous step.
Create Entity Class in JPA
The entity class for our application “Employee.java” is created in the package “com.model.entities”.PFB the code of
“Employee.java”
package com.model.entities; import java.util.Date; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; @Entity @Table public class Employee { @Id private String empid; private String password; private String password1; private String empname; private int age; private String city; private String email; //getters & setters for all the attributes }
Service Layer
The service layer exposes an interface “EmployeeService” and an implementation class “EmployeeServiceImpl” to the users. These classes are defined in the package “com.service”
EmployeeService.java
package com.service; import com.model.entities.Employee; import java.util.List; public interface EmployeeService { public void save(Employee user); }
EmployeeServiceImpl.java
package com.service; import com.model.entities.Employee; import javax.annotation.Resource; import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.NamingException; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.PersistenceContext; import javax.persistence.PersistenceContextType; import javax.transaction.NotSupportedException; import javax.transaction.SystemException; import javax.transaction.UserTransaction; @PersistenceContext(name = "persistence/myStrutsJPA", unitName = "StrutsJPAPersistenceUnit", type = PersistenceContextType.EXTENDED) @javax.ejb.TransactionManagement(javax.ejb.TransactionManagementType.BEAN) public class EmployeeServiceImpl implements EmployeeService { private EntityManager em; @Resource private UserTransaction utx; private EntityManagerFactory emf = null; public UserTransaction getUtx() { return utx; } public void setUtx(UserTransaction utx) { this.utx = utx; } public EmployeeServiceImpl() { try { Context envCtx = (Context) new InitialContext().lookup("java:comp/env"); em = (EntityManager) envCtx.lookup("persistence/myStrutsJPA"); utx = (UserTransaction) envCtx.lookup("UserTransaction"); } catch (NamingException ex) { System.out.println("PU Not found"); ex.printStackTrace(); } } public void setEntityManager(EntityManager em) { this.em = emf.createEntityManager(); System.out.println(em); } public void save(Employee Employee) { try { System.out.println("Tx Status in save:=" + utx.getStatus()); if (utx.getStatus() == 6) { // Incase there's no active Transaction, we'll start one Context envCtx = (Context) new InitialContext().lookup("java:comp/env"); utx = (UserTransaction) envCtx.lookup("UserTransaction"); utx.begin(); } em.persist(Employee); utx.commit(); } catch (NotSupportedException ex) { ex.printStackTrace(); } catch (SystemException ex) { ex.printStackTrace(); } catch (Exception ex) { ex.printStackTrace(); } } public void setEm(EntityManager em) { this.em = em; } public void setEmf(EntityManagerFactory emf) { this.emf = emf; } public EntityManager getEm() { return em; } public EntityManagerFactory getEmf() { return emf; } private EntityManager getEntityManager() { return em; } }
Action Class
Struts does the request processing with the help of Action classes. Here’s the code for the EmployeeAction.java which handles the request to create a new Employee. This class is created in the package
“com.actions”.
EmployeeAction.java
package com.actions; import com.model.entities.Employee; import com.opensymphony.xwork2.ActionSupport; import com.opensymphony.xwork2.ModelDriven; import com.opensymphony.xwork2.Preparable; import com.service.EmployeeServiceImpl; import javax.servlet.http.HttpServletRequest; import org.apache.struts2.interceptor.ServletRequestAware; public class EmployeeAction extends ActionSupport implements ModelDriven, Preparable, ServletRequestAware { private Employee emp; private HttpServletRequest req; private EmployeeServiceImpl empService; public EmployeeAction() { this.empService=new EmployeeServiceImpl(); } public Employee getEmp() { return emp; } public void setEmp(Employee emp) { this.emp = emp; } public EmployeeServiceImpl getEmpService() { return empService; } public void setEmpService(EmployeeServiceImpl empService) { this.empService = empService; } public String execute() throws Exception { empService.save(emp); return SUCCESS; } public Object getModel() { return emp; } public void prepare() throws Exception { emp=new Employee(); } public void setServletRequest(HttpServletRequest hsr) { this.req=hsr; } }
Please note that there’s no validation logic in the action class. We have put the validation logic in the
“EmployeeAction-validation.xml” file kept in the “com.actions” package. Please note that the validation.xml file should always be kept in the same package where your action class is present.
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.2//EN" "http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd"> <validators> <field name="empid"> <field-validator type="requiredstring"> <message>Employee Id is a mandatory field</message> </field-validator> <field-validator type="regex"> <param name="expression"> <![CDATA[([E][m][p][0-9][0-9][0-9])]]> </param> <message>Valid Employee Id required e.g. Emp001</message> </field-validator> </field> <field name="age"> <field-validator type="int"> <param name="min">20</param> <param name="max">80</param> <message>Age needs to between ${min} and ${max}</message> </field-validator> </field> <field name="empname"> <field-validator type="requiredstring"> <param name="trim">true</param> <message>User Name is required</message> </field-validator> </field> <field name="password"> <field-validator type="requiredstring"> <message>Password is required</message> </field-validator> <field-validator type="stringlength"> <param name="maxLength">10</param> <param name="minLength">5</param> <param name="trim">true</param> <message> Enter password between 5 and 10 characters </message> </field-validator> </field> <field name="password1"> <field-validator type="fieldexpression"> <param name="expression">(password==password1)</param> <message> Password and Re-enter password must be same </message> </field-validator> </field> <field name="city"> <field-validator type="requiredstring"> <param name="trim">true</param> <message>You can not leave city as blank</message> </field-validator> </field> <field name="email"> <field-validator type="requiredstring"> <param name="trim">true</param> <message>You can not leave email as blank</message> </field-validator> <field-validator type="email"> <message> The email address you entered is not valid. </message> </field-validator> </field> </validators>
Let us understand the request processing in the action class. The Action class has implemented the following interfaces:
- ServletRequestAware:This simplifies access to the HttpServletRequest object representing the request.
- ModelDriven:This enables the use of a POJO class to contain the data (similar to ActionForm of Struts1, with an exception that the class here is a POJO and doesn’t extend from any framework specific classes/interfaces). In our case, the entity class “Employee.java” created inside the “com.entities” package acts
as the model. - PreparableThis prepares the action class for use.
The data validation is performed with the help of “EmployeeAction-validation.xml” file. Different validation routines
are configured for the different entity attributes.
The User Interface Tier
The user interface part of the application is created in JSP. The home page of the application is “RegisterEmp.jsp”, where a form is displayed to the users asking for few details for the registration
purpose. PFB the code of “RegisterEmp.jsp”
<%@ taglib prefix="s" uri="/struts-tags"%> <html> <head> <title>Add Employee Details Using Field Validators</title> </head> <body> <h1>Struts2 and JPA Integration Demo</h1> Enter the Employee Details Here <s:form action="addEmployee"> <s:textfield name="empid" key="app.empid" /> <br> <s:textfield name="empname" key="app.empname" /> <br> <s:password key="app.password" name="password" /> <br> <s:password key="app.password1" name="password1" /> <br> <s:textfield name="city" key="app.city" /> <br> <s:textfield name="age" key="app.age" /> <br> <s:textfield name="email" key="app.email" /> <br> <br> <s:submit value="Add Employee" /> </s:form> <s:actionerror/> </body> </html>
Once the user fills in the required details and clicks on the “Submit” button, the request is submitted to the action class. Upon successful validation, a new employee record is inserted in the database and user is navigated to
“RegisterEmp_Success.jsp”page. Here’s the code of “RegisterEmp_Success.jsp”.
<%@ taglib prefix="s" uri="/struts-tags"%> <html> <head> <title>Employee Registration Success Page</title> </head> <body> <h1>Employee Details saved Successfully</h1> Employee Name: <s:property value="empname" /> <br> <br> Password: <s:property value="password" /> <br> <br> Employee Id: <s:property value="empid" /> <br> <br> Age: <s:property value="age" /> <br> <br> City: <s:property value="city" /> <br> <br> E-Mail: <s:property value="email" /> <br> <br> </body> </html>
Struts 2.0 and JPA Configuration Files
- Here’s the configuration added in the “web.xml” file:
<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> <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>/*</url-pattern> </filter-mapping> <persistence-context-ref> <persistence-context-ref-name> persistence/myStrutsJPA </persistence-context-ref-name> <persistence-unit-name> StrutsJPAPersistenceUnit </persistence-unit-name> </persistence-context-ref> <resource-env-ref> <resource-env-ref-name>UserTransaction</resource-env-ref-name> <resource-env-ref-type>javax.transaction.UserTransaction</resource-env-ref-type> </resource-env-ref> <resource-env-ref> <resource-env-ref-name>jdbc/scrambleDSN</resource-env-ref-name> <resource-env-ref-type>javax.sql.DataSource</resource-env-ref-type> </resource-env-ref> <session-config> <session-timeout> 30 </session-timeout> </session-config> <welcome-file-list> <welcome-file>RegisterEmp.jsp</welcome-file> </welcome-file-list> </web-app>
In the deployment descriptor, we have added the following configurations:
- The Welcome File of the application
- Struts2 Dispatcher Filter (Controller element of Struts2 applications)
- UserTransaction
- PersistenceUnit
- Here’s the configuration added in the “struts.xml” file kept in the default package in the “Source Packages” directory.
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <package name="default" extends="struts-default"> <action name="addEmployee" class="com.actions.EmployeeAction"> <interceptor-ref name="exception"/> <interceptor-ref name="alias"/> <interceptor-ref name="servletConfig"/> <interceptor-ref name="prepare"/> <interceptor-ref name="i18n"/> <interceptor-ref name="chain"/> <interceptor-ref name="debugging"/> <interceptor-ref name="profiling"/> <interceptor-ref name="model-driven"/> <interceptor-ref name="params"/> <interceptor-ref name="conversionError"/> <interceptor-ref name="validation"/> <interceptor-ref name="workflow"/> <result name="success">RegEmp_Success.jsp</result> <result name="error">RegisterEmp.jsp</result> <result name="input">RegisterEmp.jsp</result> </action> </package> </struts>
- The labels of various UI components on the JSP page are not hard coded and instead being derived from the resource bundle “ApplicationResources.properties” kept in the “com.messages” package in the “Source Packages” directory.
Here’s the code of “ApplicationResources.properties”
file:
app.title=Struts2 JPA Integration Application app.welcome=Welcome to Struts2 Application app.username=User Name app.city=City app.age=Age app.email=E-Mail button.save=Save app.username.blank=Please enter User Name app.email.blank=Please enter City app.age.blank=Please enter Age app.city.blank=Please enter city app.empid=Employee Id app.password=Password app.password1=Reenter Password app.empname=Employee Name app.doj=Date of joining
We configure “ApplicationResources.properties” as the default resource bundle for our application by configuring an entry in the “struts.properties” file kept in the default package in the “Source Packages” directory.
- Here’s the configuration to be added in the “struts.properties”
file:
struts.custom.i18n.resources=com.messages.ApplicationResources
Executing The Application using Struts 2.0 and JPA
Finally, we are ready to execute the application. Deploy the application to the container and execute. Depending on the configuration in the Persistence Unit to create/update/drop-create the database, the database schema is generated/updated.
In our application, we have set the configuration to create the database from scratch.Before executing the application here’s the database schema:No table with the name “Employee” is present.
Open the Register.jsp page.
Enter some invalid data, The snapshot given below shows the validation error messages
Once, all the validation error messages have been rectified, the user is forwarded to RegisterEmp_Success.jsp page, where the correct data submitted is displayed.
Check the database structure, by refreshing the database schema. A new table “Employee” is created with one record in the database.
Conclusion
Thus, any Struts application can seamlessly integrate with JPA easily.