星期日, 8月 20, 2006

JBoss Seam備忘記

JBoss Seam 是一個功能強大的開源應用程式框架,
集成眾多流行的 SOA (Service Oriented Architecture) 技術,
包括 AJAX (Asynchronous Javascript and XML), JSF (Java Server Faces),
EJB3 (Enterprise Java Bean), Java Portlets, BPM (Business Process Management) 及工作流程.

Seam 目的是簡化眾多技術的 API 及架構,
利用 POJOs (Plain Old Java Objects) 簡單標記, widgets 及小量的 XML,
使開發員能夠輕鬆開發 Web 應用程式.

使用 Seam 1.0 技術,
日後將會更容易集成於 ESB (Enterprise Service Bus) 及 JBI (Java Business Integration).


開始備忘記:
[1] 安裝 jdk 5:
[2] 安裝 ANT
[3] 安裝 JBoss JEMS with ejb3-clustering
[4] 部署及測試 Booking hotel [JSF]
[5] 部署及測試 DVD store [JSF & jBPM]
[6] 部署及測試 Issues [issues workflow system]
[7] 測試及分析簡單註冊實例[Registration]

[1] 安裝 jdk 5:
下載 jdk-1_5_0_07-nb-5_0-win-ml.exe
http://java.sun.com/j2se/1.5.0/download-netbeans.html
安裝至 D:\jdk1.5.0_07
新增環境變數 JAVA_HOME=D:\jdk1.5.0_07
D:\jdk1.5.0_07\bin 加入至 PATH 中
D:\jdk1.5.0_07\lib\dt.jar 及 D:\jdk1.5.0_07\lib\tools.jar 加入至 CLASSPATH 中
執行 D:\>java -version
輸出 java version "1.5.0_07" 即安裝成功.

[2] 安裝 ANT:
下載 apache-ant-1.6.5-bin.zip
http://ant.apache.org/bindownload.cgi
http://download.nextag.com/apache/ant/binaries/apache-ant-1.6.5-bin.zip
解壓縮至 D:\ant
新增環境變數 ANT_HOME=D:\ant
D:\ant\bin 加入至 PATH 中
執行 D:\>ant -version
輸出 Apache Ant version 1.6.5 compiled on July 16 2004 即安裝成功.

[3] 安裝 JBoss JEMS with ejb3-clustering:
下載 jems-installer-1.2.0.BETA.jar 至 D: 目錄下.
http://sourceforge.net/project/showfiles.php?group_id=22866&package_id=193295
http://umn.dl.sourceforge.net/sourceforge/jboss/jems-installer-1.2.0.BETA.jar

進入 D: 目錄
執行 D:\>java -jar jems-installer-1.2.0.BETA.jar

安裝至 D:\jboss-4.0.4.GA
安裝時選擇 ejb3-clustered , 如下圖所示


新增環境變數 JBOSS_HOME=D:\jboss-4.0.4.GA
D:\jboss-4.0.4.GA\bin 加入至 PATH 中

啟動 jboss
D:\jboss-4.0.4.GA\bin\run.bat

下載 jboss-seam-1.0.1.GA.zip
http://labs.jboss.com/portal/jbossseam/download
http://superb-east.dl.sourceforge.net/sourceforge/jboss/jboss-seam-1.0.1.GA.zip

解壓縮至 D:\jboss-seam-1.0.1.GA

修改檔案 D:\jboss-seam-1.0.1.GA\build.properties 成
(由於不使用 tomcat, 這裡主要修改 jboss.home 的位置 )
######## build.properties ##########
tomcat.home = C:\\Tomcat-5.5
jboss.home = D:\\jboss-seam-1.0.1.GA
jbossHasMyFacesLifecycleBug = true
######## build.properties ##########


[4] 部署及測試 Booking hotel [JSF]:
進入目錄 D:\jboss-seam-1.0.1.GA\examples\booking
執行 D:\jboss-seam-1.0.1.GA\examples\booking>ant deploy
如下圖所示


Browser 進入 http://localhost:8080/seam-booking
如下圖所示


"Register New User"
輸入
Username: joeyta
Real Name: joeyta chan
Password: 123123
Verify Password: 123123
按 Register 就會註冊新用戶

然後可以使用 joeyta / 123123 登入.
登入後可以進行 search 及 booking hotel 等動作.
如下圖所示



[5] 部署及測試 DVD store [JSF & jBPM]:
進入目錄 D:\jboss-seam-1.0.1.GA\examples\dvdstore
執行 D:\jboss-seam-1.0.1.GA\examples\dvdstore>ant deploy

Browser 進入 http://localhost:8080/seam-dvd
如下圖所示


"Create Account"
輸入
User Name: joeyta
Password: 123123
Verify Password: 123123
按 Continue 並填寫個人資料, 就會註冊新用戶

然後可以使用 joeyta / 123123 登入.
登入後可以進行 DVD online shopping.
如下圖所示


workflow 流程圖如下所示



[6] 部署及測試 Issues [issues workflow system]:
進入目錄 D:\jboss-seam-1.0.1.GA\examples\issues
執行 D:\jboss-seam-1.0.1.GA\examples\issues>ant deploy

Browser 進入 http://localhost:8080/seam-issues
如下圖所示

註冊帳戶後可以對項目 issue 進行委派及回覆等工作.


[7] 測試及分析簡單註冊實例[Registration]:
進入目錄 D:\jboss-seam-1.0.1.GA\examples\registration
執行 D:\jboss-seam-1.0.1.GA\examples\registration>ant deploy

Browser 進入 http://localhost:8080/seam-registration/register.seam
如下圖所示

輸入
Username: joeyta
Real Name: joeyta chan
Password: 123123
然後按 "Register"

成功後出現如下所示


代碼結構如下圖所示


-- registration
+-- build
`-- resources
`-- META-INF
-- application.xml
-- ejb-jar.xml
-- jboss-app.xml
-- persistence.xml
`-- WEB-INF
-- components.xml
-- faces-config.xml
-- web.xml
`-- src
`-- org
`-- jboss
`-- seam
`-- example
`-- registration
-- Register.java
-- RegisterAction.java
-- User.java
`-- test
-- RegisterTest.java
-- testng.xml
+-- test-output
`-- view
-- index.html
-- register.jsp
-- registered.jsp

測試流程如下所示


/*-------------------------- User.java --------------------------------*/
package org.jboss.seam.example.registration;

import static org.jboss.seam.ScopeType.SESSION;

import java.io.Serializable;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

import org.hibernate.validator.Length;
import org.hibernate.validator.NotNull;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;

@Entity // 定義此為 entity bean
@Name("user") // JSF 根據此名稱取得此實例
@Scope(SESSION) // 定義此實例範圍在 session 階段
@Table(name="users") // 定義對應資料庫的 table 名為 users
public class User implements Serializable
{
private static final long serialVersionUID = 1881413500711441951L;

private String username;
private String password;
private String name;

public User(String name, String password, String username)
{
this.name = name;
this.password = password;
this.username = username;
}

public User() {}

@NotNull // 定義對應資料庫裡此 column constraint 為 not null
public String getName()
{
return name;
}

public void setName(String name)
{
this.name = name;
}

@NotNull @Length(min=5, max=15) // constraint 定義除 not null 外, 長度為 5-15
public String getPassword()
{
return password;
}

public void setPassword(String password)
{
this.password = password;
}

@Id @NotNull @Length(min=5, max=15) // 定義此為 primary key
public String getUsername()
{
return username;
}

public void setUsername(String username)
{
this.username = username;
}

public String toString()
{
return "User(" + username + ")";
}
}
/*-------------------------- User.java --------------------------------*/


/*-------------------- RegisterAction.java --------------------------*/
package org.jboss.seam.example.registration;

import java.util.List;

import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import org.hibernate.validator.Valid;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Logger;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.core.FacesMessages;
import org.jboss.seam.log.Log;

@Stateless // 定義此為 stateless session bean
@Name("register") // JSF 根據此名稱取得此實例
public class RegisterAction implements Register
{

@In @Valid // @In 表示由 Seam 注入此實例
private User user;

@PersistenceContext // 表示注入 EJB3 entity manager
private EntityManager em;

@Logger // 注入 component's Log instance.
private Log log;

// JSF view 將觸發此 action listener method, 並依靠其返回值判斷下一步動作
public String register()
{
List existing = em.createQuery("select username from User where username=:username")
.setParameter("username", user.getUsername())
.getResultList();
if (existing.size()==0)
{
em.persist(user);
log.info("Registered new user #{user.username}");
return "/registered.jsp"; // 此返回值將確定下一步動作
}
else
{
FacesMessages.instance().add("User #{user.username} already exists");
// FaceMessages component 收集成功及失敗資訊
return null; // 此返回值將確定下一步動作
}
}

}
/*-------------------- RegisterAction.java --------------------------*/

/*-------------------- Register.java --------------------------*/
package org.jboss.seam.example.registration;

import javax.ejb.Local;

@Local // 定義此為 stateless session bean 的 local interface
public interface Register
{
public String register();
}
/*-------------------- Register.java --------------------------*/

################# components.properties ##############
myFacesLifecycleBug false
embeddedEjb true
jndiPattern #{ejbName}/local
################# components.properties ##############
此 properties 定義對應的 components 值

<!-------------------- components.xml ------------------------>
<components>

<component name="org.jboss.seam.core.init">
<property name="myFacesLifecycleBug">@myFacesLifecycleBug@</property>
<property name="jndiPattern">@jndiPattern@</property>
</component>

<component class="org.jboss.seam.core.Ejb"
installed="@embeddedEjb@"/>

</components>
<!-------------------- components.xml ------------------------>
此檔案告訴 Seam 如何尋找 components 對應的 JNDI
@xxxxx@ 值將由 components.properties 裡 key 對應的值取代

<!-------------------- web.xml ------------------------>
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">


<!-- Seam -->

<listener>
<listener-class>org.jboss.seam.servlet.SeamListener</listener-class>
</listener>

<!-- MyFaces -->

<listener>
<listener-class>org.apache.myfaces.webapp.StartupServletContextListener</listener-class>
</listener>

<context-param>
<param-name>javax.faces.STATE_SAVING_METHOD</param-name>
<param-value>client</param-value>
</context-param>

<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>

<!-- Faces Servlet Mapping -->
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.seam</url-pattern>
</servlet-mapping>

</web-app>
<!-------------------- web.xml ------------------------>
此 deployment descriptor 定義 Seam 及 MyFaces 的設定.


<!-------------------- faces-config.xml ------------------------>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE faces-config
PUBLIC "-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.0//EN"
"http://java.sun.com/dtd/web-facesconfig_1_0.dtd">
<faces-config>

<!-- Phase listener needed for all Seam applications -->

<lifecycle>
<phase-listener>org.jboss.seam.jsf.SeamPhaseListener</phase-listener>
</lifecycle>

</faces-config>
<!-------------------- faces-config.xml ------------------------>
此檔為 JSF 的設定檔, 這裡不需要對 managed bean 及 流程作描述.
交由 Seam 管理.


<!-------------------- ejb-jar.xml ------------------------>
<ejb-jar>
<assembly-descriptor>
<interceptor-binding>
<ejb-name>*</ejb-name>
<interceptor-class>org.jboss.seam.ejb.SeamInterceptor</interceptor-class>
</interceptor-binding>
</assembly-descriptor>
</ejb-jar>
<!-------------------- ejb-jar.xml ------------------------>
此檔為 EJB3 deployment descriptor, 交由 Seam 管理


<!-------------------- persistence.xml ------------------------>
<persistence>
<persistence-unit name="userDatabase">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<jta-data-source>java:/DefaultDS</jta-data-source>
<properties>
<property name="hibernate.hbm2ddl.auto" value="create-drop"/>
</properties>
</persistence-unit>
</persistence>
<!-------------------- persistence.xml ------------------------>
此檔告訴 EJB persistence provider 如何尋找 datasource,
java:/DefaultDS 表示會尋找 jboss deployment 目錄下 -ds.xml 結尾資料庫設定檔對應的 jndi name.
這實作裡使用 JBoss 預設的 HSQLDB, 即 jboss deployment 目錄下的 hsqldb-ds.xml
<property name="hibernate.hbm2ddl.auto" value="create-drop" /> 定義:
當project deploy 時就會自動產生 database schema. 當project undeploy 時就會自動清除 database schema.

<%---------------------- register.jsp -------------------%>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<%@ taglib uri="http://jboss.com/products/seam/taglib" prefix="s" %>
<html>
<head>
<title>Register New User</title>
</head>
<body>
<f:view>
<h:form>
<table border="0">
<s:validateAll>
<tr>
<td>Username</td>
<td><h:inputText value="#{user.username}" required="true"/></td>
</tr>
<tr>
<td>Real Name</td>
<td><h:inputText value="#{user.name}" required="true"/></td>
</tr>
<tr>
<td>Password</td>
<td><h:inputSecret value="#{user.password}" required="true"/></td>
</tr>
</s:validateAll>
</table>
<h:messages/>
<h:commandButton type="submit" value="Register" action="#{register.register}"/>
</h:form>
</f:view>
</body>
</html>
<%---------------------- register.jsp -------------------%>
此為註冊 jsp page,
<s:validateAll> 表示將根據 entity bean 裡的 property constraints 判斷是否正確.
#{xxx.yyy} 為 JSF expression language 使用的標記,
當 page 開始 rendered 時, 會從 "xxx" managed backing bean 返回值對應的 "yyy" property,
當 submit rendered 時會設定此值到 "xxx" managed backing bean 的 "yyy" property 裡.
<h:commandButton type="submit" value="Register" action="#{register.register}"/> 表示:
當 submit 時, 會呼叫 register managed beans 裡 register method.


<%---------------------- registered.jsp -------------------%>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<html>
<head>
<title>Successfully Registered New User</title>
</head>
<body>
<f:view>
Welcome, <h:outputText value="#{user.name}"/>,
you are successfully registered as <h:outputText value="#{user.username}"/>.
</f:view>
</body>
</html>
<%---------------------- registered.jsp -------------------%>
此為註冊成功的確認 jsp page,
這裡只簡單輸出 managed bean 的 property.


<!-------------------- application.xml ------------------------>
<application>
<display-name>Seam Registration</display-name>

<module>
<web>
<web-uri>jboss-seam-registration.war</web-uri>
<context-root>/seam-registration</context-root>
</web>
</module>
<module>
<ejb>jboss-seam-registration.jar</ejb>
</module>
<module>
<java>jboss-seam.jar</java>
</module>

</application>
<!-------------------- application.xml ------------------------>
這個 deployment descriptor 連結 enterprise archive 模組
及 /seam-registration 與 web application 關聯在一起.


/*------------------ RegisterTest.java -------------------*/
package org.jboss.seam.example.registration.test;

import org.jboss.seam.Component;
import org.jboss.seam.example.registration.Register;
import org.jboss.seam.example.registration.User;
import org.jboss.seam.mock.SeamTest;
import org.testng.annotations.Test;
// 這個 TestNG 測試程式繼承 SeamTest, 可呼叫 Seam 裡的 component
public class RegisterTest extends SeamTest
{

@Test // 定義此為測試 method
public void testLogin() throws Exception
{

new Script() {

@Override // 這 annotation 表示必須 override superclass 裡的 method
protected void updateModelValues() throws Exception
{
User user = (User) Component.getInstance("user", true);
// 這裡由 Seam 的 component 裡取出 user 實例
assert user!=null;
user.setUsername("1ovthafew");
user.setPassword("secret");
user.setName("Gavin King");
}

@Override
protected void invokeApplication()
{
Register register = (Register) Component.getInstance("register", true);
String outcome = register.register();
assert "/registered.jsp".equals( outcome );
}

@Override
protected void renderResponse()
{
User user = (User) Component.getInstance("user", false);
assert user!=null;
assert user.getName().equals("Gavin King");
assert user.getUsername().equals("1ovthafew");
assert user.getPassword().equals("secret");
}

}.run();

}

}
/*------------------ RegisterTest.java -------------------*/
此為 TestNG 測試程式

<!-------------------- testng.xml ------------------------>
<!DOCTYPE suite SYSTEM "http://beust.com/testng/testng-1.0.dtd" >
<suite name="Registration" verbose="2" parallel="false">

<test name="Register">
<classes>
<class name="org.jboss.seam.example.registration.test.RegisterTest"/>
</classes>
</test>

</suite>
<!-------------------- testng.xml ------------------------>

此為 TestNG 設定檔,
這裡表示將測試 org.jboss.seam.example.registration.test.RegisterTest



有興趣的同志可以依照以下教學,繼續進行個別項目測試及分析:
http://docs.jboss.com/seam/1.0.0.GA/reference/en/html/tutorial.html

JSF 教學:
http://www.coreservlets.com/JSF-Tutorial/

TestNG 官方網:
http://testng.org/doc/

http://myfaces.apache.org/gettingstarted.html

http://www.jboss.com/products/seam
http://labs.jboss.com/portal/jbossseam/gettingstarted

沒有留言: