Apache Pluto,Portlet Bridge和JSF 2.0集成示例教程
在之前的使用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页面。

