Portlet是组件化的面向用户的应用程序,可创建某种标记。 该标记旨在与其他标记片段聚合到一个更大的应用程序中,例如,如图1所示的门户页面。

图1.一个样本门户页面

因此,可以将Portlet视为基于用户界面(UI)的服务,这些服务采用面向服务的体系结构(SOA)的方式一直到用户界面。

Java Portlet规范的第一个版本Java规范要求(JSR)168在2003年的Java Community Processes中完成后,Java portlet开始流行。 从那时起,Java门户空间中的几乎所有供应商(包括商业供应商和开源供应商)都已实现此标准,并且开发人员已使用Java Portlet API编写了Portlet。

但是,JSR 168停止定义整个UI组件模型,并且没有定义任何从这些组件中构建集成复合应用程序的方法。 V2.0现在解决了此限制以及由于时间限制而未纳入V1.0的许多其他问题。

JSR 286的工作始于2006年1月,最终版本于2008年2月提交。JSR 286的专家组由所有主要的商业和开源门户开发人员,portlet桥开发人员和portlet框架开发人员组成。 您可以在此处找到专家组成员的完整列表。

本文概述了JSR 286中的主要新功能,以及有关如何利用这些功能的一些示例。 本文假定您具有有关1.0版中定义的Portlet编程模型的基本知识; 提供有关JSR 168的介绍性信息 。

使用协调构建复合应用程序

2.0版的主要新功能提供了在不同portlet之间进行协调的功能,这些功能可以由不同的方面实现并打包在不同的WAR文件中。 JSR 286的协调功能基于松散耦合的发布/订阅模型,该模型不需要不同的Portlet开发人员了解彼此的工作。 在开发时,您仅定义Portlet可以理解的数据,而Portlet之间的实际连接是在部署或运行​​时创建的。 利用这些协调功能,门户网站管理员或业务用户现在可以利用Portlet组件构建大型的复合应用程序,而无需进行编程。 这种功能使通过将现有组件以类似于流行的Web 2.0用法的方式组合到业务mashup中来创建新功能的可能性成为可能,甚至以组件作者可能从未想到过的方式进行。

JSR 286定义了两种不同的协调方式,解决了两种不同的用例:

  • 活动。 支持活动通知,并具有对Portlet的操作功能。
  • 公共渲染参数。 支持跨Portlet共享视图状态。

在以下各节中,我们将更详细地解释这两种用例。

大事记

JSR 286允许Portlet发送和接收事件。 如前所述,该模型是一个松散耦合的模型,其中门户网站应用程序充当不同portlet之间的代理,并分发事件。

图2.从Portlet 1到Portlet 2和3的事件分布

图2描绘了此分发方式的详细信息。 Portlet 1定义可以在其portlet.xml部署描述符中发布一些事件A。 Portlet 2和3定义它们能够在其portlet.xml部署描述符中接收事件A。 门户网站管理员或业务用户现在已将这三个Portlet放在页面上,并在发送Portlet Portlet 1与接收Portlet Portlet 2和Portlet 3之间建立了连接。现在,用户与Portlet 1交互并触发对Portlet 1的操作。此操作的结果是,Portlet发出事件A。在Portlet完成处理事件代理程序组件(通常是门户网站运行时的一部分)后,它将寻找该事件的目标。 在我们的样本中,此事件在Portlet 1与Portlet 2和3之间存在连接。 因此,事件代理调用在Portlet 2和3上的版本2.0中引入的processEvent生命周期方法,并将事件A作为有效负载。 在动作和事件处理完成之后,呈现生命周期开始,并且Portlet可以基于在动作和事件阶段中发生的状态更新来创建新的标记。

JSR 286没有指定如何定义和管理用于Portlet协调的连接。 通常,连接是在页面创建时明确定义的,或者由门户网站在运行时自动推断出来。 第一种方法允许更多的控制和灵活性,而第二种方法则更易于使用。 在这两种情况下,门户网站都需要知道Portlet可以接收和发送的事件类型。 因此,您需要在portlet.xml部署描述符中提供此信息。 你会怎么做? 首先,提供常规事件定义,然后在portlet部分中使用带有指定Portlet可以接收或发送此事件的标签的引用来引用此定义。 每个事件由XML限定名称(QName)唯一标识。 QName是在XML中使用唯一名称的机制。 它们还具有Java表示形式,作为标准库类javax.xml.namespace.QName。 QName由名称空间(例如,http://www.ibm.com)和本地部分(例如,myEvent)组成。

清单1显示了一个示例。

清单1.发布事件定义示例
<portlet>…<supported-publishing-event><qname xmlns:x="http://com.ibm/portal/portlets/ns">
x:city</qname></supported-publishing-event></portlet><event-definition><qname xmlns:x="http://com.ibm/portal/portlets/ns">x:city</qname><value-type>java.lang.String</value-type></event-definition>

您可以为每个事件指定完整的QName,或者如果对多个事件使用相同的名称空间,则可以将其声明为默认名称空间,然后仅指定每个事件的本地部分。

除了名称之外,您还需要指定事件有效载荷的类型,以允许门户应用程序在Portlet使用不同的类加载器或者甚至可以使用Web Services for Remote Portlet作为远程Portlet的情况下对载荷进行序列化/反序列化。 (WSRP)。 因此,对有效负载的要求是它是简单类型(例如,String),或者需要是Java可序列化的,并且需要XML绑定的Java体系结构(JAXB)可序列化的。 为什么我们需要这种额外的JAXB序列化? Portlet可能希望将事件发送到使用WSRP运行的远程Portlet。 这些远程Portlet可能不是用Java编写的,因此我们需要比Java序列化更通用的机制。 JAXB允许将Java对象序列化为XML。 您要做的就是相应地注释Java对象。

这个细节是困难的部分。 现在您的工作变得越来越简单:通过portlet接收和发送事件。 您可以在ActionResponse或EventResponse上使用setEvent方法发送事件。 要接收事件,您需要扩展GenericPortlet或自己实现EventPortlet接口。 扩展GenericPortlet的另一个优势是能够使用注释来表示处理事件,处理动作以及呈现特定Portlet模式的特殊方法。

清单2显示了一个示例。

清单2.使用注释的事件处理示例
@ProcessEvent(qname="{http://com.ibm/portal/portlets/ns}city")public void cityEvent(EventRequest request, EventResponse response )throws IOException, PortletException {    Event ev = request.getEvent();if ( ev.getValue().equals("Orlando") ) {….}…}

公开渲染参数

公共呈现参数允许在不同的Portlet或其他工件(例如IBM WebSphere Portal中的主题或门户页面)之间共享请求参数。 定义公共渲染参数与定义事件非常相似:您可以在portlet.xml部署描述符中指定唯一的QName和可选的别名。 QName和别名用于连接不同portlet的公共呈现参数。 您可以将这些公共渲染参数视为共享存储区,所有参与的portlet都可以在其中设置和获取值。

图3.公共渲染参数协调示例

图3描述了一个示例,其中导航器Portlet和内容Portlet共享相同的公共参数docid。 在导航器portlet中单击将docid设置为document3的链接后,内容portlet将显示该文档的内容,因为其视图状态(当前文档的ID)已由外部影响更改。 注意,可以像在WebSphere Portal实现中一样,将公共渲染参数存储在URL中,因此,您可以为页面添加书签并使用浏览器的“后退”和“前进”按钮。

Portlet如何获得对这些公共渲染参数的访问? 在portlet本身中,可以使用与处理1.0版中定义的普通渲染参数相同的方法来访问公共渲染参数。 鉴于这些方法仅将字符串作为键而不是QName作为键,因此您需要在portlet.xml中定义一个标识符,然后可以在portlet代码中使用该标识符来解决公共渲染参数。

在大多数情况下,portlet代码实际上不需要更改渲染参数是公共的还是私有的,因此您可以通过简单地在portlet.xml中声明视图状态信息中可以合理设置的那些部分来启用Portlet进行协调。外部来源作为公共参数。 如果只想在代码中仅访问公共渲染参数,则还可以在请求中使用方法getPublicParameterMap。

服务资源

在Java Portlet Specification的第一个版本中,您不能直接通过Portlet提供动态生成的资源。 相反,您需要一个服务于资源的附加servlet。 此限制有一些缺点:

  • 您没有可用的Portlet上下文,这意味着您无权访问渲染参数,Portlet模式,窗口状态,Portlet首选项,Portlet会话等。
  • Servlet中生成的URL在门户范围之外,并且留下门户体验。 另外,门户URL中累积的门户页面的当前状态会丢失。
  • 该Servlet不在门户网站访问控制下运行,需要单独保护。

通过servlet服务资源的一个优势是开销较小,因为例如在服务大型媒体流时,请求不必通过附加门户框架。

现在,版本2.0引入了新的URL类型,即资源URL。 资源URL在ResourceServingPortlet接口上触发生命周期方法serveResource,您可以利用该方法直接在portlet中创建动态资源。

资源网址

您必须具有资源URL才能触发新的serveResource生命周期方法。 让我们开始研究新资源URL的详细信息。

可以使用RenderResponse和ResourceResponse上的createResourceURL方法来创建与其他Portlet URL相似的资源URL,例如, ResourceURL url = response.createResourceURL(); ;。

现在,您可以像其他Portlet URL一样在URL上设置参数。 您可以在serveResource调用中收到这些参数。 请注意,ResourceURLs不能设置新的呈现参数,Portlet模式或窗口状态。 发生此限制是因为serveResource调用不会生成完整的新门户页面,而是返回serveResource的响应。 因此,门户网站没有机会更新该信息可能被编码的页面的其他部分。 例如,在WebSphere Portal中,所有URL都包含此信息,因此需要更新。

您还可以在资源URL上设置其他资源ID,以清楚地标识您的资源。 如果扩展GenericPortlet,则GenericPortlet尝试转发到该资源ID以进行serveResource调用。 您可以将资源的路径设置为ID,如下所示: url.setResourceID("WEB-INF/jsp/xmlcontent.jspx");

在这种情况下,GenericPortlet自动调度到给定的JSP,JSP然后可以通过包含portlet标记库来使用portlet状态信息。 注意,portlet WAR中打包的静态资源(如GIF文件)通常应使用静态资源URL进行引用,例如: String url = response.encodeURL(request.getContextPath()+"/icons/myigif.gif");

使用portlet的serveResource方法提供静态资源会导致不必要的性能开销。

服务资源

要接收由资源URL触发的serveResource调用,您需要实现新的ResourceServingPortlet或扩展已经实现新接口的GenericPortlet。 在serveResource调用中,您将获得一个特定的资源请求/响应对。

资源请求具有与渲染请求类似的方法,但除此之外,它还使您可以访问上载数据。 在资源请求中,参数处理使您可以访问三种不同类型的参数:

  • 在资源URL上设置的资源参数仅可用于此资源调用,可通过getPrivateParameterMap调用进行访问
  • Portlet的私有呈现参数,可通过getPrivateRenderParameterMap访问
  • 公共渲染参数,可通过getPublicParameterMap调用访问

普通参数访问方法返回这三个参数映射的合并集。 具有相同键的参数首先获取资源参数值,然后合并私有或公共渲染参数值,具体取决于参数键是否在portlet.xml中声明为公共渲染参数。

使用资源URL,您可以像在渲染中一样利用所有HTTP方法,而不仅是GET。 此功能意味着您可以使用POST或DELETE之类的方法来更改serveResource调用中的状态。 但是,这些状态更改应限于Portlet的私有状态:Portlet范围内的会话数据和Portlet首选项。 您不应修改会影响其他portlet的状态,因为门户框架没有机会为serveResource调用更新门户页面的其他部分,因此对页面级状态的更新可能不可见。

注:渲染参数,portlet模式和窗口状态属于此类,因为某些门户网站实现(例如WebSphere Portal)将此信息存储在URL中,以支持在浏览器中添加书签以及使用“后退”和“前进”按钮的功能。 此实现意味着,例如,更改呈现参数需要更新页面上门户无法执行的所有URL,因为客户端上响应数据的处理完全由portlet完成。

由于从serveResource返回的标记不会与门户网站框架中的其他标记聚合在一起,因此资源响应允许对输出流进行完全控制。 例如,Portlet可以设置HTTP状态代码。

缓存资源级别

服务资源有许多不同的用例。 因此,对于缓存serveResource调用的能力也有不同的要求。 您可以使用ResourceURL上的setCacheability方法来影响资源请求的HTTP缓存行为。 此方法向门户网站提示有关目标serveResource调用需要多少信息。 因此,门户网站可以从URL删除不相关的数据,从而使URL更加稳定,并且对资源请求的HTTP缓存命中率更高。 请注意,仅当您通过为返回的响应指定HTTP缓存头来允许缓存响应时,设置资源响应的可缓存性才有意义。 此规范意味着您必须至少使用response.getCacheControl()。setExpirationTime()设置到期时间。 如果返回的内容不是特定于用户的,则应另外将缓存控件设置为公共范围。

JSR 286支持以下方案:

  • 完全可缓存的资源,不依赖于交互状态。 一个示例是通过程序生成的SVG视图,该视图取决于用于后端访问的portlet首选项。 要将serveResource调用标记为完全可缓存,您需要将资源URL的可缓存性设置为FULL。 此设置意味着门户网站可以生成一个URL,该URL不包含页面和页面上的Portlet的任何交互状态。 因此,浏览器可以至少在用户与当前页面进行交互的同时缓存serveResource调用的返回标记。

    由于未对页面和页面上的Portlet的任何状态进行编码,因此,您不能指望在Portlet中获得当前的呈现参数,Portlet模式或窗口状态。 请记住,由于缺少状态信息,因此您在serveResource的输出中受到限制:您只能包含可完全缓存的资源URL,而不能包含操作或呈现URL。

  • 完整和共享的可缓存资源,例如共享的静态资源,例如JavaScript库。 您可以通过在URL上添加其他SHARED属性(以共享资源的名称作为值)来将FULL可缓存的服务资源URL标记为共享。 该名称应该是一个QName,用于唯一标识资源,例如{http://dojotoolkit.org/v1.0}dojo.js。

    指定共享属性可使门户网站每页仅使用这种共享资源的一个版本。 如果门户网站尚未加载资源,它仍然需要portlet将这些资源打包在其WAR文件中。

    利用Portlet不需要提供的门户网站资源是特定于供应商的。 例如,在WebSphere Portal中,您可以使用ResourceURLAccessor创建资源URL。

  • Portlet级可缓存资源取决于Portlet交互状态,例如,当前Portlet视图的动态生成的PDF输出。 如果您需要访问portlet状态,例如render参数,portlet模式或窗口状态,但是您不想在响应或以后的响应中放置操作或呈现URL,则将可缓存性级别设置为PORTLET 。 该级别允许刷新与Portlet进行的每次交互的资源,但是还允许在与页面上的其他Portlet进行交互时使用缓存的资源。
  • 页面级可缓存资源,例如提供操作或呈现URL的资源,例如,为Ajax调用返回的标记。 PAGE级别的可缓存性是默认设置。 您的serveResource方法没有任何限制,这意味着您可以生成操作或呈现链接。 这也意味着您无法在与门户网站页面进行的任何交互中缓存您的资源,从而改变了页面状态的一部分。

总之,这些可缓存性级别使您能够向运行时提供尽可能多的有关可缓存性的提示。 运行时还可以通过其他方式增强资源的可缓存性,例如,通过跟踪客户端上的状态更改并仅重新呈现受状态更改影响的部分,可以提高资源的可缓存性。 一个示例是WebSphere Portal V6.1 Beta中的“客户端聚合”主题。

使用Ajax

在1.0版中,对Ajax用例的支持非常少:您只能提供一个额外的servlet,以服务于Ajax片段。 但是,然后直接而不是通过门户框架对这个servlet进行寻址,因此它没有提供portlet状态,也没有受到门户的安全保护,如图4所示。 在portlet和servlet之间共享数据的唯一方法是使用应用程序作用域会话或调用servlet的URL上的参数。

图4. JSR 168中的Ajax解决方案:使用附加的servlet服务Ajax数据

新的Portlet规范如何支持Ajax用例?

如我们所见,JSR 286为您提供了直接使用Portlet服务资源的方法。 因此,您可以向ResourceURLs发出XmlHttpRequests,并在服务器端获得对Portlet上下文的完全访问权限,例如渲染参数,Portlet模式,窗口状态,Portlet首选项和Portlet会话。

我们还了解到,您可以在serveResource调用中进行一些状态更改:

  • 更改Portlet首选项
  • 在Portlet会话范围内更改数据

现在,您可以实现其他用例,例如异步更新,以使您的用户界面更具响应性。

图5. JSR 286中的Ajax解决方案:直接从Portlet提供Ajax数据

图5描述了JSR 286中的解决方案。您可以看到Ajax调用通过门户网站servlet,因此处于门户网站的控制之下。 您还可以利用资源URL提供指向JavaScript库的链接。

缩小与Servlet编程模型的差距

与Servlet编程模型相比,Java Portlet规范的第一个版本在某些方面限制了Portlet编程模型。 进行此限制是因为Portlet聚集在页面上,并且所有假定您是页面上唯一组件的概念都不容易应用于Portlet编程模型。

第二个版本解决了这些问题并提供了解决方案,以使Portlet编程模型具有与Servlet几乎相同的功能,以及Portlet特定的扩展。

Cookies,文档标题部分元素和HTTP标头

在Java Portlet Specification的第一个版本中,Portlet不能对门户页面在其Portlet窗口之外的部分作出贡献。 在第二个版本中,您可以设置cookie,文档头部分元素(例如,HTML元元素,链接或样式)和HTTP标头(例如,特定于应用程序的Pragma标头)。

两部分渲染生命周期调用
为了解决版本1.0面临在当前portlet窗口之外提供内容的问题,版本2.0添加了一个由两部分组成的render调用:

  1. RENDER_HEADER部分,允许portlet返回当前portlet窗口之外的内容,例如portlet标题,首选的下一个可能的portlet模式,cookie,文档头部分元素和HTTP标头。
  2. RENDER_MARKUP部分,它允许portlet返回其常规标记。

这两个部分是必需的,因为某些门户网站实现(例如WebSphere Portal)将页面和portlet输出直接流式传输到客户机以避免缓冲开销。 在这种情况下,portlet呈现无法将任何内容添加到文档的头部,因为头部已经被写入。 因此,想要贡献头部分或设置portlet标题的portlet应该使用portlet部署描述符中清单3所示的设置打开由两部分组成的呈现功能。

清单3.两部分渲染生命周期支持
<portlet>
…<container-runtime-option><name>javax.portlet.renderHeaders</name><value>true</value></container-runtime-option></portlet>

如果扩展GenericPortlet并覆盖GenericPortlet提供的方法(如getTitle和getHeaders),则实现两部分呈现非常简单。 GenericPortlet类为您完成其余工作。

使用cookie
您可以使用以下代码在每个生命周期方法(processAction,processEvent,render和serveResource)的response.addProperty(javax.servlet.http.Cookie cookie)response.addProperty(javax.servlet.http.Cookie cookie)

然后可以使用以下方法在所有生命周期方法中访问cookie: request.getCookies()

在portlet中使用cookie与在servlet中使用cookie的不同之处在于:

  • 如前所述,要在render方法中设置cookie,应打开renderHeaders选项,并应使用例如GenericPortlet的doHeaders()在RENDER_HEADERS部分中设置cookie。
  • Cookie可能无法在客户机上访问,因为它们存储在门户网站服务器上,或者当Portlet通过WSRP作为远程Portlet运行时,它们被放置在不同的名称空间中。
  • Cookie不能保证在不同的Portlet之间共享。

请求分派器包括和转发

在Java Portlet Specification的第一个版本中,您只能选择在呈现生命周期调用中包括servlet或JSP。 第二个版本允许您使用转发和包含,并允许您在所有生命周期方法中使用它们。

此添加意味着您现在可以分派到用servlet编写的操作或事件逻辑,或者在提供资源尝试转发到您在ResourceURL上设置的资源ID时可以进行转发。

利用Servlet生命周期侦听器

在V1.0中,由于PortletSession是HttpSession之上的外观,因此Servlet HttpSession侦听器也可以用于PortletSession。 2.0版将列表扩展到Java Servlet规范V2.5中定义的所有servlet生命周期侦听器,并且在portlet和servlet对象之间建立了对应关系:

  • javax.servlet.ServletContextListener。 用于有关servlet上下文和相应的portlet上下文的通知。
  • javax.servlet.ServletContextAttributeListener。 用于有关servlet上下文或相应的portlet上下文中的属性的通知。
  • javax.servlet.http.HttpSessionActivationListener。 用于有关HTTPSession或相应PortletSession的激活或钝化的通知。
  • javax.servlet.http.HttpSessionAttributeListener。 用于有关HTTPSession或相应PortletSession的外观的通知。
  • javax.servlet.http.HttpSessionBindingListener。 用于通知有关将对象绑定到HTTPSession或相应的PortletSession的通知。
  • javax.servlet.ServletRequestListener。 用于通知有关当前Web应用程序的HTTPServletRequest或镜像portlet请求的更改。
  • javax.servlet.ServletRequestAttributeEvent。 用于通知有关更改当前Web应用程序的HTTPServletRequest或镜像portlet请求的属性的通知。

Servlet请求侦听器可以通过查看请求属性javax.portlet.lifecycle_phase来将针对Servlet的普通Servlet请求与针对Portlet的包装Servlet请求进行区分。 此属性是在针对Portlet的请求上设置的,指示此请求的当前Portlet生命周期阶段。

对所有这些生命周期侦听器的访问为您提供了许多挂钩点,用于管理与这些生命周期相关的对象,这对于许多框架来说都是一个很棒的功能。 但是,这些挂钩点要付出一定的代价,尤其是请求生命周期侦听器,这会增加每个请求的大量处理开销。 因此,请小心使用它们。

扩展JSR 286

JSR 286的专家组致力于使JSR 286具有可扩展性,以便您可以以无创且独立于容器的方式在JSR 286的基础上在我们的框架中添加特性和功能。 你可以加:

  • Servlet生命周期侦听器
  • Portlet过滤器,用于包装请求/响应
  • PortletURL侦听器,用于在将URL写入输出流之前对其进行处理
  • Portlet管理的模式,允许Portlet提供自己的Portlet模式

一些扩展点要求容器支持此扩展点,Portlet才能利用扩展。 这些扩展点是:

  • V1.0中的请求/响应属性,用于根据请求或响应设置扩展属性。 2.0版添加了一些新的规范定义的扩展属性,例如预定义的缓存属性。
  • 用于扩展PortletURL的PortletURL属性,例如ResourceURLs的预定义SHARED属性。
  • 容器运行时选项,用于利用其他容器行为,例如两部分渲染。

让我们在下一部分中更深入地研究扩展Java Portlet规范的新功能。

Portlet过滤器

新的Portlet过滤器功能使您可以在Portlet的所有生命周期调用周围插入过滤器。 按照通用装饰器模式,过滤器可以进行预处理或后处理,并且它们可以修改或包装传递到Portlet的请求和响应对象。 Portlet过滤器的典型应用包括:

  • 将来自其他来源的信息作为属性或参数传递给Portlet
  • 输出过滤,用于安全实施或标记合规性
  • 收集诊断信息
  • Web应用程序框架之间的桥接

例如,WebSphere Portal V6.1使用过滤器方法,通过附加的语义信息来扩展Portlet的标记,以实现客户端的即点即用。

Portlet过滤器编程模型是基于servlet过滤器模型建模的:

  1. 在部署描述符中定义过滤器。 使用<filter>元素可以完成此定义,您还需要在其中声明应将过滤器应用到的生命周期调用。
  2. 在您的过滤器中实现相应的过滤器接口。 您还可以列出多个生命周期条目,并在您的类中实现多个Filter接口。
  3. 提供一个filter-mapping元素,您可以在其中描述应将过滤器应用到哪些portlet(如果应将星号用作应用程序中的所有portlet,也可以使用星号作为通配符)。

部署描述符中的过滤器映射元素的顺序还定义了应用于portlet的过滤器的顺序。 清单4显示了一个部署描述符条目的示例。

清单4. Portlet过滤器定义示例
// filter declaration
<filter><filter-name>PortletFilter</filter-name><filter-class>com.example.PortletFilter</filter-class><lifecycle>RENDER</lifecycle>
</filter>// filter mapping
<filter-mapping><filter-name>PortletFilter</filter-name><portlet-name>MyPortlet</portlet-name>
</filter-mapping>

在运行时,所有过滤器的过滤器链将应用于portlet。 每个过滤器都会获取当前的请求和响应,或者获取由先前的过滤器创建的包装版本以及过滤器链。 在完成预处理之后,过滤器实现可以终止请求处理或调用过滤器链中的下一个元素,传入接收到的请求和响应或其他包装。 过滤器链中的最后一个元素是Portlet本身。

清单5是一个过滤器的示例,该过滤器执行一些预处理和后处理,并为Portlet提供包装请求。

清单5.示例过滤器实现
public class PortletFilter implements RenderFilter {public void doFilter(RenderRequest req, RenderResponse resp, FilterChain chain) throws .. {PrintWriter pw = resp.getWriter();pw.write("Pre-processing");MyRenderResponseWrapper resWrapper = new MyRenderResponseWrapper(res);chain.doFilter(req, resWraper);pw.write("Post-processing");}
}

请注意,在使用包装器时,应坚持使用包装器模式,并且仅覆盖现有行为。 不要在包装器上添加新方法; 链中可能还有其他您不知道的包装器,因此Portlet可能无法访问您的新方法。 如果要向Portlet提供附加功能,请将提供对这些功能的访问权限的对象设置为请求属性。

Portlet URL侦听器

在某些情况下,您可能需要集中管理在Portlet URL上设置特定属性,或者增强现有Portlet的Portlet URL创建。 一个示例涉及实现资源到共享ID的映射。 您只能在侦听器中实现一次此映射,然后侦听器将检查资源URL是否以共享QName存在的资源为目标,然后将SHARED属性设置为相应的QName。 对于这些用例,JSR 286规范为您提供了Portlet URL侦听器。

You need to register such a listener with the listener element in the portlet deployment descriptor, and your class needs to implement the PortalURLGenerationListener interface that defines a callback method for each type of portlet URLs: action, render, and resource.

Portlet-managed modes

In JSR 168, the portlet could leverage only portlet modes that are supported by the portal framework running the portlet. In some use cases, the portlet wants to offer portlet-specific functionality with the same user look-and-feel as portlet modes supported by the portal (for example, with context menus on the portlet window). For example, a ShowShoppingCart portlet mode lists all entries that you currently have in your shopping cart.

To support these use cases, JSR 286 introduces the portlet-managed modes that are not known to the portal, but are managed by the portlet itself. The portlet can declare such a mode in the portlet deployment descriptor with the code shown in listing 6.

Listing 6. Defining a custom portlet managed mode
<custom-portlet-mode><description>Show shopping cart</description>
<portlet-mode>ShowShoppingCart</portlet-mode><portal-managed>false</portal-managed></custom-portlet-mode>

The important point here is to set the portal-managed mode to false. This setting indicates to the portal that it should treat this mode just like the standard View mode concerning all aspects, including how it provides the portlet with preferences and from the perspective of access control. The portal should provide UI controls that allow the portlet to switch to this mode. The portlet can define localized names for the decoration using the resource bundle entry: javax.portlet.app.custom-portlet-mode.<name>.decoration-name.

Because the portal does not know specific semantics of the portlet-managed modes, it does not know when it makes sense to present a decoration to switch to a portlet-managed mode and when it does not. Therefore, the JSR 286 specification lets you indicate for which portlet modes the portal should display UI controls as part of the render response using the method setNextPossiblePortletModes. When you use GenericPortlet, you can override getNextPossiblePortletModes, and GenericPortlet takes care of setting these in the RENDER_HEADER part. Note that you need to set this list of modes on each response and that you should enable the container runtime option javax.portlet.renderHeaders mentioned in the next section.

Container runtime options

Container runtime options allow the portlet to supply specific options to the portlet container that either change default behavior defined in the Java Portlet Specification or add additional behaviors. We've already seen an example of such a container runtime setting: the renderHeaders option. JSR 286 defines a list of container runtime options that are all optional except for actionScopedRequestAttributes. In addition to these options, specific portlet container implementations may provide their own options.

You indicate that your portlet requires a specific container runtime option in the deployment descriptor using the code shown in listing 7.

Listing 7. Container runtime option template
<portlet>
…<container-runtime-option><name>NAME_OF_THE_OPTION</name><value>optionally you can have one or more parameters</value></container-runtime-option></portlet>

These options are predefined in JSR 286:

  • javax.portlet.escapeXml. Allows you to turn off the XML escaping of portlet URLs for each default, in case you have written a JSR 168 portlet that assumes that URLs are not XML escaped and have migrated this to a JSR 286 portlet.
  • javax.portlet.renderHeaders. Enables the two-part rendering mechanism that lets you set headers in streaming-based portal implementations.
  • javax.portlet.servletDefaultSessionScope. Allows you to change the default scope of the session object provided to servlets or JSPs that are called from the portlet through forward or include from application scope to portlet scope. That way, these resources access the same session data using portlet and servlet APIs, which is particularly convenient if your JSPs are written for a servlet-based Web framework.
  • javax.portlet.actionScopedRequestAttributes. Allows Web frameworks to pass complex objects from the action or event phase to the render phase through the request. You are able to access these attributes until the next request with an action semantic (which may be an action triggered through an Action URL or an event) occurs. This feature likely is implemented by the portlet container by storing these attributes in the session. Therefore, use this feature only if you cannot avoid it, as it will probably cause some performance degradation. This option is the only one that JSR 286 requires to be supported by all containers.

Note that if you use a container runtime option and the container on which your portlet is deployed does not support this option, the container may reject the deployment of the portlet. Use these options with care if you want to develop a portlet that can run on many different portlet container implementations.

向后兼容

Java Portlet Specification V2.0 was designed to avoid breaking binary code compatibility with V1.0 and to maintain compatible behavior for all API methods. This design point means that all portlets written against the V1.0 specification should run unchanged on a V2.0 container. The only exceptions to this rule are the following slight behavior changes that normally should not break any portlets:

  • RenderResponse.setContentType is no longer required before calling getWriter or getOutputstream. Calling getWriter or getOutputstream without previously setting the content type no longer results in an IllegalStateException in V2.0.
  • getProtocol for included servlets / JSPs no longer returns null, but instead returns HTTP/1.1 in V2.0.

This backward compatibility statement also includes the deployment descriptor in which V2.0 added new entries, but did not change existing ones. This behavior means that you can turn most JSR 168 portlets into a JSR 286 portlet by changing the one line in the portlet deployment descriptor that references the schema to the new V2.0 portlet deployment descriptor schema.

In this section, we cover various smaller additions that can make your life easier and that can support new use cases, such as the new Java 5 features leveraged in the API, the new caching features, the changes in the runtime IDs that you can access, and the tag lib additions.

Java 5 features

JSR 286 leverages some of the new Java 5 features:

  • Using Generics
  • Introducing a new enum class for all user profile attributes

GenericPortlet also lets you use the following annotations to make dispatching life-cycle calls to specific handler code easier:

  • ProcessAction. This annotation allows you to annotate a method that should process a specific action. To allow dispatching, the Action URL needs to include the parameter javax.portlet.action.
  • ProcessEvent. This annotation allows you to annotate methods that should process specific events.
  • RenderMode. This annotation allows you to annotate methods that should render a specific portlet mode.

Listing 8 shows how you use the ProcessAction annotation.

Listing 8. Leveraging the ProcessAction annotation
// generating the URLPortletURL url = response.createActionURL();url.setParameter(ActionRequest.ACTION_NAME, "actionA");url.write(output);// method for handling the action@ProcessAction(name="actionA")void processMyActionA(ActionRequest req, ActionResponse resp)throws PortletException, java.io.IOException; {…}

Because some portal implementations still depend on Java 1.4, only features that can easily be removed for Java 1.4-based platforms have been added to JSR 286. For Java 1.4-based platforms, a JAR file of the JSP 286 APIs compiled with Java 1.4 compliance settings in which the preceding features have been removed.

New caching features

JSR 286 adds new caching features that help making portlets scale better:

  • Public caching scope, which marks cache entries shared across users
  • Support for multiple cached views per portlet
  • Validation-based caching for validating expired cache entries

In JSR 168, all cache entries were private for each user. If you have portlets that are not customizable and don't display different content for different users, such as a news-of-the-day portlet, then you get a cache entry for each user, even if all entries are the same. Now, in JSR 286, you can tell the portlet container that the cache entry can be shared using the cache-scope element in the deployment descriptor. Thus, you can dramatically reduce the memory footprint for these portlets, as the code in listing 9 shows.

Listiing 9. Public caching scope example
<portlet>...<expiration-cache>60</expiration-cache><cache-scope>public</cache-scope>...</portlet>

A portlet can also set the cache scope programmatically using a response property or the new CacheControl interface. We recommend that you do not change the cache scope programmatically, though, as it may be difficult for portlet containers to ensure that cache entries are invalidated correctly if your portlet mixes responses with cache scope public and private for the same user.

The JSR 168 specification demanded that portlet containers must invalidate the cache for each user interaction with a portlet (render or action URL), so that there could be only a single cache entry per portlet window and user. With the introduction of public render parameters and shared caching, the specification has been enhanced to allow the container to cache multiple portlet views based on render parameters. This enhancement means that you can use render parameters so that users can navigate between different views in a shared cacheable portlet while the output still remains cached. Only the action and event processing calls, which can cause side-effects that are not known to the container, now require a cache invalidation.

Validation-based caching is useful for situations in which you do not want to recompute the markup of a portlet often because it is an expensive operation. Ideally, set the expiration time to a high value so that the markup is cached for a long time. On the other hand, you may want to respond quickly to changes in your back-end system and provide an updated view of the portlet. Validation-based caching solves this dilemma: It allows you to define a short expiration time so that your portlet gets called often and can check for changes in the back-end system. If nothing has changed in the back-end system, you validate that the expired cache content is still valid to use by setting the CacheControl. setUseCachedContent(true) and setting a new expiration time. If something has changed in the back-end system, then you produce new markup and set the setting to false.

How do you know which back-end state the currently cached markup maps to? You can set a specific validation token, called ETag after the same tag in the HTTP specification, on the response. The portlet container can then provide you with this ETag in the request of the next render or serveResource call, indicating that it still has the cached content available, which can be revalidated.

An example of using validation-based caching is shown in listing 10.

Listing 10. Validation-based caching example
protected void doView (RenderRequest request, RenderResponse response)throws PortletException, java.io.IOException
{if ( request.getETag() != null ) {  // validation requestif ( markupIsStillValid(request) { // markup is still validresponse.getCacheControl().setExpirationTime(30);response.getCacheControl().setUseCachedContent(true);// no need to write any outputreturn;}}// create new content with new validation tagresponse.getCacheControl().setETag(computeETag(request));response.getCacheControl().setExpirationTime(60);PortletRequestDispatcher rd = getPortletContext().getPortletRequestDispatcher("jsp/view.jsp");rd.include(request, response);
}private boolean markupIsStillValid(PortletRequest request) {// check if backend state still matches validation tokenreturn computeETag(request).equals(request.getETag())
}private String computeETag(PortletRequest request) {// return some backend state indicator like a last update timestamp
}

Using validation-based caching is beneficial only if the operations for creating the markup are expensive compared to the operations for checking the back-end state. If, for example, the request to the back-end server takes up 90 percent of the time to render the portlet, it does not make sense to use validation-based caching to save only the remaining 10 percent rendering time.

Runtime IDs

Runtime IDs can be used to scope data that your portlet handles, so that you can avoid collisions between multiple occurrences of a portlet within the same portal, and maybe even on the same page. Most portlet API objects, for example, portlet session or portlet preferences, do implicit namespacing, but you need to do explicit namespacing when you access shared data stores or want to assign unique IDs to output elements. This practice is common for Ajax applications, which can particularly benefit from the two enhancements that version 2.0 provides in this area.

First, JSR 286 extended the lifetime of the namespace runtime ID, using the response.getNamespace method, from being valid for only one request to now being stable for the lifetime of the portlet window. This extension now allows you to use the namespace to also crate namespace form IDs if, for example, your portlet is intended to be aggregated in a forms-based portal server. You can also leverage the namespace for JavaScript function calls that are embedded in Ajax responses, and you can reuse namespaced JavaScipt functions provided by a previous render call.

Second, JSR 286 introduced a new API call that allows you to get to a unique ID for the portlet window using the request.getWindowID method. This call now allows you to use this ID as key for data that you want to create namespace per portlet window; for example, the portlet wants to cache data that it received from a back-end system per portlet window. Note, however, that no life-cycle calls are defined around the portlet window, so if you use the portlet window ID for specifying namespace entries in a persistent data store, you also need to clean up these entries yourself.

Taglib additions

The JSR 286 tag library has its own namespace so that new additions do not interfere with portlets using the old JSR 168 tag library. You now need to include the new tag library with: <%@ taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet" %>

The define objects tag is now enhanced and gives you the following additional variables beyond the request, response, portletConfig from the V1.0:

  • portletSession. For access to the portlet-scoped session.
  • portletSessionScope. For access to the portlet-scoped session attribute key/values map.
  • portletPreferences. For access to the portlet preferences.
  • portletPreferencesValues. For access to the portlet preferences key/values map.

Each of the URL generation tags has the following additional attributes:

  • copyCurrentRenderParameters. For copying the current private render parameters to the URL.
  • escapeXml. For turning the default XML escaping of the URL. As mentioned previously, in JSR 168 it was not defined if a URL is XML-escaped or not. JSR 286 now has defined the same behavior as the Java Standard Tag Library: by default all URLs are escaped, but you can turn it off using the escapeXml attribute.

For action URLs, there is also the additional name attribute that allows you to set the javax.portlet.action parameter that is evaluated by the GenericPortlet for dispatching to the ProcessAction annotated methods.

Other additions made in the JSR 286 specification include these:

  • Adding a new resourceURL tag for generating resource URLs
  • Adding the new propertyTag that can be used inside portlet URL tags for attaching properties to URLs

结论

As you have seen, the second version of the Java Portlet Specification added a lot of new content and abilities. The specification and the APIs more than doubled compared to version 1.0. Now the Java Portlet Specification has grown up, and it allows you to implement most use cases without the need to have vendor extensions. Note that some of the features described in this article are optional, so they may not be supported on all JSR 286-compliant platforms, but the standard ensures that those platforms that provide extended capabilities do so in a consistent and well-defined manner.

The portlet programming model now provides you with events and public render parameters so that you can build larger composite applications out of your portlets and reuse your portlets in different scenarios. Finally, you get better support for Ajax-based use cases with JSR 286 as you are now able to serve resources directly though the portlet.


翻译自: https://www.ibm.com/developerworks/websphere/library/techarticles/0803_hepper/0803_hepper.html

portlet 2.0_Java Portlet规范V2.0(JSR 286)中有哪些新功能?相关推荐

  1. 2021软件保护系统WinLicense v3.1.1.0最新版17项新功能邀你体验!

    随着软件普及程度.互联网技术的发展,以及正版软件购买用户数量和软件版本的增加,软件的保护变得越来越重要.而我们常见的软件保护方式有软件授权和软件加密. WinLicense是一个功能强大的保护系统,专 ...

  2. Fluent Operator v2.0 发布:Fluent Bit 新的部署方式——Fluent Bit Collector

    2019 年 1 月 21 日,KubeSphere 社区为了满足以云原生的方式管理 Fluent Bit 的需求开发了 FluentBit Operator.此后产品不断迭代,在 2021 年 8 ...

  3. Android 8.0正式发布 奥利奥新功能惊人

    尽管 Android 刷版本号的步伐年年加快,但每年带给用户的惊喜从未减少.经历了四个开发者预览版的洗礼后,Android 8.0 终于在今天凌晨迎来了正式版发布. 如果你还不清楚 Android O ...

  4. 太强了!Scikit-learn 0.22新版本发布,新功能更加方便

    ☞500g+超全学习资源免费领取 作者:xiaoyu,数据爱好者 Python数据科学出品 Scikit-learn此次发布的版本为0.22.我浏览了一下,此次版本除了修复之前出现的一些bug,还更新 ...

  5. netbeans6.8_NetBeans IDE 8.0和Java 8的新功能

    netbeans6.8 NetBeans IDE 8.0已发布,还为Java 8技术提供了新功能. 它具有用于与Java SE 8,Java SE Embedded 8和Java ME Embedde ...

  6. NetBeans IDE 8.0和Java 8的新功能

    NetBeans IDE 8.0已发布,还为Java 8技术提供了新功能. 它具有用于与Java SE 8,Java SE Embedded 8和Java ME Embedded 8配合使用的代码分析 ...

  7. 微信8.0新版本上线,这些新功能你知道吗?

    好家伙,昨天微博首页很多人都进入了叙利亚模式,我们来看微博战况. 令网友产生了疑问:这是叙利亚战场吗? 原来,和之前全民热衷的"拍一拍"一样,微信又发布新功能了.新功能中微信表情更 ...

  8. 一公司C#编程规范v2.0(转)

     C#编程规范 Version 2.0 目录 第一章 概述      4 规范制定原则      4 术语定义      4 Pascal 大小写      4 Camel 大小写      4 文件 ...

  9. iVX移动端应用开发指导与规范v2.0

    序言 iVX是一个高度灵活的应用开发工具,实现同一个功能,有各种不同的实现方法.为了提高项目在一个团队内部的可读性与可维护性,并确保项目运行时的性能,我们总结了30+个移动端项目,提出了一套最佳实践方 ...

最新文章

  1. Ubuntu16下安装kaldi(使用物理主机)
  2. SRM596 DIV2 250
  3. 单片机控制24v电压_最全变频器控制端子接线方法和技巧
  4. go 捕获数据库新增数据_更改数据捕获的经验教训
  5. java 定时器代码_Java定时器代码的编写
  6. SQL Server 中系统视图sysobjects中type字段的说明
  7. 昆西·拉森的净资产是多少?
  8. 在C++中用虚函数的作用是什么?为什么要用到虚函数
  9. mac 使用远程连接
  10. python 3中 的subprocess
  11. MySql如何使用索引(二)
  12. 零基础可上手 | 手把手教你用Cloud AutoML做毒蜘蛛分类器
  13. 二相步进电机和三相步进电机有什么区别?
  14. visual studio 2015中的webapi生成helpPage,页面不显示方法说明问题解决
  15. 计算机专业暑假实践心得7篇,计算机专业实习周记总结10篇
  16. 课设-基于51单片机+超声波模块的避障小车(源码+原理图+Protel仿真)
  17. 目标优化之帕累托最优
  18. 饥饿游戏3:嘲笑鸟(上)[The Hunger Games:Mockingjay - Part 1]
  19. 上半年晋升 P8 成功,还买了别墅!
  20. 极域课堂分发文件与一键开关机教程

热门文章

  1. 普相女、经济适用男…… 这些男女新词,你知道吗
  2. 大四Java实习刚到公司什么都不会该咋办
  3. 借《腾讯传》加深对腾讯的理解
  4. 3D Xpoint技术与NAND Flash、3D NAND Flash及DRAM的比较
  5. easyexcel实现多sheet导出(全网最简单)
  6. 贝壳c++开发工程师春招一面面经
  7. MyBatis实现模糊查询
  8. android Tombstone 流程
  9. 微信小程序定位当前城市
  10. 拖死项目的不是团队,可能是失败的管理