Apache Pluto和Velocity集成教程示例

时间:2020-02-23 14:29:41  来源:igfitidea点击:

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。
系统会立即通知用户注册操作的结果以及注册操作是否成功完成。