Portlet Servlet JSP
在之前研究过的以前的教程中,RenderResponse编写器主要用于将Portlet的片段写入Portal页面。
形成Portlet内容的这种方式并没有得到很好的使用,因为它也没有遵循关注点分离(SoC)概念。
结合地结合业务代码和UI片段将导致我们难以维护,难以追踪和缺乏敏捷性的代码。
因此,对您的各个部分进行某种分离非常重要。
用户界面和业务。
Portlet Servlet JSP
本教程将帮助您实现所有这些概念,因为它向您展示了如何在Portlet,JSP和Servlet之间进行协调以使员工注册任务如此井井有条的完整示例。
我们将要执行的注册表格是完整的表格,因为您可以填写想要注册的员工的所有必需信息。
Portlet接受并且数据库应保留的信息,例如员工标识符,员工姓名,员工职位和员工薪水。
任何专业的应用程序都必须向最终用户提供确认消息,或者过程成功完成或者发生错误。
在这两者之间,您将学习所有详细的细粒度信息,这可能有助于您实现Portlet,JSP和Servlet之间的集成。
不同的集成原则,例如RequestDispatcher,Portlet标记库,处理异常以及可以利用所有这些原则来顺利完成所有工作的方式。
Portlet标签库
JavaServer Pages可以使用Portlet标记库来访问Portlet功能。
任何JSP页面都可以导入" pluto-taglib"依赖项已经提供的Portlet标记库。
<defineObjects 、、 <param 、、 <actionURL 、、 <renderURL 和<namespace ,所有这些都提供了供JavaServer Page使用的标记。
" <defineObjects "标签用于根据JSP页面中调用Portlet的请求定义多个对象。
使用 <defineObjects
将在您的JSP脚本中定义三个不同的变量。
这些变量是" portletConfig"," renderRequest"和" renderResponse"。
标签<param>用于发送<actionURL 和<renderURL 的参数。
每个参数必须是一对名称和值。
<actionURL 和<renderURL 用于分别生成用于调用动作阶段的动作URL和用于调用渲染阶段的渲染URL。
他们两个都提供了var属性,稍后将在表单操作或者链接目标中使用。
<namespace 用于生成唯一的名称,该名称将与JavaScript代码一起使用以创建唯一JavaScript变量,库或者资源。
Portlet请求分派器
与Servlet类似," RequestDispacther"将请求分配到另一个Web资源中。
但是,如果您对PortletContext对象调用了getRequestDispatcher()或者getNamedDispatcher(),则可能会获得" PortletRequestDispatcher"对象。
此处的区别在于," getRequestDispatcher()"已使用路径来定位Web资源,而" getNamedDispatcher()"已使用已定义的名称来定位Web资源。
您可以在" PortletRequestDispatcher"上调用" include"和" forward"。
使用forward
将在调用后关闭输出流,而include
方法则使输出流保持打开状态。
员工表设计
在开始实施注册表格之前,让我们看一下表格的结构及其相关的列以及使您在数据库中创建类似表格的SQL语句。
CREATE TABLE `employee` ( `EMP_ID` int(11) NOT NULL AUTO_INCREMENT, `EMP_NAME` varchar(45) DEFAULT NULL, `EMP_JOB` varchar(45) DEFAULT NULL, `EMP_SALARY` int(11) DEFAULT NULL, PRIMARY KEY (`EMP_ID`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
员工POJO
正如我们之前所说的Employee Table,您很容易想到Employee POJO。
package com.theitroad.data; public class Employee { private int id; private String name; private String job; private int salary; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getJob() { return job; } public void setJob(String job) { this.job = job; } public int getSalary() { return salary; } public void setSalary(int salary) { this.salary = salary; } }
如您所见,Employee POJO只是一个普通的Java bean,包含四个不同属性及其gets和set方法。
ID,名称,工作和薪水是用户必须提供的属性,以使雇员正常注册。
设计图
主要是,已实现的大量框架已将MVC(模型,视图和控制器)视为其基本设计。
MVC是一种设计模式,旨在分离关注点,以允许以松散耦合的方式开发组件。
成为MVC是一种设计模式,适用于您将Portlet设计为符合MVC。
JSP可以用于查看目的,Portlet作为控制器,简单的POJO(普通Java对象)可以作为您的模型。
在下面的序列图中查找,它向您显示了注册过程的处理方式以及所提议的部分如何共同工作,从而使此任务成为可能。
以下是上面列出的图表的详细说明:
RegisterEmployee JSP页面是用于收集正在注册的员工信息的视图。
RegisterEmployeePortlet是控制器,它将用于处理业务并将其分派到下一个视图中。
我们添加了RegisterEmployeeServlet作为补充组件,旨在处理员工注册业务。EmployeeDAO和ConnectionUtility只是包含所有相关数据库代码的组件。
员工注册视图
我们为员工注册目的定义了不同的视图,因为注册过程可能会导致成功或者失败状态,因此我们为主要视图添加了另外两个视图(registerEmployee.jsp)。success.jsp
和failure.jsp
应该可以帮助用户获取确认消息,以了解该过程是成功完成还是失败以及失败的原因是什么。
registerEmployee.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib uri='https://java.sun.com/portlet' prefix='portlet'%> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>Register Employee</title> </head> <body> <portlet:actionURL var="registerLink" <form action="<%=registerLink%>" method="POST"> <table width="100%"> <tr width="60%"> <td>Enter Employee ID:</td> <td><input name="employeeID" </td> </tr> <tr width="60%"> <td>Enter Employee Name:</td> <td><input name="employeeName" </td> </tr> <tr width="60%"> <td>Enter Employee Job:</td> <td><input name="employeeJob" </td> </tr> <tr width="60%"> <td>Enter Employee Salary:</td> <td><input name="employeeSalary" </td> </tr> <tr width="60%" align="center"> <td colspan="2"><input type="submit" value="Register" </td> </tr> </table> </form> </body> </html>
以下是上面列出的代码的详细说明:
员工注册表格中不包含任何奇怪的代码,除了与<portlet:actionURL 标签相关的代码。
通常,操作URL用于生成一个URL,单击该URL会调用prcoessAction()
。我们使用生成的操作网址来构成整个表单的操作。
注意使用Scriptlet访问生成的URL。
success.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib uri='https://java.sun.com/portlet' prefix='portlet'%> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>Register Employee</title> </head> <portlet:renderURL var="registerAnother"> <portlet:param name="status" value="initiate" </portlet:renderURL> <img src="<%=request.getContextPath()%>/images/success.jpg" name="<portlet:namespaceSuccess" <body> Congratulations ! you've just add a new employee<br<a href="<%=registerAnother%>">Register Another</a> </body> </html>
以下是上面列出的代码的详细说明:
我们已经使用了<renderURL 来生成用于调用渲染阶段的渲染URL。
传递了一个参数,该参数的状态将用于调度目的。可以使用由JSP和Servlet接收的隐式对象,例如"请求","响应",但是有一个限制,因为它们的许多方法在Portlet容器中使用时都不执行任何操作或者返回null。
failure.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib uri='https://java.sun.com/portlet' prefix='portlet'%> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>Register Employee</title> </head> <portlet:defineObjects <portlet:renderURL var="registerAnother"> <portlet:param name="status" value="initiate" </portlet:renderURL> <body> Unfortunately ! you Jan not be able of registering a new employee cause the reason below <br <br <img src="<%=request.getContextPath()%>/images/failed.jpg" name="<portlet:namespaceFailed" <span style="font-size:small ;font-style: italic;color: red;font-weight: bold;"> <%=renderRequest.getAttribute("exception")%> <br <br <a href="<%=registerAnother%>">Try Again</a> </body> </html>
以下是上面列出的代码的详细说明:
我们使用了两个不同的Portlet标签,分别是<renderURL 和<defineObjects 。
如果不使用
<defineObjects
Portlet标记,我们将永远无法访问隐式对象renderRequest。可以使用由JSP和Servlet接收的隐式对象,例如"请求","响应",但是有一个限制,因为它们的许多方法在Portlet容器中使用时都不执行任何操作或者返回null。
员工注册门户
正如我们定义的视图一样,它涵盖了员工注册时可能遇到的所有情况。
现在是时候在" RegisterEmployeePortlet"下面定义控制器了,它可以帮助您分派到所需的视图中。
package com.theitroad.portlet; import java.io.IOException; import javax.portlet.ActionRequest; import javax.portlet.ActionResponse; import javax.portlet.GenericPortlet; import javax.portlet.PortletException; import javax.portlet.PortletRequestDispatcher; import javax.portlet.RenderRequest; import javax.portlet.RenderResponse; public class RegisterEmployeePortlet extends GenericPortlet{ public void doView(RenderRequest request, RenderResponse response) throws PortletException, IOException { if(request.getParameter("status") == null){ //Create a dispatcher PortletRequestDispatcher dispatcher = this.getPortletContext().getRequestDispatcher("/register/registerEmployee.jsp"); dispatcher.include(request, response); } else if(request.getParameter("status") != null && request.getParameter("status").equals("initiate")){ //Create a dispatcher PortletRequestDispatcher dispatcher = this.getPortletContext().getRequestDispatcher("/register/registerEmployee.jsp"); dispatcher.include(request, response); } else if(request.getParameter("status") != null && request.getParameter("status").equals("success")){ //Create a dispatcher PortletRequestDispatcher dispatcher = this.getPortletContext().getRequestDispatcher("/register/success.jsp"); dispatcher.include(request, response); } else if(request.getParameter("status") != null && request.getParameter("status").equals("failed")){ //Create a dispatcher PortletRequestDispatcher dispatcher = this.getPortletContext().getRequestDispatcher("/register/failure.jsp"); request.setAttribute("exception", request.getParameter("exception")); dispatcher.include(request, response); } } public void processAction(ActionRequest request, ActionResponse response) throws PortletException, IOException{ //Create request dispatcher PortletRequestDispatcher dispatcher = this.getPortletContext().getNamedDispatcher("RegisterEmployeeServlet"); try { //Include dispatcher.include(request, response); //Set render parameter response.setRenderParameter("status", "success"); } catch(Exception ex){ //Set render parameter response.setRenderParameter("status", "failed"); response.setRenderParameter("exception", ex.getMessage()); } } }
以下是上面列出的代码的详细说明:
RegisterEmployeePortlet从其GenericPortlet父类覆盖两个方法。
doView()
方法将根据status参数确定应委派哪个视图。processAction()
将处理注册动作,因为它应该将动作委派给RegisterEmployeeServlet
。如果
RegisterEmployeeServlet
成功完成其工作,则将状态success
参数设置为呈现参数,然后在此之后由呈现阶段访问。如果
RegisterEmployeeServlet
启动了异常,则catch块将对其进行处理,并将状态'failed'作为渲染参数,以供随后的渲染阶段访问。使用" getRequestDispatcher()"来定位JSP,而使用" getNamedDispatcher()"来定位Servlet。
我们已将在部署描述符中注册的Servlet的名称作为参数传递。
员工注册Servlet
RegisterEmployeeServlet用于完成注册过程。
package com.theitroad.servlet; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.log4j.Logger; import com.theitroad.dao.EmployeeDAO; import com.theitroad.data.Employee; public class RegisterEmployeeServlet extends HttpServlet { private static final long serialVersionUID = 1L; Logger logger = Logger.getLogger(RegisterEmployeeServlet.class); public RegisterEmployeeServlet() { super(); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //Create employee Employee employee = new Employee(); //Fill in required data from the request sent employee.setId(Integer.parseInt(request.getParameter("employeeID"))); employee.setName(request.getParameter("employeeName")); employee.setJob(request.getParameter("employeeJob")); employee.setSalary(Integer.parseInt(request.getParameter("employeeSalary"))); try { //Asking employeeDAO creating the employee against registered database Employee createdEmployee = EmployeeDAO.getInstance().createEmployee(employee); //Print out the created employee information logger.info("Employee Created"+createdEmployee); } catch (Exception e) { //Log the exception logger.error("Employee Creation Failed", e); //Throw another exception for notifying the Portlet throw new ServletException(e); } } }
以下是上面列出的代码的详细说明:
由于动作的形式已经使用了POST方法,因此Servlet应该为其提供实现。
雇员对象是通过从请求对象中提取其属性来创建的。
它使用
EmployeeDAO
来创建员工。如果您有一个异常,并且为Portlet通知目的而抛出一个新异常,它将处理异常。
员工DAO和ConnectionUtility
我们实现了两种不同的单例类,分别用于处理员工的数据库操作和获取所需的连接。
package com.theitroad.dao; import java.io.IOException; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; import com.theitroad.dao.utility.ConnectionUtility; import com.theitroad.data.Employee; public class EmployeeDAO { public static EmployeeDAO employeeDAO = null; private EmployeeDAO(){ } public static EmployeeDAO getInstance(){ synchronized(EmployeeDAO.class){ if(employeeDAO == null){ employeeDAO = new EmployeeDAO(); } } return employeeDAO; } public Employee createEmployee(Employee employee) throws SQLException, IllegalAccessException, IOException, ClassNotFoundException{ //Get connection instance Connection connection = ConnectionUtility.getInstance().getConnection(); //Create Prepared Statement PreparedStatement query = connection.prepareStatement("INSERT INTO EMPLOYEE VALUES (?,?,?,?)"); //Set variables query.setInt(1, employee.getId()); query.setString(2, employee.getName()); query.setString(3, employee.getJob()); query.setInt(4, employee.getSalary()); try { //Execute query.execute(); //Return employee instance return employee; } catch(Exception e){ //Close statement query.close(); //Close connection connection.close(); //Throw another exception for notifying the Servlet throw new SQLException(e); } } public boolean deleteEmployee(Employee employee){ return false; } public boolean updateEmployee(Employee employee, int employeeId){ return false; } }
package com.theitroad.dao.utility; import java.io.IOException; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.util.Properties; public class ConnectionUtility { private static ConnectionUtility connectionUtiliy = null; private Connection connection = null; private ConnectionUtility() { } public static ConnectionUtility getInstance() throws IOException, IllegalAccessException, SQLException, ClassNotFoundException{ //Synchronized against connectionUtility instance synchronized(ConnectionUtility.class){ //Check whether the connectionUtility is null or not if(connectionUtiliy == null){ //Create a properties instance Properties properties = new Properties(); //Load properties from classpath properties.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("connection.properties")); //Set connection with connectionUtility connectionUtiliy = new ConnectionUtility(); //Load driver class Class.forName("com.mysql.jdbc.Driver"); //Create connection connectionUtiliy.setConnection(DriverManager.getConnection("jdbc:mysql://localhost:3306/theitroad", properties)); } return connectionUtiliy; } } public Connection getConnection() throws ClassNotFoundException, SQLException, IOException { if(connection.isClosed()){ //Create a properties instance Properties properties = new Properties(); //Load properties from classpath properties.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("connection.properties")); //Load driver class Class.forName("com.mysql.jdbc.Driver"); //Create connection connectionUtiliy.setConnection(DriverManager.getConnection("jdbc:mysql://localhost:3306/theitroad", properties)); } return connection; } public void setConnection(Connection connection) { this.connection = connection; } }
重要的是要知道数据库的用户名和密码是从资源中定义的connection.properties文件中读取的。
部署描述符
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "https://java.sun.com/dtd/web-app_2_3.dtd" > <web-app> <display-name>Employee Registration</display-name> <servlet> <servlet-class>com.theitroad.servlet.RegisterEmployeeServlet</servlet-class> <servlet-name>RegisterEmployeeServlet</servlet-name> </servlet> <servlet-mapping> <servlet-name>RegisterEmployeeServlet</servlet-name> <url-pattern>/registerEmployeeServlet</url-pattern> </servlet-mapping> <taglib> <taglib-uri>https://java.sun.com/portlet</taglib-uri> <taglib-location>/WEB-INF/portlet.com</taglib-location> </taglib> </web-app>
Maven构建文件
<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/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.theitroad</groupId> <artifactId>EmployeeRegistrationApp</artifactId> <packaging>war</packaging> <version>0.0.1-SNAPSHOT</version> <name>EmployeeRegistrationApp</name> <url>https://maven.apache.org</url> <properties> <deployFolder>D:/Apache Pluto/pluto-2.0.3/webapps</deployFolder> </properties> <dependencies> <!-- Java Portlet Specification V2.0 --> <dependency> <groupId>org.apache.portals</groupId> <artifactId>portlet-api_2.0_spec</artifactId> <version>1.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.4</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>jsp-api</artifactId> <version>2.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.3</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> <dependency> <groupId>org.apache.pluto</groupId> <artifactId>pluto-taglib</artifactId> <version>1.1.7</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.32</version> </dependency> </dependencies> <build> <finalName>${project.artifactId}</finalName> <plugins> <!-- bind 'pluto2:assemble' goal to 'process-resources' lifecycle --> <!-- This plugin will read your portlet.xml and web.xml and injects required lines --> <plugin> <groupId>org.apache.portals.pluto</groupId> <artifactId>maven-pluto-plugin</artifactId> <version>2.1.0-M3</version> <executions> <execution> <phase>generate-resources</phase> <goals> <goal>assemble</goal> </goals> </execution> </executions> </plugin> <!-- configure maven-war-plugin to use updated web.xml --> <!-- This plugin will make sure your WAR will contain the updated web.xml --> <plugin> <artifactId>maven-war-plugin</artifactId> <version>2.1.1</version> <configuration> <webXml>${project.build.directory}/pluto-resources/web.xml</webXml> </configuration> </plugin> <plugin> <artifactId>maven-antrun-plugin</artifactId> <executions> <execution> <id>copy</id> <phase>integration-test</phase> <configuration> <tasks> <copy file="target/${project.artifactId}.war" tofile="${deployFolder}/${project.artifactId}.war" </tasks> </configuration> <goals> <goal>run</goal> </goals> </execution> <execution> <id>delete</id> <phase>clean</phase> <configuration> <tasks> <delete file="${deployFolder}/${project.artifactId}.war" <delete dir="${deployFolder}/${project.artifactId}" </tasks> <detail>true</detail> </configuration> <goals> <goal>run</goal> </goals> </execution> </executions> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.1</version> <configuration> <source>1.7</source> <target>1.7</target> </configuration> </plugin> </plugins> </build> </project>
员工注册演示
现在该执行刚刚开发的应用程序了。
首先,请确保您已将应用程序部署到Pluto Portal中,并且可以访问theitroad页面。
进入此页面后,请确保已执行doView()方法并检查状态变量是否为null。
结果,doView()方法已将请求分派到registerEmployee.jsp中。
只需填写所有员工信息。
通过单击Register,将执行RegisterEmployeePortlet的processAction(),RegisterEmployeeServlet将处理整个注册过程。
结果,必须对员工进行注册,并必须确认用户。
现在,您有机会通过单击"注册另一个"链接重新进入注册表单。
此链接在传递状态值后与<renderURL 关联。
如果您尝试使用相同的标识符注册同一名员工,会发生什么情况。
大多数情况下,您都会遇到与数据库有关的例外情况。