如果你刚开始考虑应用portal解决方案,或者想了解把一个新的或现有的JSF应用整合进一个portal环境有多容易,那么本文绝对值得你仔细研读。
在过去几年中,portals无论是在企业还是在增强的portlet 2.0(JSR 286)规范中都得到了长足发展。新2.0版本的portlets在聚合不同的应用,并将其呈现在同一个页面的不同窗口方面赋于开发人员很大的自由。此外,它还提供了认证功能,成熟的个性化特性和更好的处理AJAX的现成方法。
JSR 301 portlet桥接规范(portlet bridge specification)的出现,使我们具备了运行JSF应用的标准方式,不论portlets版本是1.0还是2.0。portlet桥(bridge)可以支配portal的Action/Render范式适当地处理JSF的生命周期。本教程将主要向你展示配置与开发JSF portlet是多么容易的一件事情,同时还会介绍新的JBoss Portlet Container 2.0及其一些很酷的新特性。
本文是由三部分组成的系列文章的第一篇。该系列文章从基本的JSF portlet和portal知识一直讲到portlet环境中的AJAX和Seam的高级用法。
现在准备开始吧!
开发工具:
要想成功实践文中的范例就要下载最新版本的Maven(我用的是2.0.9版本)。
安装 Maven 2.0.9+。
设置 Maven二进制版本的环境变量。
例子中用到的服务器和二进制版本:
JBoss Portal’s Portlet Container 2.0
JBoss Portlet Bridge Beta3
目前,JBoss Portlet Bridge是JSR 301规范的唯一实现,它允许你运行JSF、RichFaces和Seam的任意组合。你项目的Maven配置可安排下载打包在一起的JBoss AS和JBoss Portlet Container 2.0,如果你想分别下载它们,可以在此处找到这些文件。否则,就给Maven几分钟让它去自行下载适当的文件。
注意——在当前的2.6.5.SP1版本的JBoss Portal中同样可以运行这个portlet ,我在本文中使用了JBoss Portlet Container 2.0,但是桥(bridge)在两者中都能工作。你可通过此处了解令其可在任意版本的JBoss Portal中运行的配置方法。
下述Maven原型(Maven archetype)是创建项目的一种简易方式,可以快速建立并运行一个启始项目(或者模板项目)。一旦你运行了这些命令,就可获得实践本文示例所需的一切文件。
打开一个终端窗口并运行如下指令:
mvn archetype:generate -DarchetypeGroupId=org.jboss.portletbridge.archetypes -DarchetypeArtifactId=1.2-basic -DarchetypeVersion=1.0.0.B3 -DgroupId=org.whatever.project -DartifactId=myprojectname -DarchetypeRepository=http://repository.jboss.org/maven2/ -Dversion=1.0.0.B3
现在,找到你的新建项目所在文件目录(如果你使用的跟上面的例子一样,那么目录名就应该是 “myprojectname”),浏览其中刚刚创建的所有文件,你将会看到一个基本Maven文件结构,其中带有适用于本文范例的源码。现在你就可以打开最喜欢的IDE环境,引入这个Maven项目了。
从现在开始的开发过程更像是在Servlet世界中开发JSF应用,不同之处在于:处理类似单点登录这样的问题;诸如命名空间(namespacing)这样的需要portletContext 和访问变量的问题;以及portlet中像“Help”和“Edit”这样的窗口模式问题。我不想在本文讲述过多portlet的细节,但我会提供进行基本开发的必要信息。如果你想了解更多portlets的信息,请参考JSR 168或JSR 286规范说明。
JBoss Portlet Bridge比较酷的一点在于它不是一个portlet,它只是portlet和JSF之间的媒介。而你的JSF应用则是个portlet,不过除了 WEB-INF目录下3到4个额外的xml文件和关于bridge的Jar包外,它的其余部分与你的Servlet版本的应用毫无二致。下面几个配置文件是主要区别:
portlet.xml
<portlet> ...v <portlet-class> javax.portlet.faces.GenericFacesPortlet </portlet-class> <init-param> <name>javax.portlet.faces.defaultViewId.view</name> <value>/home.xhtml</value> </init-param> <init-param> <name>javax.portlet.faces.defaultViewId.edit</name> <value>/jsf/edit.xhtml</value> </init-param> <init-param> <name>javax.portlet.faces.defaultViewId.help</name> <value>/jsf/help.xhtml</value> </init-param> ... </portlet>
web.xml
<context-param> <param-name>org.ajax4jsf.VIEW_HANDLERS</param-name> <param-value> org.jboss.portletbridge.application.FaceletPortletViewHandler </param-value> </context-param> <context-param> <param-name>javax.portlet.faces.renderPolicy</param-name> <param-value> ALWAYS_DELEGATE </param-value> </context-param>
faces-config.xml
<application> <view-handler> org.jboss.portletbridge.application.PortletViewHandler </view-handler> <state-manager>org.jboss.portletbridge.application.PortletStateManager</state-manager> </application>
由于上述设置已经应用在你刚刚设定的Maven原型中,因此现在我们就可以编译项目,并在JBoss Portal上部署它了。
编译新项目,并通过如下两个步骤完成部署:
步骤1: mvn install cargo:start -Premote-portal -Dpc20
这行命令将会花上几分钟去下载打在一起的server+portal包,所以在进行下一步骤之前,请耐心等待直至看到如下内容:
*注意——在server启动前你还会看到一个PortletException ,这很正常,而且它也是Portlet Container 2.0示例FailDuringInitPortlet的一部分。
接下来打开另外一个终端窗口,找到JSF portlet项目的根目录,并运行:
步骤2: mvn cargo:deploy -Premote-portal -Dpc20
命令行参数通知cargo你正在运行的portal版本和JBoss+Portal包所在位置。这个例子同样可以运行于遵从JSR 168的最新版本JBoss Portal 2.6.5.SP1中。如果想了解更多Maven命令及相关信息,你可以参考JBoss Portlet Bridge文档。要查看已部署的JSF portlet情况可访问如下链接:http://localhost:8080/simple-portal/demo/jsr-301.jsp。
现在让我们来了解将portlet和JSF联系在一起的技术。
把你的portlet视作一个普通的Web应用
当把你的应用视作servlet端的Web应用时,portlet桥是透明的。作为portlet开发人员,一种很好的做法是偶尔仔细检查一下你的应用以确保正在进行的开发没有扩大的趋势,因为它将运行在一个portal环境中。要查看此demo应用,或者任何已部署的带有桥的应用,可以访问http://localhost:8080/JSFRIPortlet/home.jsf。
命名空间
在portal环境中桥可以处理JSF的命名空间组合。当你需要在JSF/xhtml标记中使用元素id时,正常情况下在显示的标记中你所看到的是类似‘form1:myBtn’的代码,而如今在桥命名空间中你所看到的将是象:
jbpns_2fdefault_2fNews_2fStories_2fStoryTemplateWindow12snpbj:_viewRoot:form1:myBtn
这样的标记。
要克服这一点,你可以在你的Facelets页中应用下面的表达式,来为你的javascript代码预先设定命名空间:
document.getElementById('#{facesContext.externalContext.response.namespace}the_rest_of_JSF_ID');
请注意,因为使用了portletResponse,所以一旦你试图在servlet应用端查看这个页面的时候就会得到一个异常。为避免这种情况的发生,你需要在你的后台Bean(backing bean)中检查响应的类型,并为UI分配一个新的、“安全的”命名空间变量。
preserveActionParams
当你的web.xml文件中preserveActionParams值被设置为TRUE时,桥必须负责维护在portlet动作请求期间指派的任意参数,它们被保存在“桥请求域(bridge request scope)”中。当这个属性没有出现或者值为FALSE时,只在portlet请求作用域维护动作的请求参数。
<init-param> <param-name>javax.portlet.faces.preserveActionParams</param-name> <param-value>true</param-value> <init-param>
在你的Facelets页面中应用类似 #{request.yourParam}这样的代码可以利用这一设置。
排除桥请求域中的属性
当你的应用在每一次请求上都要使用请求属性而你又不想在扩展的桥请求作用域中管理特殊的属性时,你就必须在你的faces-config.xml文件中使用如下的配置了。下面你会看到任何命名空间为foo.bar的属性或者是以foo.baz(通配符)打头的属性都会被从桥请求作用域中排除,这些属性只能用在每一次请求当中:
<application> <application-extension> <bridge:excluded-attributes> <bridge:excluded-attribute>foo.bar</bridge:excluded-attribute> <bridge:excluded-attribute>foo.baz.*</bridge:excluded-attribute> </bridge:excluded-attributes> </application-extension> </application>
或者你可以使用javax.portlet.faces.annotation.ExcludeFromManagedRequestScope
j注解,对你不想包含在请求中的对象类进行注解,也可以达到同样的效果。
正如你所看到的,JSR 301规范不仅使得把已存在的JSF web应用建立成portlet更加容易,而且还提供给开发人员一种管理和处理JSF和portlets差异的方式。不过,规范目前仍处于频繁地评审和修订阶段。写这篇文章时最新的公开版本是Early Draft Review 3,但是在JSR 301专家组内部,对此修订版本又已经有很多改进了,对桥比较重大的改进是在下一个JBoss Portlet Bridge版本(Beta 4)中将会实现的portlet模式导航和状态。
如果想了解更多项目信息和桥支持的兼容版本,或者想要参与社区论坛,请访问我们的项目页面和blog。
查看英文原文:Developing Portlets using JSF, Ajax, and Seam (Part 1 of 3) 。
志愿参与InfoQ中文站内容建设,请邮件至[email protected]。也欢迎大家到InfoQ中文站用户讨论组参与我们的线上讨论。