============================================== Seam Page Flow
1.简单导航 在方法中直接返回URL public String run(){ return "/result.seam" } 2.JSF风格导航
典型的JSF程序中,在faces-config.xml中定义page flow。 如: <navigation-rule> <from-view-id>/myPage.jsp</from-view-id> <navigation-case> <from-outcome>success</from-outcome> <to-view-id>/success.jsp</to-view-id> </navigation-case> <navigation-case> <from-outcome>fail</from-outcome> <to-view-id>/failure.jsp</to-view-id> </navigation-case> </navigation-rule> <!—This navigaion rule defines global page flow --> <navigation-rule> <navigation-case> <from-outcome>logout</from-outcome> <to-view-id>/logout.jsp</to-view-id> </navigation-case> </navigation-rule>
Seam使用类似的机制,但是是在WEB-INF/的pages.xml中定义。 优点:可以传递参数,可以使用EL表达式。
可以拆分为多个xxx.page.xml,和JSP文件放在一起?
<?xml version="1.0" encoding="UTF-8"?> <pages xmlns="http://jboss.com/products/seam/pages" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://jboss.com/products/seam/pages http://jboss.com/products/seam/pages-2.1.xsd"> <page view-id="/vacations.jsp"> <navigation from-action= "#{vacationManagerAction.selectVacationType}"> <rule if-outcome="city"> <redirect view-id="/city.jsp" /> </rule> </navigation> </page> </pages>
Page可以是范围的指定 <page view-id="/*"> <page view-id="/secure/*">
可以使用表达式 <rule if="#{destination.minimumBudget lt 100.0}"> <redirect view-id="/insufficientfunds.jsp" /> </rule>
还可以捕捉异常 <exception class="com.davidsalter.HolidayException"> <redirect view-id="/error.jsp"> <message severity="ERROR"> Whoops. Better make sure we write some better tests ! </message> </redirect> </exception>
但需要web.xml中有这一段才好使。 <filter> <filter-name>Seam Filter</filter-name> <filter-class> org.jboss.seam.servlet.SeamFilter </filter-class> </filter> <filter-mapping> <filter-name>Seam Filter</filter-name> <url-pattern>*.seam</url-pattern> </filter-mapping>
渲染页面之前,执行action <page view-id="/vacations.jsp" action="#{vacationManagerAction.beforeRender}"> </page>
可以执行多个 <page view-id="/vacations.jsp"> <action execute="#{vacationManagerAction.beforeRender}" /> <action execute="#{vacationManagerAction.doMore}" /> </page>
3.Seam jPDL导航 JBPM Process Defnition Language (jPDL) ================================================================================ Facelets 在JSF生存周期里负责管理Restore View和Render Response阶段
优点:模板,性能,EL function,XHTML,无脚本 (1)模板 <ui:composition/> 用在模板中,进行定义 <ui:insert/> 用在模板client中,即使用模板的地方 <ui:define/> 用在模板中,进行定义
<ui:composition xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html" template="../layout/template.xhtml"> <ui:define name="body"> … HTML Content Defined Here </ui:define> <ui:define name="footer"> … HTML Content Defined Here </ui:define> </ui:composition>
<body> <div class="body"> <ui:insert name="body"/> </div> <div class="footer"> <ui:insert name="footer"/> </div> </body>
传递参数: <ui:include src="footer.xhtml" > <ui:param name="vacation" value="#{vacation}"> <ui:param name="vacationPlanner" value="David"> </ui:include>
(3)EL function 在JSF页面中可以使用 JSF Expression Language (EL), 在FaceLets中,还可以使用EL function。 <h:inputText id="#{someText}" value="#{trim[someText]}"> </h:inputText> 使用了trim方法()
facelets支持的EL称为Unifed Expression Language。 http://java.sun.com/products/jsp/reference/techart/unifiedEL.html
(4)XHTML facelets的页面是用xhtml形式的。 可以使用namespaces。 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core"> … </html> facelets默认会处理xhtml中的注释,并在页面源码中显示出来。 可以skip注释。 web.xml中 <context-param> <param-name>facelets.SKIP_COMMENTS</param-name> <param-value>true</param-value> </context-param>
(5)无脚本 JSP中的这种写法在facelets中是不可以的,这样保证了view和业务逻辑的分离。 <% if validate() { %> <b> Valid </b> <% } else { %> <b> Invalid</b> <% } %>
配置方法: facelets网站 https://facelets.dev.java.net
jsf-facelets.jar放在工程的classpath中。
web.xml中追加 <context-param> <param-name>javax.faces.DEFAULT_SUFFIX</param-name> <param-value>.xhtml</param-value> </context-param>
faces-config.xml中: <application> <view-handler> com.sun.facelets.FaceletViewHandler </view-handler> </application> ============================================== Seam Gen: Seam提供的一个命令行的工具,可以生成Seam工程,for eclipse或netbean <SEAM_HOME>/seam.bat
setup Runs the setup wizard, which asks the user about the project that they are working on, such as which database drivers to use, the package name for classes, and so on. The answers are stored within the <SEAM_HOME>/seam-gen/build.properties fle. The details specifed within this fle defne the current project.
create-project Creates a Seam project using the defnitions stored within the <SEAM_HOME>/seam-gen/build.properties fle.
update-project Updates the current project to use the latest Seam JAR fles.
delete-project Deletes the current project.
archive Builds the current project as either a WAR or a EAR fle, depending upon the project confguration options.
deploy Deploys the current project to JBoss.
undeploy Undeploys the current project from JBoss.
explode Deploys the current project to JBoss as an exploded archive.
restart Restarts the currently-exploded project.
unexploded Undeploys the currently-exploded project.
new-action Creates a new Seam action class.
new-form Creates a new Seam action form and action class.
new-conversation Creates a new Seam conversation.
new-entity Creates a new Seam entity class.
generate-model Reverse engineers the current project's database, and genera JPA entity classes from it.
generate-ui Creates entity management pages (create, update, delete) fo existing Seam entity classes.
generate Creates entity management pages and JPA entity classes by reverse engineering the current project's database. ========================================= Seam Debug seam-debug.jar需要在classpath里 http://xxx/xx/debug.seam ========================================= TestNG Seam内置了TestNG http://www.testng.org
例: package mytests; import org.testng.annotations.* public class ATest { @Test (groups= {"examples"}) public void runATest() { assert getString().equals("some value"); } }
groups这个测试属于哪个组,可选的。
如果方法执行完毕,没有异常,就表示成功。 注意使用assert。 http://java.sun.com/j2se/1.4.2/docs/guide/lang/assert.html
支持前处理,后处理 @AfterSuite / @BeforeSuite @AfterGroups / @BeforeGroups @AfterTest / @BeforeTest @AfterClass / @BeforeClass @AfterMethod / @BeforeMethod 如 @BeforeClass public void setUp() { // Setup initialization data. this.intVal=17; } 将在这个类的第一个Test方法之前执行
Test控制文件,一组相关的测试 xxxTest.xml,一般放在default package或单独的package里 如: <!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" > <suite name="MySuite" verbose="2" parallel="false"> <test name="MyTest"> <classes> <class name="myPackage.MyTest" /> </classes> </test> </suite> 测试类太多的话,可以指定package <suite name="MySuite" verbose="2" parallel="false"> <test name="MyTest"> <packages> <package name="myTestPackage" /> </packages> </test> </suite>
执行的话,使用 ant test命令。 ============================================================ 结合测试 SeamGen生成的工程有个 bootstrap 目录,里面有内嵌的JBoss服务器。 用Seam做结合测试的时候,是部署到这里。
从 org.jboss.seam.mock.SeamTest类 得到一个匿名类,它继承了 org.jboss.seam.mock.FacesRequest。 可以模仿JSF的不同生命周期。 ------------------------------------------- protected void applyRequestValues() This method is called during the apply request value phase of the JSF life cycle.
protected void invokeApplication() This method is called during the invoke application phase of the JSF life cycle.
protected void processValidations() This method is called during the process validations phase of the JSF life cycle.
protected void renderResponse() This method is called during the render response phase of the JSF life cycle. ------------------------------------------- import org.jboss.seam.mock.SeamTest; import org.testng.annotations.Test; public class MyTest extends SeamTest { @Test(groups={"ui"}) public void testMe() throws Exception { new FacesRequest() { @Override protected void processValidations() throws Exception { // Perform Tests } @Override protected void updateModelValues() throws Exception { // Perform Tests } @Override protected void invokeApplication() throws Exception { // Perform Tests } @Override protected void renderResponse() throws Exception { // Perform Tests } }.run(); } 在Override的方法中可以get,set,validate Seam组件的属性,调用Seam组件的方法。 ----------------------------------------------------- protected Object getValue(…) Gets a value from a JSF expression such as #{destination.minimumBudget}.
protected void setValue(…) Sets the value of a JSF expression.
protected Boolean validateValue(…) Validates an expression based upon the JSF validations present.
protected Object invokeMethod(…) Invokes a JSF method binding such as {vacationManagerAction.selectVacationType}. ---------------------------------------------------- 例子: package com.davidsalter.vacationplanner.actions.test; import org.jboss.seam.mock.SeamTest; import org.testng.annotations.Test; import com.davidsalter.vacationplanner.model.Destination; public class VacationTest extends SeamTest { @Test(groups={"ui"}) public void testCityVacation() throws Exception { new FacesRequest() { @Override protected void processValidations() throws Exception { validateValue("#{destination.destinationType}", Destination.DestinationType.CITY); validateValue("#{destination.minimumBudget}", 200.0); assert !isValidationFailure(); } @Override protected void updateModelValues() throws Exception { setValue("#{destination.destinationType}", Destination.DestinationType.CITY); setValue("#{destination.minimumBudget}", 200.0); } @Override protected void invokeApplication() { assert invokeMethod("#{vacationManagerAction.selectVacationType}").equals("/city.xhtml"); } @Override protected void renderResponse() { assert getValue("#{destination.minimumBudget}"). toString().equals("200.0"); } }.run(); } ============================================================ 测试Seam组件 只想测试组件,而不想有JSF的生命周期。 从org.jboss.seam.mock.SeamTest继承我们的测试类, 创建匿名org.jboss.seam.mock.ComponentTest类的实例。 声明testComponents()方法。 ------------------------ import org.jboss.seam.mock.SeamTest; import org.testng.annotations.Test; public class MyTest extends SeamTest { @Test(groups={"component"}) public void testMe() throws Exception { new ComponentTest() { @Override protected void testComponents() throws Exception { // Perform tests. } }.run(); ------------------------ 例: package com.davidsalter.vacationplanner.actions.test; import org.jboss.seam.mock.SeamTest; import org.testng.annotations.Test; import com.davidsalter.vacationplanner.model.Destination; public class VacationManagerActionTest extends SeamTest { @Test public void testSelectVacationType() throws Exception { new ComponentTest() { @Override protected void testComponents() throws Exception { setValue("#{destination.destinationType}", Destination.DestinationType.ACTION); assert invokeMethod("#{vacationManagerAction. selectVacationType}").equals("danger"); } }.run(); } } ------------------------ Mock Seam组件 在测试中,有时很难真正模仿Seam组件的功能,比如操作用户数据库。 可以用Mock对象来实现, 用组件优先级的方式。
定义Seam组件时,默认是APPLICATION, 内置的优先级,从低到高 -------------------- Built in (Install.BUILT_IN) Framework (Install.FRAMEWORK) Application (Install.APPLICATION) Deployment (Install.DEPLOYMENT) Mock (Install.MOCK) -------------------- Seam只会实例化优先级最高的那个。 @Install(precedence=Install.MOCK)
例: --------------------------- package com.davidsalter.vacationplanner.actions; import org.jboss.seam.annotations.Install; import org.jboss.seam.annotations.Name; @Name("vacationManagerAction") @Install(precedence=Install.MOCK) public class MockVacationManagerAction { public String selectVacationType() throws Exception { return "danger"; } } --------------------------- ================================================ RiceFaces 基于JSF框架的UI组件,有Ajax支持。 http://www.jboss.org/jbossrichfaces/downloads/
添加Jar文件到WEB-INF/lib目录下。 如 richfaces-api-3.2.1.jar richfaces-impl-3.2.1.jar richfaces-ui-3.2.1.jar
web.xml追加 <context-param> <param-name>org.richfaces.SKIN</param-name> <param-value>blueSky</param-value> </context-param> 几种内置的皮肤,区分大小写,在 richfaces-impl.jar的META-INF/skin目录下。 -------------------- DEFAULT Plain emeraldTow blueSky wine japanCherr ruby classic deepMarine --------------------
xhtml中使用追加namespace <ui:composition xmlns="http://www.w3.org/1999/xhtml" xmlns:s="http://jboss.com/products/seam/taglib" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html" xmlns:rich="http://richfaces.org/rich" template="layout/template.xhtml"> ======================================================== 数据持久化
Java Persistence API (JPA)是一个标准,有不同的实现,如Hibernate和TopLink。 Seam默认使用Hibernage,但也可以用TopLink或其他的。
Data Source文件位于 <Jboss_home>/server/default/deploy *-ds.xml,一个文件里可以定义多个。 ---------------- <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE datasources PUBLIC "-//JBoss//DTD JBOSS JCA Config 1.5//EN" "http://www.jboss.org/j2ee/dtd/jboss-ds_1_5.dtd"> <datasources> <local-tx-datasource> <jndi-name>DataDatasource</jndi-name> <connection-url>jdbc:hsqldb:.</connection-url> <driver-class>org.hsqldb.jdbcDriver</driver-class> <user-name>sa</user-name> <password></password> </local-tx-datasource> </datasources> -------------------------
persistence.xml 例: ------------------------- ?xml version="1.0" encoding="UTF-8"?> <!-- Persistence deployment descriptor for dev profile --> <persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0"> <persistence-unit name="Data"> <provider>org.hibernate.ejb.HibernatePersistence</provider> <jta-data-source>java:/DataDatasource</jta-data-source> <properties> <property name="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect"/> <property name="hibernate.hbm2ddl.auto" value="create-drop"/> <property name="hibernate.show_sql" value="true"/> <property name="hibernate.format_sql" value="true"/> <property name="jboss.entity.manager.factory.jndi.name" value="java:/DataEntityManagerFactory"/> </properties> </persistence-unit> </persistence> -------------------------
@Entity 类不能是final的 有get,set方法。 不能有private的无参数构造方法。 --------------------- @Entity @Name("customer") @Table(name="CUSTOMER_TABLE") public class Customer { // .. Omitted for brevity @Id @GeneratedValue @Column(name="CUSTOMER_ID") public Long getId() { return id; } ------------------------
@Stateful @Name("customerManager") public class CustomerManagerAction implements CustomerManager { @PersistenceContext private EntityManager em;
public void saveCustomer (Customer customer) { em.persist(customer); } @Destroy @Remove public void destroy() { } } ----------------------------
private List<Customer>customers; public void getCustomers() { customers = em.createQuery("select customer from Customer customer order by customer.secondName").getResultList(); } ---------------------------- 注解为DataModel,自动Out为JSF的DataModel,可以用在页面的List中
package com.davidsalter.data.actions; import com.davidsalter.data.entity.Customer; import javax.ejb.Remove; import javax.ejb.Stateful; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import org.jboss.seam.annotations.Destroy; import org.jboss.seam.annotations.Factory; import org.jboss.seam.annotations.Name; import org.jboss.seam.annotations.datamodel.DataModel; import org.jboss.seam.annotations.datamodel.DataModelSelection; @Stateful @Name("customerManager") public class CustomerManagerAction implements CustomerManager { @PersistenceContext private EntityManager em;
@DataModel private List<Customer> customers;
@DataModelSelection("customers") private Customer selectedCustomer;
@Factory public void getCustomers() { customers = em.createQuery( "select customer from Customer customer order by customer.secondName").getResultList(); } @Destroy @Remove public void destroy() { } } -------------------------------- Entity之间的关系映射
One-to-one relationships One-to-many relationships Many-to-one relationships Many-to-many relationships
Entity的CURD ---------------------- @PersistenceContext EntityManager em … em.remove(customer); ------------------------- @PersistenceContext EntityManager em … Customer customer = em.find(Customer.class, customerId); customer.setSecondName(secondName); -------------------------
Seam提供Home和Query对象,可以在Facelets中直接执行CURD操作。
==================================================== Seam Conversations
通常的Scope: Servlet中局部变量 Session Application,Servlet中全局Static变量 --------
Seam中的7中Scope: ----- Stateless,无状态,用完就扔。 Event,只在单个Request中有效。 Page,在Render page过程中有效,所有用来render page的方法中有效。AJAX postbacks 也是Page Scope的,从页面开始渲染,到下一个页面被渲染之前。 Conversation,组件的默认Scope,默认周期是从request开始到页面渲染完毕。 Session 即HTTP Session Business Process,jBPM定义的business process. Application,组件成为servlet的全局变量 ------- 例: ------------ import org.jboss.seam.annotations.Scope; import static org.jboss.seam.ScopeType.CONVERSATION; @Name("vacation") @Scope(CONVERSATION) public class Vacation { } ------------
Conversation分为临时conversation和long-running conversation. 一个时刻只能有一个long-running的!! long-running需要手工起动,不会自动起动。 -------------- @Stateful @Name("hitCount") @Scope(ScopeType.CONVERSATION) public class HitCountManager implements HitCount { @Begin public String start() { } } ---------------- 第一次被调用时,首先创建的是临时的conversation,遇到@Begin后,将临时的提升为long-running的。 如果用户浏览页面,调用到另外一个有 @Begin(join=true)的方法, 将使用当前的long-running conversation。 如果另外一个是@Begin,那么将报错,因为已经有一个long-running的了。
当前运行的conversation,可以通过内置的conversation组件查看。 如: ID:<h:outputText value="#{conversation.id}"/> Long running?:<h:outputText value="#{conversation.longRunning}"/> Nested?:<h:outputText value="#{conversation.nested}"/>
页面被访问时,自动Begin long-running Conversation. <page view-id="/conversation.xhtml"> <begin-conversation join="true" /> </page>
@End 如果是nested的话,从Stack中取出上一层conversation,并使用 如果不是nested的,结束当前convsation. --------------- @Stateful @Name("hitCount") @Scope(ScopeType.CONVERSATION) public class HitCountManager implements HitCount { @Begin(join=true) public String start() { return"/conversation.xhtml"; } @End public String end() { return"/home.xhtml"; } } ----------------- 页面被调用时,自动结束conversation. <page view-id="/autoEnd.xhtml"> <end-conversation /> </page>
比如出异常时: <exception class="javax.persistence.OptimisticLockException"> <end-conversation/> <redirect view-id="/error.xhtml"> <message>Another user changed the same data, please try again</message> </redirect> </exception> --------------------------------- Propagating conversations,传播conversation
下面这两句不会传播conversation <h:outputLink value="/newpage.xhtml" /> <h:commandLink value="Click Me" action="#{form.clicked}"> 可以用<s:link>代替,它会自动传播conversation,<s:button>也是。 <s:link view="/conversationPage.xhtml" value="Next Page"/>
可以用propagation属性,决定怎么传播。 ---- begin – A new conversation is begun upon clicking the link join – An existing conversation is joined upon clicking the link none – The link acts as a standard <a /> tag, ignoring any conversational status end – The current long-running conversation is terminated ------
components.xml中可以定义传递时使用的名字 <core:manager conversation-id-parameter="cid"/> xxx.seam?cid=2
超时时间,毫秒 <core:manager conversation-timeout="60000"/>
conversation超时时,组件中有@Destroy和@Remove的方法自动被执行。 @Destroy @Remove public void destroy() { // Perform any clean up. }
在conversation内操作时,Seam会同步访问,避免线程的问题。 有时,如Ajax请求,可能会同时处理多个请求。 并发的request的超时时间: <core:manager concurrent-request-timeout="500" /> 默认500毫秒,一般足够了。太大的话,可能有性能问题。
Standard Seam conversation ID http://localhost:8080.conversation/findVacation?cid=10 Natural conversation Seam ID http://localhost:8080/conversation/findVacation?vacationId=10 Natural Seam conversation ID after URL rewriting http://localhost:8080/conversation/Vacation/10
pages.xml中 <conversation name="findVacation" parameter-name="vacationId" parameter-value="#{vacationManager.vacation.vacationid}"/>
<page view-id="/find.xhtml" conversation="findVacation"> <begin-conversation join="true" /> </page> ===================================================== Seam Remoting
WEB-INF/web.xml -------------------------- <servlet> <servlet-name>Seam Resource Servlet</servlet-name> <servlet-class> org.jboss.seam.servlet.SeamResourceServlet </servlet-class> </servlet> <servlet-mapping> <servlet-name>Seam Resource Servlet</servlet-name> <url-pattern>/seam/resource/*</url-pattern> </servlet-mapping> -------------------------- 使用@WebRemote
@Name("helloWorld") public class HelloWorld implements HelloWorldAction { @WebRemote public String sayHello() { return "Hello world !!"; } -------------------------- Session Bean必须定义在接口上,而不是实现上。
import javax.ejb.Local; import org.jboss.seam.annotations.remoting.WebRemote; @Local public interface HelloWorldAction { @WebRemote public String sayHello(); @WebRemote public String sayHelloWithArgs(String name); }
import javax.ejb.Stateless; import org.jboss.seam.annotations.Name; @Stateless @Name("helloWorld") public class HelloWorld implements HelloWorldAction { public String sayHello() { return "Hello world !!"; } public String sayHelloWithArgs(String name) { return "Hello "+name; } } ------------------------------ <script type="text/javascript" src="/HelloWorld/seam/resource/ remoting/resource/remote.js"> </script> <script type="text/javascript" src="/HelloWorld/seam/resource/ remoting/interface.js?helloWorld">
调用多个组件的话 <script type="text/javascript" src="/HelloWorld/seam/resource/ remoting/interface.js?helloWorld&mySecondComponent& myThirdComponent">
这样更简单: <s:remote include="helloWorld"/> 自动生成对应的javascript
多个组件的: <s:remote include="helloWorld, mySecondComponent, myThirdComponent" />
调用: <script type="text/javascript"> function sayHello() { var callback = function(result) { document.getElementById("helloResult").innerHTML=result; }; Seam.Component.getInstance("helloWorld").sayHello(callback); } </script>
使用conversation: Seam.Remoting.getContext().setConversationId(# {conversation.id});
Remoting的Debug: WEB-INF/components.xml -------------------- <component name="org.jboss.seam.remoting.remoting"> <property name="debug">true</property> </component>
或 <remoting:remoting debug="true" /> -------------------- 会弹出一个popup窗口,显示提交和返回的信息,
写log Seam.Remoting.log("This is a debug message"); Seam.Remoting.log("Value:"+name);
改变please wait信息: function sayHello() { Seam.Remoting.log("setting the loading message"); Seam.Remoting.loadingMessage = "Hold on a moment..."; // .. rest of code removed for brevity. } ======================================= AJAX4JSF现在是RiceFaces的一部分了。
对于Ajax请求,Seam会排队存取conversation。 如果在指定的时间内没有等到使用conversation,将创建一个临时conversation. 这个等待的时间长度在 components.xml 中定义 <core:manager concurrent-request-timeout="500" /> 500毫秒 ======================================== Security 安全
用户验证: components.xml中指定验证方法。 <security:identity authenticate-method="#{authenticator.authenticate}" remember-me="true"/> 这个方法返回true,表示验证成功。
在user的@Entity中可以使用一些Seam的注解,这些注解是Seam identity manager API使用的。 ----------------------------------------- @Name The name of the Seam component that we are creating. This annotation is used in the same fashion as discussed earlier in this book.
@UserPassword(hash="none") This feld is used to store the user's password using the hashing algorithm specifed by the hash attribute. Seam supports both MD5 and SHA1 hashing algorithms, as well as no password hashing for unencrypted passwords. @UserPassword(hash="none") @UserPassword(hash="md5") @UserPassword(hash="sha")
@UserPrincipal This feld is used to store the user's username.
@UserEnabled This feld stores a Boolean fag indicating whether the user account is enabled (TRUE) or not (FALSE).
@UserFirstName This feld stores the user's frst name.
@UserLastName This feld stores the user's Surname. ----------------------------------------- <page vide-id="/userList.xhtml" login-required="true" /> <page view-id="/secure/*" login-required="true" />
<pages xmlns= "http://jboss.com/products/seam/pages" xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation= "http://jboss.com/products/seam/pages http://jboss.com/products/seam/pages-2.1.xsd" login-view-id="/login.xhtml"> ------------------------------------------ <h:form id="login"> <rich:panel> <f:facet name="header">Login</f:facet> <h:panelGrid columns="2"> <h:outputLabel for="username">Username</h:outputLabel> <h:inputText id="username" value="#{credentials.username}"/> <h:outputLabel for="password">Password</h:outputLabel> <h:inputSecret id="password" value="#{credentials.password}"/> <h:outputLabel for="rememberMe">Remember me</h:outputLabel> <h:selectBooleanCheckbox id="rememberMe" value="#{identity.rememberMe}"/> </h:panelGrid> </rich:panel> <h:commandButton value="Login" action="#{identity.login}"/> </h:form> --------------------------------------- 登录成功后,跳转到用户访问的页面。 componets.xml追加即可。 <event type="org.jboss.seam.security.notLoggedIn"> <action execute="#{redirect.captureCurrentView}"/> </event> <event type="org.jboss.seam.security.loginSuccessful"> <action execute="#{redirect.returnToCapturedView}"/> </event> --------------------------------------- 用户登录成功,失败等会触发事件,可以进行相应的处理,如写LOG @Observer("event name") public void handleEvent() { } ------------- Org.jboss.seam.security.loginSuccessful The user has successfully logged in to the application Org.jboss.seam.security.loginFailed The user has failed to log in to the application Org.jboss.seam.security.alreadyLoggedIn The user has attempted to log in to the application when they are already logged in Org.jboss.seam.security.notLoggedIn The user has attempted to access a secured resource and is not logged in Org.jboss.seam.security.notAuthorized The user is logged in and has attempted to access a secured resource but does not have the appropriate role Org.jboss.seam.security.loggedOut The user has logged out of the application ---------------- @Observer("org.jboss.seam.security.loginSuccessful") public void onSuccessFulLogin() { LoginAudit audit = new LoginAudit(); audit.setStatus("Success"); audit.setUserName(credentials.getUsername()); entityManager.persist(audit); } @Observer("org.jboss.seam.security.loginFailed") public void onFailedLogin() { LoginAudit audit = new LoginAudit(); audit.setStatus("Fail"); audit.setUserName(credentials.getUsername()); entityManager.persist(audit); } ---------------------- 登录时,要用户回答问题: 首先,需要配置(org.jboss.seam.servlet.SeamResourceServlet),web.xml?参照前面。 问题图片: <h:graphicImage value="/seam/resource/captcha"/> 用户回答: <h:inputText id="captchaInput" value="#{captcha.response}" required="true"> <s:validate /> </h:inputText> 问题message: <h:message for="captchaInput"/>
问题可以自定义: package com.davidsalter.seamsecurity; import java.util.Random; import org.jboss.seam.annotations.Create; import org.jboss.seam.annotations.Name; import org.jboss.seam.captcha.Captcha; import org.jboss.seam.captcha.CaptchaResponse; @Name("org.jboss.seam.captcha.captcha") public class SubtractCaptcha extends Captcha { @Override @Create public void init() { Random random = new Random(1234567890); int firstNum = random.nextInt(100); int secondNum = random.nextInt(100); this.setChallenge(firstNum+"-"+secondNum); this.setCorrectResponse(""+(firstNum-secondNum)); } @Override @CaptchaResponse(message="Math isn't your strong point is it?") public String getResponse() { return super.getResponse(); } } =========================== The Seam identity manager API 实现标准的验证接口
components.xml中指定用户和角色。 <security:jpa-identity-store user-class="com.davidsalter.seamsecurity.entity.User" role-class="com.davidsalter.seamsecurity.entity.Role" /> 创建用户和角色 IdentityManager.createUser(String username, String password); IdentityManager.grantRole(String username, String rolename);
............. ============================= OpenID 多个web application都可以通过OpenID来验证,一次登录多个application.
4步: (1)Confgure an OpenID phase listener (2)Ensure that the correct JAR fles are on the application's classpath (3)Write a logon form (4)Confgure page redirection after an OpenID logon
(1)faces-config.xml <lifecycle> <phase-listener> org.jboss.seam.security.openid.OpenIdPhaseListener </phase-listener> </lifecycle>
(2) htmlparser.jar openid4java.jar openxri-client.jar openxri-syntax.jar 可能需要commons-codec.jar,它在Jboss5的common/lib下。 (3)登录页面,只需要openID,不需要密码。 ID是类似于URL形式的: http://MySeamID.myopenid.com
<h:outputLabel for="username">Open ID</h:outputLabel> <h:inputText id="openid" value="#{openid.id}"/> <h:commandButton value="Login using OpenID" action="#{openid.login}"/>
(4)OpenID登录成功后,自动转到我们webapplication的openid.xhtml上。 这是个虚拟的页面,不必存在。 <page view-id="/openid.xhtml"> <navigation evaluate="#{openid.loginImmediately()}"> <rule if-outcome="true"> <redirect view-id="/secure/securePage.xhtml"> </redirect> </rule> <rule if-outcome="false"> <redirect view-id="/home.xhtml"> </redirect> </rule> </navigation> </page> 用户的openID可以这样显示:#{openid.validatedId}
|