`
alienj
  • 浏览: 77418 次
  • 性别: Icon_minigender_1
  • 来自: 重庆
社区版块
存档分类
最新评论

第29章. 配置Seam和打包Seam应用程序

阅读更多

29章. 配置Seam和打包Seam应用程序

 

配置是一个非常无聊话题和一个极其乏味的消遣。遗憾的是,需要几行XML整合Seam到你的JSF实现和servlet容器。你没有必要为下面部分耽搁时间;你根本不需要自己去输入任何东西,因为你可以只使用seam-gen启动你的应用程序或者你可以从例子应用程序拷贝粘贴完成!

 

29.1. 基本Seam配置

 

首先,让我们看一看只要Seam和JSF一起使用就必需的基本配置。

 

29.1.1. SeamJSF和你的servlet容器集成

 

当然,你需要一个faces servlet!

 
<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>*.seam</url-pattern>
 
</servlet-mapping>
 

(你可以调整URL模式来适合你的口味。)

另外, Seam需要在你的web.xml文件中有下面的条目:

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

这个侦听器负责启动Seam,销毁会话和应用程序上下文。

 

 

某些JSF实现的服务器边状态保存实现较差,干扰了Seam的对话的传播。如果你在表单提交期间使用对话传播有问题,试试客户边状态保存。在web.xml中你将需要这些:

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

JSF规范中关于视图状态值的易变性是一个小的灰色地带。 因为Seam使用JSF视图状态返回它的PAGE作用域,在某些情况下这可以成为一个问题 。如果你正在就JSF-RI使用服务器边状态保存 ,并希望一个PAGE作用域bean为一个页面的一个特定的视图保持它的精确值,你将需要指定下面的context-param 。否则如果一个用户使用“back”按钮,如果值已改变,PAGE作用域组件将是最新的值,而不是“back”返回页面的值。(见Spec Issue)。默认时,这个设置没有激活,因为用每请求序列化JSF视图的高性能击中(because of the performance hit of serializing the JSF view with every request.)。

 
<context-param>
 
    <param-name>com.sun.faces.serializeServerState</param-name>
 
    <param-value>true</param-value>
 
</context-param>
 

29.1.2. 使用Facelets

 

如果你希望根据我们的建议,使用Facelets代替JSP,增加下面的行到faces-config.xml

 
<application>
 
    <view-handler>com.sun.facelets.FaceletViewHandler</view-handler>
 
</application>
 

增加下面的行到web.xml:

 
<context-param>
 
    <param-name>javax.faces.DEFAULT_SUFFIX</param-name>
 
    <param-value>.xhtml</param-value>
 
</context-param>
 

如果你在JBoss AS中使用facelets你会发现Facelets日志坏了 (日志消息没有成为服务器日志)Seam提供了一个桥接修补了这个问题。为了使用这个桥接,拷贝 lib/interop/jboss-seam-jul.jar$JBOSS_HOME/server/default/deploy/jboss-web.deployer/jsf-libs/ ,并且在你的应用程序的WEB-INF/lib中包含jboss-seam-ui.jar。 Facelets日志类别被逐条列在Facelets Developer Documentation.

 

29.1.3. Seam Resource Servlet

 

Seam Resource Servlet提供Seam Remoting、captchas(见安全一章)和某些JSF UI控件使用的资源。配置Seam Resource Servlet在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>
 
 

29.1.4. Seam servlet过滤器

 

对基本操作而言,Seam不需要任何servlet过滤器。 然而,有几个依赖过滤器使用的功能。为了让你方便,Seam 让你增加并配置servlet过滤器,就象配置其它内建Seam组件一样。为了利用此功能,我们必须首先在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>/*</url-pattern>
 
</filter-mapping>
 

Seam主过滤器必须在web.xml中被指定为第一个过滤器。这样确保它首先运行。

 

Seam过滤器共享几个公共属性, 你可以在components.xml中设置它们,除下面讨论的所有参数之外:

 

·         url-pattern — 用来指定被过滤的请求,默认是对所有的请求。url-pattern是Tomcat样式模式,允许拥有通配符后缀。

·         regex-url-pattern —用来指定被过滤的请求,默认是对所有的请求。regex-url-pattern 是一个与请求路径匹配的真正的正则表达式。

·         disabled — 用来禁用内建的过滤器。

 

注意,该模式根据请求的URI路径进行匹配(见HttpServletRequest.getURIPath()),并且在匹配前servlet上下文的名字被删除。

 

增加了主过滤器就可启用下面的内建过滤器。

 

29.1.4.1.  异常处理

 

pages.xml中的这个过滤器提供了异常映射功能 (差不多所有的应用程序都需要这个)。 在未捕获异常发生时,它也处理未提交的事务的回滚。(根据Java EE规范,网页容器应该自动做这个,但是我们发现在所有应用程序服务器中这个行为不能被信赖。并且它的确不需要如Tomcat之类的普通servlet引擎)。

 

默认时,异常处理过滤器会处理所有请求,然而可以通过在components.xml中增加一个<web:exception-filter>条目来调节这个行为,如下面显示的例子这样:

 
<components xmlns="http://jboss.com/products/seam/components"
 
            xmlns:web="http://jboss.com/products/seam/web">
 
 
    <web:exception-filter url-pattern="*.seam"/>
 
</components>
 

29.1.4.2.  对话传播重定向

 

这个过滤器允许Seam在所有浏览器重定向中传播对话上下文 。它拦截浏览器重定向,并增加一个请求参数,指明Seam 对话标识符。

 

默认时,这个重定向过滤器会处理所有请求,但是这个行为也可以在components.xml中进行调节:

 
<web:redirect-filter url-pattern="*.seam"/>
 

29.1.4.3.  URL重写

 

这个过滤器允许对基于pages.xml 文件中的配置的视图应用URL重写。默认时,这个过滤器没有被激活,但是可以在components.xml 中增加配置激活它:

 
<web:rewrite-filter view-mapping="*.seam"/>
 

view-mapping参数必须与在web.xml 文件中对Faces Servlet定义的 servlet映射相匹配。如果忽略,重写过滤器假设模式为*.seam

 

[ *URL重写是拦截传入 Web的请求并自动将请求重定向到其他URL的过程]

 

29.1.4.4.  多表单提交

 

在利用Seam文件上传JSF控件是,这个功能是必要的。它侦测多表单请求,并且根据多/表单数据(multipart/form-data)规范(RFC-2388)处理它们。为覆盖默认设置,增加下列条目到components.xml

 
<web:multipart-filter create-temp-files="true" 
 
                      max-request-size="1000000" 
 
                      url-pattern="*.seam"/>
 

·         create-temp-files — 如果设置为true,上传文件被写到临时文件(替代维持在内存)。 如果希望上传大文件,这可能是一个重点考虑的因素。默认设置为false

·         max-request-size — 如果一个请求上传的文件大小(通过读取在请求中的Content-Length头来决定)超过了这个值,请求会被中止。默认设置是0(没有大小限制)。

 

29.1.4.5.  字符编码

 

设置提交表单数据的字符编码。

 

默认时,这个过滤器没有被安装,并且在components.xml中需要一个条目来启动它:

 
<web:character-encoding-filter encoding="UTF-16" 
 
                               override-client="true" 
 
                               url-pattern="*.seam"/>
 

·         encoding — 使用的编码。

·         override-client — 如果这个设置为true,无论请求是否已经指定了什么编码,请求的编码会被设置为encoding指定的那个编码。如果设置为false,假若请求也没有指定编码, 请求编码才会被设置。默认设置为false

 

29.1.4.6.  RichFaces

 

如果RichFaces被用在你的项目中, Seam会为你安装RichFaces Ajax过滤器,确何安装它在所有其它内建过滤器之前。在web.xml中你不必亲自安装RichFaces Ajax过滤器。

 

如果RichFaces jars包被引入你的项目, RichFaces Ajax过滤器才会被安装。

 

为覆盖默认设置,增加下面条目到components.xml中。该选项与在RichFaces Developer Guide指定它们是一样的:

 
<web:ajax4jsf-filter force-parser="true" 
 
                     enable-cache="true" 
 
                     log4j-init-file="custom-log4j.xml"
 
                     url-pattern="*.seam"/>
 

·         force-parser — 强迫所有JSF页面被Richfaces的XML语法检查器校验 。如果为false, 仅AJAX响应被校验,并转换成格式良好的XML。设置force-parser为false改善了性能 ,仅仅为AJAX 可视部件提供更新。

·         enable-cache — 启用框架生成的资源缓存(如,javascript, CSS, images等等)。当开发自定义javascript或CSS时,设置为true,防止浏览器缓存资源。

·         log4j-init-file —被用来设置per-application注册。应提供log4j.xml 配置文件的一个路径,相对于网页应用上下文。

 

29.1.4.7. 身份日志(Identity Logging

 

这个过滤器添加认证用户名字到log4j映射诊断的上下文中,所以,如果需要,通过增加%X{username}到模式(pattern),它可以被包含在格式化的日志输出中。

 

默认时,日志过滤器将处理所有请求。然而,可以通过增加一个<web:logging-filter>条目到components.xml中调节这种行为,如下面显示的这个例子:

 
<components xmlns="http://jboss.com/products/seam/components"
 
            xmlns:web="http://jboss.com/products/seam/web">
 
    <web:logging-filter url-pattern="*.seam"/>
 
</components>
 
 

29.1.4.8.   自定义servlets的上下文管理

 

直接发送到JSF servlet以外的某些servlet的请求是没有通过JSF生命周期处理的,所以,Seam提供了一个过滤器,它可用于需要访问Seam 组件的任何其他servlet。

 

这个过滤器允许自定义与Seam 上下文交互的servlets。它在每个请求开始时创建Seam上下文, 在请求结束时销毁它们。你应该确保这个过滤器从来都没有用于JSF的FacesServlet。 Seam在一个JSF请求中对上下文的管理使用了一个阶段侦听器。

 

默认时,这个过滤器没有被安装,并且在components.xml需要一个条目来启动它:

 
<web:context-filter url-pattern="/media/*"/>
 

此上下文过滤器希望查找到任何对话上下文的在请求参数中名为conversationId的对话id。你要负责的是确保在请求中发送它。

 

你也要负责确保任何新对话id返回到客户端。Seam把对话id作为内建组件对话的一个属性暴露它。

 

29.1.4.9.  增加自定义过滤器

 

Seam可以为你安装你的过滤器,允许你指定你的过滤器放置在链中的什么地方(如果你在web.xml中指定你的过滤器,servlet规范没有提供一个良好的定义顺序)。只需增加@Filter注释到你的Seam组件 (它必须实现javax.servlet.Filter):

 
@Startup
 
@Scope(APPLICATION)
 
@Name("org.jboss.seam.web.multipartFilter")
 
@BypassInterceptors
 
@Filter(within="org.jboss.seam.web.ajax4jsfFilter")
 
public class MultipartFilter extends AbstractFilter {

 

添加的@Startup注释意味着该组件在Seam启动期可用;在这里双向注入不可用 (@BypassInterceptors); 并且这个过滤器应该放在RichFaces过滤器链后 (@Filter(within="org.jboss.seam.web.ajax4jsfFilter"))。

 

29.1.5.  Seam与你的 EJB 容器集成

 

在一个Seam应用程序中, EJB组件明显有二元性,它们都被 EJB容器和Seam管理。 实际上,多数是由Seam 解析EJB组件引用,管理有状态会话bean组件,并且通过拦截器也参与每个方法调用。让我们开始Seam 拦截器链的配置。

 

我们需要应用SeamInterceptor到我们的Seam EJB组件。这个拦截器代理一组内建服务器边拦截器,处理诸如双向注入、对话划分和业务流程信号等问题。对整过应用程序而言,实现它的最简单方法是在ejb-jar.xml 中增加下面的拦截器:

 
<interceptors>
 
    <interceptor>
 
        <interceptor-class>org.jboss.seam.ejb.SeamInterceptor</interceptor-class>
 
    </interceptor>
 
</interceptors>
 
   
 
<assembly-descriptor>
 
    <interceptor-binding>
 
        <ejb-name>*</ejb-name>
 
        <interceptor-class>org.jboss.seam.ejb.SeamInterceptor</interceptor-class>
 
    </interceptor-binding>
 
</assembly-descriptor>
 

 

Seam需要知道用JNDI在何处查找到会话beans。一个方法是在每个会话bean Seam组件上指定@JndiName注释。然而,这是相当乏味的。一个好的方法是指定一个模式,Seam根据它可以从EJB名字计算出JNDI名字。不幸地是,在EJB3规范中没有映射到全局JNDI定义的标准,所以,这种映射是属于特定供应商的(并且也可能依赖于你自己的命名对话)。我们通过在components.xml 中指定这个选项。

 

JBoss AS,下面的模式是恰当的:

 
<core:init jndi-name="earName/#{ejbName}/local" />
 

在这个案例中, earName是部署bean所在的EAR的名字,Seam用EJB名字替代#{ejbName},最后部分表示接口类型(local或remote)。

 

在一个EAR上下文外(在使用JBoss 嵌入式EJB3容器中 container), 因为没有EAR,所以第一部分可以删除,如以下模式:

 
<core:init jndi-name="#{ejbName}/local" />
 

这些JNDI名字如何被解析,以何种方式查找到EJB组件,在这点上有点象黑色魔术,所以让我们深入细节。首先,让我们谈谈EJB组件如何进入到JNDI。

 

JBoss人不太关心XML,如果你不能告诉(?)。因此在他们设计JBoss AS时, 他们决定使用刚描述的模式(例如,EAR名字/EJB名字/接口类型)自动指派一个全局JNDI名字给EJB组件。EJB名字是来自下面列表的第一个非空值:

 

·         ejb-jar.xml中的<ejb-name>的值

·         @Stateless或@Stateful注释中的name属性的值

·         bean类的简单名字

 

让我们看一个例子。假设你有下面的EJB bean和接口定义。

 

package com.example.myapp;
 
 
import javax.ejb.Local;
 
 
@Local
 
public class Authenticator
 
{
 
    boolean authenticate();
 
}
 
 
package com.example.myapp;
 
 
import javax.ejb.Stateless;
 
 
@Stateless
 
@Name("authenticator")
 
public class AuthenticatorBean implements Authenticator
 
{ 
 
    public boolean authenticate() { ... }
 
}

 

假设你的EJB bean类被部署在一个名为myapp的EAR中, 在JBoss AS 中,全局JNDI名字myapp/AuthenticatorBean/local将被指派给它。正如你了解的,你可以象一个Seam组件一样用名字authenticator引用这个EJB组件,并且 Seam将根据JNDI模式(或@JndiName注释)用JNDI查找到它。

 

那么其他的应用服务器又如何?好的,根据Java EE 规范,大部分供应商试图虔诚地坚持,对你的EJB你必须声明一个EJB引用,以便于被指派一个JNDI 名字。这需要一些XML。也意味着由你来决定如何建立一个JNDI命名对话,因此你可以利用Seam JNDI 模式。接下来你可能会找到一个很好的JBoss 对话。

 

当你在非JBoss应用服务器上使用Seam时,有两个地方你必须定义EJB引用。如果你打算通过JSF或Seam JavaBean组件查找Seam EJB组件(在一个JSF视图中或用一个JSF动作侦听器),那么你必须在web.xml中声明EJB引用。 这里只显示EJB引用的例子组件:

 
<ejb-local-ref>
 
    <ejb-ref-name>myapp/AuthenticatorBean/local</ejb-ref-name>
 
    <ejb-ref-type>Session</ejb-ref-type>
 
    <local>org.example.vehicles.action.Authenticator</local>
 
</ejb-local-ref>
 

这种引用方法覆盖了在Seam应用程序中的大多数组件的用途。如果你想能使用@In注入Seam EJB 组件到另一个Seam EJB 组件,你需要在另一个地方定义这个EJB引用。这次,它必须定义在ejb-jar.xml 中,并且有点麻烦。

 

在一个EJB方法调用的上下文内,你不得不处理有些受保护的JNDI上下文。当Seam企图找到另一个Seam EJB组件来满足由@In定义的注入点时,用JNDI查找组件是不会成功的。你不能象你希望的那样,简单地解析JNDI名字。你必须明确地定义这些引用。然而,不象使用网页上下文,你不能声明所有EJB组件的EJB引用为全局的。相反,你不得不一个接一个对每一个给定的EJB组件指定JNDI资源。 (这适用于JBoss AS 5,而不适用于非JBoss 应用服务器).

 

让我们假定我们有一个名字为RegisterAction的EJB(名字解析使用前面提及的三个步骤)。该EJB拥有下面的Seam注入:

@In(create = true)
 
Authenticator authenticator;

 

为了使这个注入能工作,必须在ejb-jar.xml建立连接,象下面这样:

 
<ejb-jar>
 
    <enterprise-beans>
 
        <session>
 
            <ejb-name>RegisterAction</ejb-name>
 
            <ejb-local-ref>
 
                <ejb-ref-name>myapp/AuthenticatorAction/local</ejb-ref-name>
 
                <ejb-ref-type>Session</ejb-ref-type>
 
                <local>com.example.myapp.Authenticator</local>
 
            </ejb-local-ref>
 
        </session>
 
    </enterprise-beans>
 
 
 
    ...
 
    
 
</ejb-jar>
 

注意<ejb-local-ref>上下文是与我们在web.xml中定义的是一样的。我们能让引用进入到EJB上下文,以便RegisterAction bean可以使用它。对任何Seam EJB组件注入另一个Seam EJB组件,你需要使用@In来增加这些引用中的一个。(你可以看jee5/booking例子)。

 

但是@EJB有什么用的? 使用@EJB,它为ture,你可以注入一个EJB 到另一个中。然而,这样做,你是注入了一个真实的EJB引用,而不是Seam  EJB 组件实例。在这种情况下,一些Seam 功能可以使用,另一些则不能使用。那就是为什么在任何一个方法调用一个EJB组件时需要调用的Seam 拦戴器。但是,仅可调用Seam服务器边的拦截器链。失去了Seam 的状态管理和Seam 客户边的拦截器链。客户边的拦截器处理诸如安全和并发等问题。当注入一个SFSB时,无论情况如何,也不能保证会约束活动的会话或对话。因而,你一定想使用@In注入Seam EJB 组件。

 

这包括如何定义和使用JNDI名字。使用某些应用服务器的经验,如GlassFish, 是你不得不为所有EJB组件明确地指定JNDI名字,有时两次!即使是根据象JBoss AS同样的命名对话,在Seam 中的JNDI 模式可能也需要被改变。例如,在GlassFish 中的全局JNDI名字用java:comp/env 自动加前缀,所以你需要象下面这样定义JNDI模式:

 
<core:init jndi-name="java:comp/env/earName/#{ejbName}/local" />
 

最后,让我们谈谈事务。在一个EJB3环境中,我们推荐使用专门的事务管理内建组件,其充分知道容器的事务,可以正确地处理用事件组件注册成功事件的事务。如果没有增加这行到你的components.xml 文件,Seam 就不知道容器托管事务在什么时候结束。

 
<transaction:ejb-transaction/>
 
 

29.1.6.  切记!

 

你需要知道的最后一条。你必须在你部署的seam组件的每个档案中设置seam.properties、META-INF/seam.properties 或META-INF/seam.properties 文件(即使是空的properties也要这样做)。在启动时, Seam会用seam.properties扫描每个档案的seam 组件

 

在一个Web档案(WAR)文件中,在 WEB-INF/classes 目录下,如果包含有任何Seam组件,你必须在该目录中放置一个 seam.properties文件。

 

这就是为什么所有的Seam例子都有一个空的 seam.properties 文件。希望一切能仍旧工作,你不能删除它!

 

你可能会认为这很愚蠢,框架的设计者让一个空文件来影响他们的软件行为是多么的白痴??嗯,这是一个针对<span lang

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics