Apache Pluto和Velocity集成教程示例
Velocity是Apache Software Foundation的一个项目,在Apache Velocity Engine下发布,其中它被视为模板引擎,它提供模板语言来引用Java代码中定义的对象。
Velocity的主要目的是确保Web应用程序中表示层和业务层之间的清晰分隔,这与我们之前使用的众多表示技术类似。
但是,您使用JSP和Servlet开发的Portlet用于注册员工,并且使用JSP作为表示层,使用Standard Portlet作为控制器,并使用Servlet处理注册业务。
这次将使用相同的示例,只是有所不同,因为Velocity模板和Velocity Portlet将分别替换您使用的JSP和Standard Portlet。
本教程将向您提供一个完整的场景,说明如何使用Velocity模板获取功能全面的Portlet,该Portlet可用于将员工保存在数据库中。
项目结构
其中您将获得整个创建项目的完整描述:
员工表
在开始研究在应用程序中集成Velocity模板的方式之前,请先浏览一下用于保留员工的Employee表和用于在数据库中创建Table的SQL语句。
Employee.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=3 DEFAULT CHARSET=utf8;
员工模式
根据MVC(模型-视图-控制器)设计模式,数据必须保留在模型内部,因为它将在应用程序的不同组件(即视图和控制器)之间来回发送。
由于我们只有一个Employee Table,因此Employee模型将如下所示:
Employee.java
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; } }
RegisterEmployeePortlet速度Portlet
Velocity Portlet的创建并不是一件复杂的任务,因为您可能熟悉这里介绍的许多类型的Portlet。
像任何普通的Portlet一样,您的Velocity应该如下所示:
RegisterEmployeePortlet.java
package com.theitroad.portlet; import java.io.IOException; import javax.portlet.ActionRequest; import javax.portlet.ActionResponse; import javax.portlet.PortletException; import javax.portlet.PortletRequestDispatcher; import javax.portlet.RenderRequest; import javax.portlet.RenderResponse; public class RegisterEmployeePortlet extends org.apache.portals.bridges.velocity.GenericVelocityPortlet { public void doView(RenderRequest request, RenderResponse response) throws PortletException, IOException { //Check the status parameter if(request.getParameter("status") == null){ //In case it's null, return into default view that's already defined in the portlet.xml viewPage super.doView(request, response); } else { //In case, the status value is success if(request.getParameter("status").equals("success")){ PortletRequestDispatcher dispatcher = this.getPortletContext().getRequestDispatcher("/register/success.vm"); //delegate the view into success.vm dispatcher.include(request, response); } else { //In case, the status value is failure PortletRequestDispatcher dispatcher = this.getPortletContext().getRequestDispatcher("/register/failure.vm"); //delegate the view into failure.vm 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()); } } }
以下是上述代码的详细说明:
您的Portlet应该扩展org.apache.portals.bridges.velocity.GenericVelocityPortlet。
像任何标准Portlet一样,您有机会覆盖必需的方法。
通常,您可以访问不同类型的请求/响应。
正如您可以像在《 JSP&Servlet教程》中那样将请求委托到JSP视图中一样,您可以使用Velocity标记模板执行相同的操作。
由于我们拥有默认的ViewPage值作为初始参数,因此super.doView的调用将为registerEmployee.vm提供处理请求的机会。
根据状态参数,您可能会访问success.vm或者failure.vm。
您可以在项目中使用定义的路径来引用Velocity模板,因为它们被视为与您处理过的任何演示技术一样。
我们建议的路径是/register/registerEmployee.vm、/register/success.vm和/register/faliure.vm。
RegisterEmployeePortlet速度Portlet描述符
幸运的是,在Portlet部署描述符(Portlet.xml)中定义Portlet时,您不必知道很多细节。
但是,像任何标准Portlet一样,您的Portlet.xml应该如下所示:
portlet.xml
<?xml version="1.0" encoding="UTF-8"?> <portlet-app xmlns="https://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd" version="2.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd"> <portlet> <display-name>Register Employee</display-name> <portlet-name>RegisterEmployee</portlet-name> <portlet-class>com.theitroad.portlet.RegisterEmployeePortlet </portlet-class> <description>Employee Registration</description> <init-param> <name>ViewPage</name> <value>/register/registerEmployee.vm</value> </init-param> <supports> <mime-type>text/html</mime-type> <portlet-mode>VIEW</portlet-mode> </supports> <portlet-info> <title>Employee Registration</title> <keywords>employee, registration</keywords> <short-title>Employee Registration</short-title> </portlet-info> </portlet> </portlet-app>
这是上面输入的代码的详细说明:
您应该定义ViewPage初始参数,以使Portlet通过其包含的页面访问后立即显示registerEmployee.vm。
与ViewPage类似,您可以添加EditPage和HelpPage。
支持不同的视图需要您调整支持元素。
应用程序部署描述符和Maven构建文件
您可能会询问定义的速度模板是如何执行的,因为没有明确声明其完成方式。
实际上,事实上,已定义的Servlet将处理Velocity Templates解释过程,因此一旦您的Portlet将请求委托给任何以* .vm结尾的Velocity资源中,Velocity Servlet将被执行并返回HTML输出。
在下面查看部署描述符(web.xml):
web.xml
<!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> <!-- Define Velocity Servlet --> <servlet> <servlet-name>velocity</servlet-name> <servlet-class>org.apache.portals.bridges.velocity.BridgesVelocityViewServlet</servlet-class> </servlet> <!-- Map *.vm files to Velocity --> <servlet-mapping> <servlet-name>velocity</servlet-name> <url-pattern>*.vm</url-pattern> </servlet-mapping> </web-app>
您会注意到,BridgesVelocityServlet已与映射元素结合使用。
让我们看一下使用过的Maven构建文件:
pom.xml
<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>VelocityBridge</artifactId> <packaging>war</packaging> <version>0.0.1-SNAPSHOT</version> <name>VelocityBridge</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-beanutils</groupId> <artifactId>commons-beanutils</artifactId> <version>1.7.0</version> </dependency> <dependency> <groupId>commons-collections</groupId> <artifactId>commons-collections</artifactId> <version>3.1</version> </dependency> <dependency> <groupId>commons-digester</groupId> <artifactId>commons-digester</artifactId> <version>1.7</version> </dependency> <dependency> <groupId>commons-digester</groupId> <artifactId>commons-digester</artifactId> <version>1.7</version> </dependency> <dependency> <groupId>org.apache.velocity</groupId> <artifactId>velocity</artifactId> <version>1.5</version> </dependency> <dependency> <groupId>org.apache.velocity</groupId> <artifactId>velocity-tools</artifactId> <version>2.0</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> <dependency> <groupId>org.apache.portals.bridges</groupId> <artifactId>portals-bridges-velocity</artifactId> <version>2.0</version> </dependency> <dependency> <groupId>org.apache.portals.bridges</groupId> <artifactId>portals-bridges-common</artifactId> <version>2.0</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.6</source> <target>1.6</target> </configuration> </plugin> </plugins> </build> </project>
速度模板(视图)
如前所述,您可以使用Velocity Templates代替默认的JSP来呈现您请求的视图。
与JSP类似,在Velocity模板中定义了一个隐式对象,以便在开发视图时使用。
让我们看一下创建的视图,看看必须注意的主要事项。
registeremployee.vm
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>Register Employee</title> </head> <body> <form action="$renderResponse.createActionURL()" 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>
以下是上面列出的代码的详细说明:
您可能需要的隐式对象是RenderRequest,RenderResponse和PortletConfig。
您应该分别使用$renderRequest,$renderResponse和$portletConfig来引用这些对象。
由于这些对象是Java对象,因此,它们具有所有相关的功能,就像在标准Portlet中调用它们一样。
我们通过调用$renderResponse.createActionURL()创建了一个actionUrl。
success.vm
<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="$renderRequest.getContextPath()/images/success.jpg" name="$renderResponse.getNamespace()Success" <body> Congratulations ! you've just add a new employee<br<a href="$renderResponse.createRenderURL()">Register Another</a> </body> </html>
failure.vm
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>Register Employee</title> </head> <body> Unfortunately ! you Jan not be able of registering a new employee cause the reason below <br <br <img src="$renderRequest.getContextPath()/images/failed.jpg" name="$renderResponse.getNamespace()Failed" <span style="font-size:small ;font-style: italic;color: red;font-weight: bold;"> $renderRequest.getParameter("exception") <br <br <a href="$renderResponse.createRenderURL()">Try Again</a> </body> </html>
这里是成功和失败速度模板的详细说明:
我们使用$renderRequest.getContextPath()来指定正在运行的应用程序的上下文。
为了获得安全的代码,由于速度模板可能包含相同JavaScript变量/代码,因此您可以使用$renderReponse.getNamespace()来创建唯一的标识符。
您可以通过调用$renderRequest.getParamater()来访问特定的请求属性。
$renderResponse()。
createRenderURL()用于针对您的Portlet渲染请求。
注册员工业务处理程序
处理员工注册业务不是Portlet的任务,因为在将您的组件映射到MVC设计时,它被视为控制器。
无论您使用的是哪种Portlet,我们都有一个单独的Servlet负责处理注册过程;提取请求的参数,并调用数据库实用程序以保存员工。
RegisterEmployeeServlet.java
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); } } }
数据库实用程序
由于我们的应用程序已访问数据库以使雇员实例得以持久保存,因此请使用我们拥有的最佳实践来处理数据库代码,这一点很重要,EmployeeDAO和ConnectionUtility都是用于安全且异步地访问数据库的类。
EmployeeDAO.java
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; } }
ConnectionUtility.java
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; } }
员工注册演示
我们假设您有一个正在运行的Apache Pluto实例,同时您还创建了一个名为theitroad的Portal页面,并且您熟悉了将Portlet部署到Portal页面中的方式。
如果您是第一次听到此消息,那么有必要返回到我们的Apache Pluto简介来完成所有相关的事情。
一旦用户激活了注册动作,Portlet的processAction方法将被执行,然后将注册Register Servlet。
系统会立即通知用户注册操作的结果以及注册操作是否成功完成。