配置是一个非常枯燥的话题,也是一种极其乏味的消遣。不幸的是,为了将Seam集成到你的JSF实现和Servlet容器中,有些XML语句是必须的。 你不必再被以下部分拖延时间了;你将永远不必亲自键入以下任何内容,因为你可以从示例应用程序中拷贝和粘帖。
首先,让我们看看每当将Seam和JSF一起使用时所需要的基本配置。
当然,你需要一个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>
这个listener负责引导Seam,以及销毁会话和应用程序。
有些JSF实现在服务器端状态保持方面实现得很差,它们会与Seam的对话传播发生冲突。 如果你在表单提交时遇到了对话传播的问题,就尝试着切换到客户端状态保持。在你的 web.xml 中需要有这些:
<context-param> <param-name>javax.faces.STATE_SAVING_METHOD</param-name> <param-value>client</param-value> </context-param>
如果你愿意听从我们的建议,使用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>
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>
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 中指定的第一个过滤器。这样就能保证它首先运行。
为了取消内建的过滤器,你可以在一个特定的过滤器中设置 disabled="true"
增加主过滤器使得以下内建过滤器可用。
这个过滤器在 pages.xml 中提供异常映射功能(几乎所有的应用程序都需要这个)。 当未捕获的异常发生时,它也负责回滚未提交的事务。 (根据Java EE规范,Web容器应该能自动做到这一点,但我们发现在所有应用程序服务器中,都不支持这一行为。 当然在普通Servlet引擎如Tomcat中,它并不是必需的。)
默认情况下,异常处理过滤器将处理所有请求,但是这种行为可以通过增加一个 <web:exception-filter> 项到 components.xml 文件来进行调整,如下面的例子所示:
<components xmlns="http://jboss.com/products/seam/components" xmlns:web="http://jboss.com/products/seam/web"> <web:exception-filter url-pattern="*.seam"/> </components>
url-pattern — 被用来指定过滤哪些请求,默认是所有请求。
这个过滤器允许Seam通过浏览器重定向来传播对话上下文。 它拦截任何浏览器重定向,并增加一个指定Seam对话标识符的请求参数。
默认情况下,重定向过滤器将处理所有的请求,但这个行为也可以在 components.xml 文件中进行调整:
<web:redirect-filter url-pattern="*.seam"/>
url-pattern — 被用来指定过滤哪些请求,默认是所有请求。
当使用Seam文件上传JSF控件时,这个特性很有必要。它依据多重表单/数据规范(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(大小不限)。
url-pattern — 被用来指定过滤哪些请求,默认是所有请求。
设置被提交的表单数据的字符编码
默认没有装载这个过滤器,它需要由 components.xml 中的一个项来启用:
<web:character-encoding-filter encoding="UTF-16" override-client="true" url-pattern="*.seam"/>
encoding — 要使用的编码
override-client — 如果它被设置为 true,则请求中的编码将被设置为 encoding 指定的任何值,而不管请求中是否已经指定一种编码。 如果设置为 false,当请求中没有指定任何一种编码时,才会设置请求编码。默认设置是 false
url-pattern — 被用来指定过滤哪些请求,默认是所有请求。
如果在你的工程中使用了Ajax4jsf,Seam会确保在装载其他所有内建过滤器之前,为你装载Ajax4jsf过滤器。 你不必亲自在 web.xml 中装载Ajax4jsf过滤器。
只有当你的工程中有RichFaces jar包时,才会安装RichFaces Ajax过滤器。
为了覆盖默认的设置,要在 components.xml 中增加以下项。这些选项与RichFaces开发者指南中指定的一样:
<web:ajax4jsf-filter force-parser="true" enable-cache="true" log4j-init-file="custom-log4j.xml" url-pattern="*.seam"/>
force-parser — 强制Richfaces的XMl语法检验器去验证所有的JSF页面。 如果为 false,则只有AJAX响应才被验证并被转换成合适的XML。 设置force-parser 为 false 可以提高性能,但会在AJAX更新时提供可视化的工件。
enable-cache — 启用框架生成资源的缓存(如,JavaScript,CSS,Images等)。 当开发定制的JavaScript或CSS时,设置为true以阻止浏览器缓存这些资源。
log4j-init-file — 被用于安装 per-application 记录。 应该提供一个相对于web程序上下文的log4j.xml配置文件路径。
url-pattern — 用来指定过滤哪些请求,默认是所有请求。
这个过滤器将被验证的用户名添加到log4j映射诊断上下文中,以便如果喜欢,可能通过在模式中添加%X{username},使它能够被包含在格式化过的日志输出中。
默认情况下,记录过虑器会处理所有请求,但是这一行为可以通过在 components.xml 中添加 <web:logging-filter> 项来进行调整,如下面的例子所示:
<components xmlns="http://jboss.com/products/seam/components" xmlns:web="http://jboss.com/products/seam/web"> <web:logging-filter url-pattern="*.seam"/> </components>
url-pattern — Used to specify which requests the filter is active for. The default is all requests. url-pattern —用来指定该过滤器是为哪些请求而激活的。默认是所有请求。
直接发送到某些Servlet而不是JSF Servlet的请求不在JSF生命周期中被处理,因此Seam提供了一个Servlet过滤器供其他的Servlet使用,这些Servlet需要访问Seam组件。
这个过滤器允许定制的Servlet与Seam上下文交互。它在每个请求的开始设立Seam上下文,并在请求结束时卸掉它们。 你必须确保这个过滤器 永远不 被用于JSF FacesServlet。 Seam在JSF请求中为上下文管理使用阶段监听器(phase listener)。
默认没有装载这个过滤器,它需要由 components.xml 中的一个项来启用:
<web:context-filter url-pattern="/media/*"/>
url-pattern — 被用来指定过滤哪些请求,默认是所有请求。 如果上下文过滤器的URL模式已指定,则将启用该过滤器(除非明确取消)。
上下文过滤器期望在名称为 conversationId 的请求参数中,找到所有对话上下文的对话id。 你要负责确保对话ID在请求中被发送。
你还需要负责确保将所有新的对话id传送回客户端。Seam将对话id作为内建的 conversation 组件的一个属性暴露出来。
Seam能为你装载过滤器,允许你指定过滤器要放在过滤器链中的 什么位置 (如果你在 web.xml 中指定自己的过滤器,servlet规范就没有提供一个定义良好的顺序)。 只需要给你的Seam组件加上 @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启动时有效;双向注入(bijection)在这里无效(@BypassInterceptors); 并且该过滤器在链中应该比Ajax4jsf过滤器(@Filter(within="org.jboss.seam.web.ajax4jsfFilter"))更靠后一些。
我们需要将 SeamInterceptor 用于我们的Seam组件。 在整个程序中完成这个的最简单方式是在 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中到哪里去寻找会话Bean。其中一种方法是在每个会话Bean Seam组件中指定 @JndiName注解。 然而,这样相当乏味。更好的一种方式是指定一种模式,Seam可以根据这个模式从EJB名称里推算出JNDI的名称。 不幸的是,在EJB3规范里面,没有定义到全局JNDI的标准映射,所以这个映射是特定于供应商的。 我们通常在 components.xml 中指定这个选项。
对于JBoss AS,以下的模式是正确的:
<core:init jndi-name="myEarName/#{ejbName}/local" />
myEarName就是部署Bean所在EAR的名称。
在一个EAR的外面(当使用嵌入式的JBoss的EJB3容器时),以下模式就是要用到的那个:
<core:init jndi-name="#{ejbName}/local" />
你不得不尝试着去找到其他应用程序服务器的正确配置。 注意有些服务器(比如GlassFish)要求你为所有EJB组件直接(而且很乏味地)指定JNDI名称。 在这种情况下,你可以选择你自己的模式。:-)
在一个EJB3的环境中,我们建议给事务管理使用一个特殊的内建组件,它完全知道容器事务,并且可以正确地处理用 Events 组件注册的事务成功事件。 如果你没有在你的 components.xml 文件中增加这几行,Seam就不会知道容器管理的事务什么时候结束:
<transaction:ejb-transaction/>
还有最后一点你要知道的。你必须在部署你的Seam组件的每个压缩文档中(即使空的属性文件也要这样),放置一个 seam.properties、META-INF/seam.properties 或META-INF/components.xml文件。 在启动时,Seam将会根据 seam.properties 文件扫描所有压缩档案寻找Seam组件。
在一个Web压缩档案(WAR)文件中,如果在 WEB-INF/classes 目录下,包含有任何Seam组件,你必须在该目录中放置一个 seam.properties文件。
这就是为什么所有的Seam示例程序都有一个空的 seam.properties 文件的原因。 你不能删掉这些文件,还期待一切仍然正常运行!
你可能会觉得这很愚蠢,并且框架的设计者做一个空文件来影响他们的软件该是多么的白痴啊? 当然,这是针对Java虚拟机的限制所采取的一种权宜之计 — 如果我们不使用这个机制,那么我们的下一个最佳选项将逼迫你显式地在 components.xml 中列出所有的组件,就像其他某些竞争框架所做的那样!我想你会喜欢更喜欢我们这种方式。
如果你是在Java EE 5的环境下运行,这就是开始使用Seam所需的全部配置!
一旦你将所有这些东西都打包到一个EAR文件中,该压缩文档看起来就会像这样:
my-application.ear/ jboss-seam.jar lib/ jboss-el.jar META-INF/ MANIFEST.MF application.xml my-application.war/ META-INF/ MANIFEST.MF WEB-INF/ web.xml components.xml faces-config.xml lib/ jsf-facelets.jar jboss-seam-ui.jar login.jsp register.jsp ... my-application.jar/ META-INF/ MANIFEST.MF persistence.xml seam.properties org/ jboss/ myapplication/ User.class Login.class LoginBean.class Register.class RegisterBean.class ...
你要在 META-INF/application.xml 中将 jboss-seam.jar 声明为一个EJB模块。 jboss-el.jar 应该被放置在EAR的lib目录下(将它放在EAR的classpath中)。
如果你想使用jBPM或Drools,你必须在EAR的lib目录下包含所需要的jar文件。
如果你想使用Facelets(我们建议使用),你必须在WAR的 WEB-INF/lib 目录下包含 jsf-facelets.jar
如果你想使用Seam标签库(大多数Seam应用程序都这么做),你就必须在WAR的 WEB-INF/lib 目录下包含 jboss-seam-ui.jar。 如果你想用PDF或者Email标签库,则需要在 WEB-INF/lib 目录下放置 jboss-seam-pdf.jar 或 jboss-seam-mail.jar。
如果你想要使用Seam调试页面(只在使用Facelets的应用程序中有效),你必须在WAR的 WEB-INF/lib 目录下包含jboss-seam-debug.jar
Seam发行时包括几个示例程序,他们能在任何支持EJB 3.0的Java EE容器中部署。
我真的希望这就是配置专题所要说的全部内容,可惜的是,我们只涉及到了它的三分之一。 如果你实在无法忍受所有这些关于配置的废话,尽管放心地跳过本节剩下的部分,以后再回来这里。
Seam很有用,即使你还没有冒险尝试EJB 3.0。在这种情况下,你可以使用Hibernate3或者JPA代替EJB 3.0持久化技术,并用简单的JavaBeans代替会话Bean。 你可能会错过会话bean的某些很好的特性,但是当你准备好时,这将会很容易迁移到EJB 3.0,同时,你将能利用Seam独特的声明式状态管理架构。
Seam JavaBean组件不会像会话Bean那样提供声明式事务划分。你 可以 使用 JTA UserTransaction 手动管理你的事务,或者使用Seam的 @Transactional 标注声明。 但是当与JavaBean一起使用Hibernate时,大部分应用程序只会用到由Seam管理的事务。
Seam发行包里包括预订示例程序的一种使用Hibernate3和JavaBeans而不是EJB3的版本,以及另一种使用JPA和JavaBeans的版本。 这些示例程序可以随时部署到任何J2EE应用程序服务器中。
如果你装载了一个内建的组件,Seam将会从你的 hibernate.cfg.xml 文件中引导一个 Hibernate SessionFactory:
<persistence:hibernate-session-factory name="hibernateSessionFactory"/>
如果你想要通过注入得到一个由Seam管理的Hibernate Session,还需要配置一个 managed session。
<persistence:managed-hibernate-session name="hibernateSessionFactory" session-factory="#{hibernateSessionFactory}"/>
如果你装载了一个内建的组件,Seam将会从你的 persistence.xml 文件中引导一个 JPA EntityManagerFactory:
<persistence:entity-manager-factory name="entityManagerFactory"/>
如果你想要通过注入得到一个由Seam管理的JPA EntityManager,还需要配置一个 managed persistence context。
<persistence:managed-persistence-context name="entityManager" entity-manager-factory="#{entityManagerFactory}"/>
我们可以将我们的应用程序按照以下结构打包成一个WAR文件:
my-application.war/ META-INF/ MANIFEST.MF WEB-INF/ web.xml components.xml faces-config.xml lib/ jboss-seam.jar jboss-seam-ui.jar jboss-el.jar jsf-facelets.jar hibernate3.jar hibernate-annotations.jar hibernate-validator.jar ... my-application.jar/ META-INF/ MANIFEST.MF seam.properties hibernate.cfg.xml org/ jboss/ myapplication/ User.class Login.class Register.class ... login.jsp register.jsp ...
如果我们想要将Hibernate部署在一个非EE的环境中(如Tomcat或TestNG),我们需要多做一些工作。
完全在一个EE环境之外使用Seam是有可能的。在这种情况下,你需要告诉Seam怎样去管理事务,因为没有JTA可用。 如果你在使用JPA,就可以告诉Seam去使用JPA本地资源的事务,如 EntityTransaction,像这样:
<transaction:entity-transaction entity-manager="#{entityManager}"/>
如果你在使用Hibernate,就可以告诉Seam像下面这样使用Hibernate事务API:
<transaction:hibernate-transaction session="#{session}"/>
当然,你还需要去定义一个数据源。
一种更好的替代方案是使用嵌入式的JBoss去访问EE的API。
嵌入式的JBoss让你可以在Java EE 5应用程序服务器的上下文的外面运行EJB3组件。这个不但对测试有用,而且是特别有用。
Seam的booking示例程序包括一个通过 SeamTest 在嵌入式的JBoss上运行的TestNG集成测试套件。
booking示例程序甚至可以在Tomcat上部署。
为了让Seam应用程序在Tomcat上正确运行,必须将嵌入式JBoss装载到Tomcat中。 嵌入式的JBoss可以在 这里 下载。将嵌入式的JBoss安装到Tomcat 6的过程非常简单。首先,你必须将嵌入式JBoss的JAR和配置文件都拷贝到Tomcat中。
将嵌入式JBoss的 bootstrap 和 lib 目录下的所有文件和目录,除了 jndi.properties 文件之外,都拷贝到Tomcat的 lib 目录下。
从Tomcat的 lib 目录中移除 annotations-api.jar 文件。
接下来,需要更新两个配置文件,用来增加嵌入式JBoss特有的功能。
将嵌入式JBoss listener增加到 conf/server.xml 中。它在文件中应该排列在所有其他listener的后面。
<Listener className="org.jboss.embedded.tomcat.EmbeddedJBossBootstrapListener" />
应该通过增加一个listener到 conf/context.xml 文件中来启用WAR文件扫描功能。
<Listener className="org.jboss.embedded.tomcat.WebinfScanner" />
关于更多的配置选项,请参考Tomcat集成嵌入式JBoss Wiki条目。
一个在Servlet引擎(如Tomcat)中基于WAR部署的压缩文档,它的结构有时看起来像这样:
my-application.war/ META-INF/ MANIFEST.MF WEB-INF/ web.xml components.xml faces-config.xml lib/ jboss-seam.jar jboss-seam-ui.jar jboss-el.jar jsf-facelets.jar jsf-api.jar jsf-impl.jar ... my-application.jar/ META-INF/ MANIFEST.MF persistence.xml seam.properties org/ jboss/ myapplication/ User.class Login.class LoginBean.class Register.class RegisterBean.class ... login.jsp register.jsp ...
绝大部分Seam示例程序可以通过运行 ant deploy.tomcat 部署到Tomcat中。
Seam的jBPM集成没有被默认装载,因此你需要通过装载一个内建的组件来启用jBPM。你还需要显式地列出你的流程和页面流定义。 在 components.xml 文件中:
<bpm:jbpm> <bpm:pageflow-definitions> <value>createDocument.jpdl.xml</value> <value>editDocument.jpdl.xml</value> <value>approveDocument.jpdl.xml</value> </bpm:pageflow-definitions> <bpm:process-definitions> <value>documentLifecycle.jpdl.xml</value> </bpm:process-definitions> </bpm:jbpm>
如果你只有页面流,就不需要更多特殊的配置。如果你的确有业务流程定义,就需要为jBPM提供一个jBPM配置和一个Hibernate配置。 Seam的DVD Store Demo包括了与Seam共同工作的 jbpm.cfg.xml 和 hibernate.cfg.xml 文件的例子:
<jbpm-configuration> <jbpm-context> <service name="persistence"> <factory> <bean class="org.jbpm.persistence.db.DbPersistenceServiceFactory"> <field name="isTransactionEnabled"><false/></field> </bean> </factory> </service> <service name="tx" factory="org.jbpm.tx.TxServiceFactory" /> <service name="message" factory="org.jbpm.msg.db.DbMessageServiceFactory" /> <service name="scheduler" factory="org.jbpm.scheduler.db.DbSchedulerServiceFactory" /> <service name="logging" factory="org.jbpm.logging.db.DbLoggingServiceFactory" /> <service name="authentication" factory="org.jbpm.security.authentication.DefaultAuthenticationServiceFactory" /> </jbpm-context> </jbpm-configuration>
在这儿需要注意的最重要事情是JBPM事务处理控制功能是禁用的。Seam或EJB3应该控制JTA事务。
对于jBPM配置和流程/页面流定义文件,还没有任何明确的打包格式。 在Seam的示例程序中,我们决定简单地将所有这些文件打包到EAR的根目录下。在以后,我们可能设计其他一些标准的打包格式。 因此EAR看起来有点像这样:
my-application.ear/ jboss-seam.jar lib/ jboss-el.jar jbpm-3.1.jar META-INF/ MANIFEST.MF application.xml my-application.war/ META-INF/ MANIFEST.MF WEB-INF/ web.xml components.xml faces-config.xml lib/ jsf-facelets.jar jboss-seam-ui.jar login.jsp register.jsp ... my-application.jar/ META-INF/ MANIFEST.MF persistence.xml seam.properties org/ jboss/ myapplication/ User.class Login.class LoginBean.class Register.class RegisterBean.class ... jbpm.cfg.xml hibernate.cfg.xml createDocument.jpdl.xml editDocument.jpdl.xml approveDocument.jpdl.xml documentLifecycle.jpdl.xml
要将Seam程序作为一个portlet运行,你需要提供某些portlet元数据(portlet.xml,等)作为对通常Java EE元数据的补充。 参考 examples/portal 目录,作为booking demo预配置以便在JBoss Portal上运行的一个例子。
将有状态会话Bean的超时值设置得比HTTP的超时值高一些是很重要的,否则SFSB可能在HTTP会话结束前已经超时。 JBoss程序服务器有一个30分钟的默认会话Bean超时值,它在 server/default/conf/standardjboss.xml(用你自己的配置取代 default)中配置。
默认的SFSB超时值可以通过修改 LRUStatefulContextCachePolicy 缓存配置中 max-bean-life 的值得到调整。
<container-cache-conf> <cache-policy>org.jboss.ejb.plugins.LRUStatefulContextCachePolicy</cache-policy> <cache-policy-conf> <min-capacity>50</min-capacity> <max-capacity>1000000</max-capacity> <remover-period>1800</remover-period> <!-- SFSB timeout in seconds; 1800 seconds == 30 minutes --> <max-bean-life>1800</max-bean-life> <overager-period>300</overager-period> <max-bean-age>600</max-bean-age> <resizer-period>400</resizer-period> <max-cache-miss-period>60</max-cache-miss-period> <min-cache-miss-period>1</min-cache-miss-period> <cache-load-factor>0.75</cache-load-factor> </cache-policy-conf> </container-cache-conf>
可以为JBoss 4.0.x在 server/default/deploy/jbossweb-tomcat55.sar/conf/web.xml 中修改,或者为JBoss 4.2.x在 server/default/deploy/jboss-web.deployer/conf/web.xml 中修改默认的HTTP会话超时值。 这个文件中的以下项控制着所有Web应用程序的默认会话超时值:
<session-config> <!-- HTTP Session timeout, in minutes --> <session-timeout>30</session-timeout> </session-config>
要为你自己的应用程序覆盖这个值,只需在你应用程序自己的 web.xml 文件中包含这个项。