Apache Pluto,Portlet Bridge和JSF 2.0集成示例教程

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

在之前的使用JSP和Servlet开发Portlet中,我们阐明了如何使用Portlet,JSP和Servlets创建易于维护,易于调试和易于开发的MVC体系结构应用程序。

但是为什么我们会花很多时间来开发这种编排,而其他编排却已经做了呢。
JSF框架是MVC兼容框架,因为其组件包含关注分离(SoC)的概念。

Facelets技术用于处理视图,它的支持bean充当控制器,并且您可以使用其Expression Language将视图与建议的模型粘合在一起。

您可以在Portlet中使用它的优雅框架,这将使它们成为基于JSF的Portlet。
为此,Java Community(JC)提出了两种不同的JSR(Java Specification Request),以简化将Portal与知名的JSF框架集成的过程。

JSR-301是Portlet容器规范,它定义了代理JSF工件的JSR-168,286(Portlet规范)的必需行为。
同时,JSR-329适用于JavaServer Faces(JSF)1.2规范的Portlet容器2.0桥,该规范定义了允许JSF Application/View作为Java Portlet访问所需的行为。

在编写这些行之前,尽管已经提供了实现,但尚未发布JavaServer Faces 2.0的规范。
本教程将为您提供详细的信息,以帮助您将所有这些技术有效地结合在一起。

  • 任何门户(符合Portlet容器2.0),例如Apache Pluto。

  • JSF 2.0(使用MyFaces 2.0或者使用参考实现JSF Mojarra 2.0)。

  • 任何与JSR-329兼容的Portlet Bridge,例如MyFaces 2.0 Portlet Bridge。

员工注册操作是实现集成的过程,因为不需要使用Standard Java Portlet,Java Server Pages和Servlet。

数据库设计和所需模型

为了便于说明,最好让我们知道数据库用来保留注册员工的表格的形式。

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;

将根据上图的员工表创建的"员工"实体(模型)将是:

package com.theitroad.data;

public class Employee {
 private String id;
 private String name;
 private String job;
 private String salary;

 public String getId() {
 return id;
 }

 public void setId(String id) {
 this.id = id;
 }

 public String getSalary() {
 return salary;
 }

 public void setSalary(String salary) {
 this.salary = salary;
 }

 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;
 }

}

设计图

员工注册操作的设计视图就像JSF框架所做的那样,桥接过程消除了遍历Portlet容器伴随的需求。

上图的设计描述了执行流程的进行方式,就像您过去制作的任何JSF应用程序一样。

注册员工XHTML

JSF 2.0使用Facelets作为视图技术,而不是JSF 1.x支持的JSP。
注册员工页面如下所示:

register.xhtml

<html xmlns="https://www.w3.org/1999/xhtml"
	xmlns:ui="https://java.sun.com/jsf/facelets"
	xmlns:h="https://java.sun.com/jsf/html"
	xmlns:f="https://java.sun.com/jsf/core">
	<h:head>
		<title>Register Employee</title>
	</h:head>
	<h:body>
		<h:messages showSummary="true" errorStyle="color:red;" infoStyle="color:green;"
		<h:form prependId="false">
			<h:panelGrid columns="2" style="width:100%">
					<h:outputLabel for="id" value="Identity"></h:outputLabel> 
					<h:inputText id="id" value="#{registerEmployeeManagedBean.employee.id}" required="true" requiredMessage="ID is mandatory">
						<f:validateRegex pattern="[0-9]*"></f:validateRegex>
					</h:inputText>			
					<h:outputLabel for="name" value="Name"></h:outputLabel> 
					<h:inputText id="name" value="#{registerEmployeeManagedBean.employee.name}"></h:inputText>
					<h:outputLabel for="job" value="Job"></h:outputLabel> 
					<h:inputText id="job" value="#{registerEmployeeManagedBean.employee.job}"></h:inputText>
					<h:outputLabel for="salary" value="Salary"></h:outputLabel> 
					<h:inputText id="salary" value="#{registerEmployeeManagedBean.employee.salary}" requiredMessage="Salary is mandatory">
						<f:validateRegex pattern="[0-9]*"></f:validateRegex>
						<f:validateLength minimum="2" maximum="4"></f:validateLength>
					</h:inputText>																								
			</h:panelGrid>
			<h:commandButton value="Register" actionListener="#{registerEmployeeManagedBean.registerListener}" style=""></h:commandButton>
		</h:form>
	</h:body>
</html>

以下是上面列出的代码的详细说明:

  • 注册页面已使用XHML格式。
    Facelets支持XHML作为其视图格式。

  • 用户需要填写所有必填信息字段,例如身份和薪水,以防万一您错过了它们,系统会标记一条错误消息。
    当我们使用<f:validateRegex 和必需的属性时,这些错误消息将由JSF框架本身显示。

  • 如果您在身份或者薪水字段中输入了非数字值,则会显示一条JSF错误消息,提示您输入的是无效值。

  • 所有字段均与各自的属性相关联,该属性与RegisterEmployeeManagedBean中已定义的雇员属性有关。
    因此,不会从请求对象中提取任何提交的值。

注册员工托管的Bean

正如JSF承诺的那样,所有内容都将保留在您的bean中,无需编排以前提取用户提交的参数时使用过的许多组件,因为不需要处理很多问题,以防万一您决定不这样做。
使用诸如消息,语言环境等的JSF框架。

package com.theitroad.beans;

import java.io.IOException;
import java.sql.SQLException;

import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;

import org.apache.log4j.Logger;

import com.theitroad.dao.EmployeeDAO;
import com.theitroad.data.Employee;

@ManagedBean
@SessionScoped
public class RegisterEmployeeManagedBean {
	private static Logger logger = Logger.getLogger(RegisterEmployeeManagedBean.class);
	private Employee employee = new Employee();

	public Employee getEmployee() {
		return employee;
	}

	public void setEmployee(Employee employee) {
		this.employee = employee;
	}

	public void registerListener(ActionEvent event){
		try {
			//Register Employee
			this.employee = EmployeeDAO.getInstance().createEmployee(employee);
			logger.debug("Employee Has Registered");
			FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO,
					"Employee has regisered successfully !",""));
			//Reset employee
			this.employee = new Employee();
		} catch (IllegalAccessException | ClassNotFoundException | SQLException | IOException e) {
			logger.debug("Registration Process Has Failed",e);
			FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR,
					"Unforunately!, employee hasn't registered cause to : "+e,""));
		}
	}
}

这里是对所发现的Bean代码的详细说明:

  • 一个普通的旧Java对象(POJO)雇员用于绑定目的。
    在JSF中,每个组件都可以与受管Bean提供的值相关联。
    提交表单后,便会启动JSF框架,并将提交的值神奇地传输到绑定的bean中。

  • 在JSF 2.0中,不需要人脸配置文件(faces-config.xml),因此,我们使用了新添加的注释来声明托管bean及其作用域。

  • 请记住,由于没有针对JSF 2.0提出的JSR建议,但是它已经实现,并且您可能会在许多受JSR-286支持的Portlet容器中使用它。

  • 您可能会注意到使用EmployeeDAO和ConnectionUtility,如果您还记得我们在使用JSP和Servlet开发Portlet中所讨论的内容,那么良好的设计将引导您获得易于维护的代码,易于调试和易于实现的代码。
    发展(即关注分离)。
    现在看,您可以重用您编写的代码。
    真的!这两个实用程序没有任何关系。

  • 与数据库通信相关的所有这些类均已隔离,以后可用于任何表示层。
    因此,您可以使用JSF,默认的JSP/Servlet,普通的Java Desktop应用程序,也可以将它们包装为基于SOAP的服务或者基于Restful的服务。

  • 无论是成功完成注册过程还是失败注册过程,用户都会收到一条确认消息。

  • 如果注册过程失败,则确认消息将向您显示该失败的主要原因。

JSF-Portlet桥依赖性

正如我们之前提到的那样,Portlet Bridge只是一个规范,因此许多供应商已经实现了它。
我们将使用为Portlet Container 2.0提供的MyFaces 3.0.0-alpha版本。
必须将以下列出的两个依赖项添加到您的Maven构建文件中,才能涉及和使用Portlet Bridge实施。

MyFaces-PortletBridge

<dependency>
	<groupId>org.apache.myfaces.portlet-bridge</groupId>
	<artifactId>portlet-bridge-api</artifactId>
	<version>3.0.0-alpha</version>
</dependency>
<dependency>
	<groupId>org.apache.myfaces.portlet-bridge</groupId>
	<artifactId>portlet-bridge-impl</artifactId>
	<version>3.0.0-alpha</version>
</dependency>

JSF实施和部署描述符

JSF是标准规范,这意味着不同的供应商已经提供了他们自己的实现。
但是,本教程的这一部分非常重要,因为我们将向您展示如何使用两种不同的实现来使用JSF实现,以及该用法对部署描述符文件的影响。

首先让我们选择最标准的JSF实施,可以肯定的是JSF Mojarra 2.0。
您必须将以下这些依赖项添加到您自己的Maven中,才能使用JSF Mojarra。

`JSF-参考实现-Mojaraa依赖关系'

<!-- Faces Implementation -->
<dependency>
	<groupId>com.sun.faces</groupId>
	<artifactId>jsf-impl</artifactId>
	<version>2.1.15</version>
</dependency>
<!-- Faces Library -->
<dependency>
	<groupId>com.sun.faces</groupId>
	<artifactId>jsf-api</artifactId>
	<version>2.1.15</version>
</dependency>

因此,您的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-name>Faces Servlet</servlet-name>
		<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
		<load-on-startup>1</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>Faces Servlet</servlet-name>
		<url-pattern>*.xhtml</url-pattern>
	</servlet-mapping>
	<context-param>
		<description>State saving method: 'client' or 'server' (=default). See
			JSF Specification 2.5.2</description>
		<param-name>javax.faces.STATE_SAVING_METHOD</param-name>
		<param-value>server</param-value>
	</context-param>
	<listener>
		<listener-class>com.sun.faces.config.ConfigureListener</listener-class>
	</listener>
</web-app>

现在让我们看看使用MyFaces 2.0而不是参考实现JSF Mojarra 2.0需要哪些依赖关系。

JSF-MyFaces依赖关系

<dependency>
	<groupId>org.apache.myfaces.core</groupId>
	<artifactId>myfaces-api</artifactId>
	<version>2.1.15</version>
</dependency>
<dependency>
	<groupId>org.apache.myfaces.core</groupId>
	<artifactId>myfaces-impl</artifactId>
	<version>2.1.15</version>
</dependency>

因此,您必须在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-name>Faces Servlet</servlet-name>
		<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
		<load-on-startup>1</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>Faces Servlet</servlet-name>
		<url-pattern>*.xhtml</url-pattern>
	</servlet-mapping>
	<context-param>
		<description>State saving method: 'client' or 'server' (=default). See
			JSF Specification 2.5.2</description>
		<param-name>javax.faces.STATE_SAVING_METHOD</param-name>
		<param-value>server</param-value>
	</context-param>
</web-app>

完整的Maven构建文件

在下面找到完整的maven构建文件,该文件将用于实现JSF/Mojarra Portlet。

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>EmployeeRegistration-JSFBridge</artifactId>
	<packaging>war</packaging>
	<version>0.0.1-SNAPSHOT</version>
	<name>EmployeeRegistration-JSFBridge</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>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.myfaces.portlet-bridge</groupId>
			<artifactId>portlet-bridge-api</artifactId>
			<version>3.0.0-alpha</version>
		</dependency>
		<dependency>
			<groupId>org.apache.myfaces.portlet-bridge</groupId>
			<artifactId>portlet-bridge-impl</artifactId>
			<version>3.0.0-alpha</version>
		</dependency>
		<!-- Faces Implementation -->
		<dependency>
			<groupId>com.sun.faces</groupId>
			<artifactId>jsf-impl</artifactId>
			<version>2.1.15</version>
		</dependency>
		<!-- Faces Library -->
		<dependency>
			<groupId>com.sun.faces</groupId>
			<artifactId>jsf-api</artifactId>
			<version>2.1.15</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>

员工DAO和ConnectionUtility

现在,是时候快速查看EmployeeDAO和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, Integer.parseInt(employee.getId()));
		query.setString(2, employee.getName());
		query.setString(3, employee.getJob());
		query.setInt(4, Integer.parseInt(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;
	}

}

注册员工演示

演示首先将开发的WAR部署到Apache Pluto Portlet容器2.0中。
登录到您的Apache Pluto,然后导航到先前介绍的教程中已实现的theitroad页面。