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页面。