lunedì 16 settembre 2013

Post-Redirect-Get (PRG) pattern

http://theopentutorials.com/tutorials/java/design-patterns/post-redirect-get-prg-pattern-in-servlet-jsp/

Post-Redirect-Get (PRG) pattern in Servlet, JSP

20 August 2012 By Nithya Vasudevan 7,165 views 4 Comments
VN:F [1.9.21_1169]
Rating: 3.8/5 (4 votes cast)

Post/Redirect/Get (PRG) Pattern

Post/Redirect/Get (PRG) is a web development design pattern that prevents some duplicate form submissions, creating a more intuitive interface for users.
When a user attempts to refresh the server response on a form submitted through an HTTP POST request can cause the contents of the original HTTP POST request to be resubmitted, possibly causing undesired results, such as a duplicate web purchase.

To avoid this problem, many web developers use the PRG pattern where instead of returning a web page directly, the POST operation returns a redirection where the browser can safely refresh the server response without causing the initial HTTP POST request to be resubmitted.

Project Description

  • In the Servlet example mentioned here, we configured datasource in Apache Tomcat and used @Resource annotation to inject datasource into servlet.
  • Here in this example, the application uses a MVC pattern along with DAO and TO.
  • This example creates a user-defined custom application exception which is used by methods of DAO class to throw the exception to the upper layer.
  • Container injects the data source in servlet during deployment. For simplicity, the servlet handles the application logic as well.
  • DAO creates a connection from data source and inserts a row in Employee table.
  • The view – JSP page – displays the form and status message.
  • Post-Redirect-Get (PRG) pattern is also used to avoid some duplicate form submissions.

Environment Used

  • JDK 6 (Java SE 6) (To install JDK – Windows , Ubuntu)
  • Eclipse Indigo IDE for Java EE Developers (3.7.1) (To install Eclipse, refer this page)
  • Apache Tomcat 6.x (To install Tomcat refer this page)
  • MySQL 5.5 (To install MySQL refer this page)
  • MySQL Connector/J 5.1 JAR file
  • JSTL JAR file (jstl-1.2.jar)
  • Java EE 5 API (Servlet 2.5)
  • Java Database Connectivity (JDBC) API (java.sql.*, javax.sql.*)
  • [Optional] For monitoring and analyzing HTTP headers between the browser and web servers, you can use one of these add-ons of Firefox
    • Live HTTP Headers
    • HttpFox

Create Database and table in MySQL

This example uses an ‘Employee’ table and its description is shown below.
FieldTypeKeyExtra
emp_idintPrimary Keyauto_increment
emp_namevarchar(255)
salarydouble
dept_namevarchar(255)
  • Open command prompt (Windows) or Terminal(Linux) and type
    mysql –u [your-username] –p
    and press enter and type the password.
  • If you are using Windows, you can also use MySQL command line client which will be available in All programs menu.
  • For creating a new database, refer this page. In this example, the database name is ‘exampledb’.
  • After creating the database type the command “use <database_name>;”
  • For creating a new table, refer this page. In this example, the table name is ‘employee

Create Dynamic Web Project

  • Open Eclipse IDE
  • For writing Servlet and JSP, we need to create a new Dynamic Web project. Create this project and name it as “ServletPRGMVC“.

Download MySQL connector

  • The connector can be downloaded from: http://dev.mysql.com/downloads/connector/j/.
  • This tutorial uses 5.1 version. Unzip the connector to a safe location on your computer which contains MySQL Connector J JAR.
  • Copy the MySQL Connector J JAR and paste it inside project’s ‘lib’ folder.

Download JSTL JAR File

Configure Context

  • Configure the JNDI DataSource in Tomcat by adding a declaration for your resource to your Context in context.xml.
  • This xml file contains context information and can be placed in one of the location as explained below,
    • In the /conf/context.xml file
    • In the /conf/[enginename]/[hostname]/context.xml.
    • META-INF/context.xml inside the application.
We will place the context.xml file in appliction’s META-INF folder.
Create a new XML file in META-INF folder and copy the following content.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
<?xml version="1.0" encoding="UTF-8"?>
 <!-- The contents of this file will be loaded for each web application -->
  <Context crossContext="true">
 
    <!-- Default set of monitored resources -->
    <WatchedResource>WEB-INF/web.xml</WatchedResource>
 
   <Resource name="jdbc/testDB" auth="Container"
        type="javax.sql.DataSource"
        maxActive="100" maxIdle="30" maxWait="10000"
        username="<YOUR_DATABASE_USERNAME>"
        password="<YOUR_DATABASE_PASSWORD>"
        driverClassName="com.mysql.jdbc.Driver"
        url="jdbc:mysql://localhost/exampledb"/>
</Context>
Fill the username and password for your database and enter your database name in the url string.

Create Transfer Object (TO) class

Create a new Class “Employee.java” in package “com.theopentutorials.to” and copy the following code.
01
02
03
04
05
06
07
08
09
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
package com.theopentutorials.to;
public class Employee {
    private int employeeId;
    private String employeeName;
    private double salary;
    private String deptName;
 
    public int getEmployeeId() {
        return employeeId;
    }
    public void setEmployeeId(int employeeId) {
        this.employeeId = employeeId;
    }
    public String getEmployeeName() {
        return employeeName;
    }
    public void setEmployeeName(String employeeName) {
        this.employeeName = employeeName;
    }
    public double getSalary() {
        return salary;
    }
    public void setSalary(double salary) {
        this.salary = salary;
    }
    public String getDeptName() {
        return deptName;
    }
    public void setDeptName(String deptName) {
        this.deptName = deptName;
    }
    @Override
    public String toString() {
        return "Employee [employeeId=" + employeeId + ", employeeName="
                + employeeName + ", salary=" + salary + ", deptName="
                + deptName + "]";
    }
}

Create Custom Application Exception class

Create a new Class “ApplicationException.java” in package “com.theopentutorials.exception” and copy the following code.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
package com.theopentutorials.exception;
public class ApplicationException extends Exception {
    private static final long serialVersionUID = 1L;
 
    public ApplicationException() { }
 
    public ApplicationException(String message) {
        super(message);
    }
 
    public ApplicationException(String message, Throwable e) {
        super(message, e);
    }
}

Create DbUtil class

Create a new Class “DbUtil.java” in package “com.theopentutorials.db” and copy the following code. This class is used to close Statement, Connection and ResultSet objects.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package com.theopentutorials.db;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
 
public class DbUtil {
    public static void close(Connection connection) {
        if (connection != null) {
            try {
                connection.close();
            } catch (SQLException e) { }
        }
    }
 
    public static void close(Statement statement) {
        if (statement != null) {
            try {
                statement.close();
            } catch (SQLException e) { }
        }
    }
 
    public static void close(ResultSet resultSet) {
        if (resultSet != null) {
            try {
                resultSet.close();
            } catch (SQLException e) { }
        }
    }
}

Create Data Access Object (DAO) class

Create a new Class “EmployeeDAO.java” in package “com.theopentutorials.dao” and copy the following code.
01
02
03
04
05
06
07
08
09
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
package com.theopentutorials.dao;
 
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import javax.sql.DataSource;
import com.theopentutorials.db.DbUtil;
import com.theopentutorials.exception.ApplicationException;
import com.theopentutorials.to.Employee;
 
public class EmployeeDAO {
 
    DataSource ds;
 
    public EmployeeDAO(DataSource ds) {
        this.ds = ds;
    }
 
    public int addEmployee(Employee employee) throws ApplicationException {
        Connection connection = null;
        Statement stmt = null;
        String query = "insert into employee(emp_name, salary, dept_name) values('"
                + employee.getEmployeeName()
                + "',"
                + employee.getSalary()
                + ",'" + employee.getDeptName() + "')";
 
        int row = -1;
        try {
            connection = ds.getConnection();
            stmt = connection.createStatement();
            row = stmt.executeUpdate(query);
        } catch (SQLException e) {
            ApplicationException exception = new ApplicationException(
                    "Unable to insert data: " + e.getMessage(), e);
            throw exception;
        } finally {
            DbUtil.close(stmt);
            DbUtil.close(connection);
        }
        return row;
    }
}
The constructor gets the datasource object and stores it. DAO method creates Connection and Statement object. After executing the query it returns the result to the upper layer. If any exception occurs, it creates ApplicationException with message and cause (Throwable object) of the exception and throws it.

Create Servlet (Controller)

Create a new Servlet “EmployeeServlet.java” in the package “com.theopentutorials.servlets” and copy the following code.
Here, instead of doing JNDI lookup for the JDBC datasource, we use resource injection using @Resource annotaion and store it in DataSource object. We pass this object to the DAO method.
01
02
03
04
05
06
07
08
09
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
package com.theopentutorials.servlets;
 
import java.io.IOException;
import javax.annotation.Resource;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;
import com.theopentutorials.dao.EmployeeDAO;
import com.theopentutorials.exception.ApplicationException;
import com.theopentutorials.to.Employee;
 
public class EmployeeServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
 
    @Resource(name="jdbc/testDB")
    DataSource ds;
 
    public EmployeeServlet() {
            super();
    }
 
    public void doGet(HttpServletRequest request,
            HttpServletResponse response)
            throws ServletException, IOException {
        RequestDispatcher view = request.getRequestDispatcher("displayEmployee.jsp");
        view.forward(request, response);
    }
 
    public void doPost(HttpServletRequest request,
            HttpServletResponse response)
            throws ServletException, IOException {
 
            //Get Request parameters from form
            String empName = request.getParameter("employeeName");
            String deptName = request.getParameter("deptName");
            double salary = Double.parseDouble(request.getParameter("salary"));
 
            //Create Employee Object
            Employee employee = new Employee();
            employee.setEmployeeName(empName);
            employee.setSalary(salary);
            employee.setDeptName(deptName);
 
            //Invoke method in DAO class passing employee object
            EmployeeDAO empDAO = new EmployeeDAO(ds);
 
            int rows;
            int success = 0;
            try {
                rows = empDAO.addEmployee(employee);
                /*Using PRG Pattern.
                 * Instead of forwarding from doPost() method, we are doing a
                 * redirection to avoid duplicate form submission.
                 */
                if(rows > 0)
                    success = 1;
            } catch (ApplicationException e) {
                //Log the error
                request.setAttribute("error", e.getMessage());
            }
 
            response.sendRedirect("displayEmployee.do?s=" +
                            success);
    }
}
  • When the application is deployed in the tomcat container, container looks up the JDBC resource and injects it into Servlet.
  • In Servlet doPost() method, Employee Transfer Object is created – method in DAO class is invoked which takes TO as parameter. The result is returned to the EmployeeServlet
  • This Servlet redirects to another Servlet setting the success value. Here, instead of forwarding to JSP to display the result, we use Post-Redirect-Get pattern to redirect to GET request of another servlet to avoid duplicate form submission.

Create Display Servlet

Create a new Servlet “DisplayEmployeeServlet.java” in the package “com.theopentutorials.servlets” and copy the following code.
This Servlet constructs the result message and forwards it to the JSP view page.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package com.theopentutorials.servlets;
 
import java.io.IOException;
 
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
public class DisplayEmployeeServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
 
    public DisplayEmployeeServlet() {
        super();
    }
 
    protected void doGet(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        int success = Integer.parseInt(request.getParameter("s"));
        if (success == 1)
            request.setAttribute("result", "Employee Successfully Inserted");
        else
            request.setAttribute("result",
                    "Employee Not Inserted: " + request.getAttribute("error"));
        RequestDispatcher view = request
                .getRequestDispatcher("displayEmployee.jsp");
        view.forward(request, response);
    }
}

Create JSP page (View)

Create a new JSP page in WebContent folder and name it as “displayEmployee.jsp“. This JSP page uses JSP Standard Tag Library (JSTL) along with Expression Language (EL). It displays the form if the result attribute is empty, else it displays the result (stored in the request scope) forwarded by the DisplayEmployeeServlet. To use JSTL libraries, you must include a <taglib> directive in your JSP page.
01
02
03
04
05
06
07
08
09
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
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
 
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Employees</title>
</head>
<body>
    <c:if test="${not empty result}">
        <h5><c:out value="${result}"></c:out></h5>
    </c:if>
 
    <%--  Displays Form --%>
    <form action="employee.do" method="post">
    <table border = "1" cellpadding="5" cellspacing="5">
    <tr>
        <td>Employee Name: </td>
        <td><input type="text" name="employeeName"></td>
    </tr>
    <tr>
        <td>Salary: </td>
        <td><input type="text" name="salary"></td>
    </tr>
    <tr>
        <td>Department Name: </td>
        <td><input type="text" name="deptName"> </td>
    </tr>
    <tr>
        <td colspan="2"><input type="submit" name="submit" value="Insert"> </td>
    </tr>
    </table>
    </form>
</body>
</html>

Configure web.xml

A resource reference is an element in a deployment descriptor that identifies the component’s coded name for the resource. More specifically, the coded name references a connection factory for the resource. In the example in the following section, the resource reference name is “jdbc/testDB“.
Now create a WEB-INF/web.xml for this application.
01
02
03
04
05
06
07
08
09
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
<?xml version="1.0" encoding="UTF-8"?>
    /web-app_2_5.xsd" id="WebApp_ID" version="2.5">
  <resource-ref>
    <description>DB Connection</description>
    <res-ref-name>jdbc/testDB</res-ref-name>
    <res-type>javax.sql.DataSource</res-type>
    <res-auth>Container</res-auth>
  </resource-ref>
 
  <servlet>
    <servlet-name>EmployeeServlet</servlet-name>
    <servlet-class>
        com.theopentutorials.servlets.EmployeeServlet
    </servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>EmployeeServlet</servlet-name>
    <url-pattern>/employee.do</url-pattern>
  </servlet-mapping>
 
  <servlet>
    <servlet-name>DisplayEmployeeServlet</servlet-name>
    <servlet-class>
        com.theopentutorials.servlets.DisplayEmployeeServlet
    </servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>DisplayEmployeeServlet</servlet-name>
    <url-pattern>/displayEmployee.do</url-pattern>
  </servlet-mapping>
</web-app>

Folder Structure

The complete folder structure of this example is shown below.

Output

Deploy the project in Server and start/restart the server.
Use Ctrl + F11 in Eclipse to run the Servlet. The URL for requesting the Servlet is
http://localhost:8080/ServletPRGMVC/employee.do


Program control flow


  1. Web client (browser) sends requests to EmployeeServlet through request URL. Since the request is sent directly to Servlet it is a HTTP GET Request
  2. Servlet in doGet() method forwards the request to JSP.
  3. JSP sends HTTP Response with HTML content to client.
  4. Client submits the form to EmployeeServlet as HTTP POST request.
  5. Servlet creates Transfer Object (TO pattern).
  6. Servlet invokes a method in Data Access Object (DAO pattern).
  7. DAO accesses database to insert a row.
  8. Database Result is returned to DAO method.
  9. DAO returns the result to Servlet.
  10. Servlet checks the result and sets a variable and redirects the result to another Servlet
  11. Client makes a new HTTP GET request to DisplayServlet.
  12. DisplayServlet sets request attribute and forwards it to JSP.
  13. JSP displays the result to the client by reading the request attribute.
The content of HTTP request and response headers can be monitored using browser add-ons such as Live HTTP Headers, HttpFox, etc
Post-Redirect-Get (PRG) pattern in Servlet, JSP, 3.8 out of 5 based on 4 ratings

Nessun commento: