Important new features -
1. Java 5 based and alignment with J2EE 1.4 and WSRP 2.0
2. Portlet Coordination
I. Portlet Events
a) Publishing Event
b) Event source as live text
II. Public Render Parameters
3. Resource Serving - Direct Portlet access through ResourceURLs
4. Portlet Filters
5. Portlet URL listeners
6. Portlet-managed modes
7. Extended Caching
I. private/shared
II. expiration/validation
8. Closing the gap to the servlet programming model
I. Support for page <head> contributions and Cookies
II. Leveraging the servlet life-cycle listener
9. Better web framework support
10. Container runtime options
11. Some more new features
I. Taglib additions
II. Namespacing
1) Java 5 based and alignment with J2EE 1.4 and WSRP 2.0
● Java 5 and annotation support in GenericPortlet
● API access to the Portlet Window ID
● Portlet managed Modes and setting nextPossiblePortletModes
● PORTLET_SCOPE session attributes for dispatched Servlets
● Providing ActionRequest attributes to the RenderRequest
● PortletURL generation callback listeners
// generating the URL PortletURL 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; { … } |
Use this hidden parameter when submitting a html form
<INPUT type="hidden" name="javax.portlet.action" value="addOrder">
2) Portlet Coordination
i) Portlet Events
Events enable portlets to communicate with each other by sending and receiving events. An event is a life cycle operation that occurs before the rendering phase. Events can be described as a loosely coupled, brokered means of communication between portlets. Events allow portlets to respond on actions or state changes not directly related to an interaction of the user with the portlet. A portlet can declare events in its deployment descriptor by using the event-definition element in the portlet application section.
In the portlet application section, each portlet specifies the events it would like to publish through the supported-publishing-event element and the events it would like to process through the supported-processing-event element. The supported-publishing-event and supported-processing-event elements must reference the event name defined in the portlet application section in a event-definition element.
The portlet creates events using the setEvent() method during action processing. This will be processed by the portlet container after the action processing has finished. To receive events, the portlet must implement the javax.Portlet.EventPortlet interface. The portlet container calls the processEvent() method for each event targeted to the portlet with an EventRequest and EventResponse object. The portlet can access the event that triggered the current process event call by using the EventRequest.getEvent() method. This method returns an object of type Event encapsulating the current event name and value.
a) Publishing Event
A portlet specifies which events it wants to receive or send
<defaultnamespace>
</defaultnamespace>
<eventdefinition>
<name>foo</name>
<alias xmlns:y=”http://acme.com/events1.0/”>y:foo</alias>
<valuetype>java.lang.String</valuetype>
</eventdefinition>
<portlet>
<portletname>PortletA</portletname>
...
<supportedprocessingevent>
<name>foo</name>
</supportedprocessingevent>
<supportedpublishingevent>
<qname xmlns:x=”http://acme.com/events2.0”>x:foo</qname>
</supportedpublishingevent>
</portlet>
Portlet Events example
public void processAction(ActionRequest request, ActionResponse response) throws PortletException, IOException {
try {
QName qName = new QName("http://acme.com/events-2.0", "foo", "x");
com.acme.Foo foo = new com.acme.Foo();
// initialize Foo
response.setEvent(qName, foo);
} catch (Exception e) {
// handle exception
e.printStackTrace();
}
}
@javax.portlet.ProcessEvent(qname = "{http://acme.com/events-2.0}foo")
public void handleFooEvent(EventRequest request, EventResponse response throws PortletException, IOException {
javax.portlet.Event event = request.getEvent();
com.acme.Foo foo = (com.acme.Foo) event.getValue();
// process Foo
}
WebSphere Portal extends this model with an interportlet communication mechanism in which propagation of information is explicitly controlled by the user: We call this mechanism the click-to-action model.
The idea is that instead of publishing an event during the action phase, a portlet can embed event information into its markup using special HTML constructs for enabling an event source as live text. This event source then becomes an active hotspot in the browser: When clicked, the event source dynamically collects all matching processing targets on the page into a menu that is displayed to the user. The user can then select a processing action for the information that is executed.
<%-- live text markup enables this date as an event source --%>
<TD>
<SPAN class="c2a:source">
<SPAN class="c2a:typename" style="display:none">
</SPAN>
<SPAN class="c2a:anchor">
<fmt:formatDate value="${order.shipdate}" type="date" dateStyle="full"/>
</SPAN>
<%-- the actual event payload needs a different formatting than the displayed date --%>
<SPAN class="c2a:value" style="display:none">
<fmt:formatDate value="${order.shipdate}" type="date" pattern="yyyy-MM-dd"/>
</SPAN>
</SPAN>
</TD>
ii) Public Render Parameters
A public render parameter is handled almost identically to an ordinary (private) render parameter: The portlet can set and read this parameter using the same API methods that JSR 168 introduced for private render parameters. From a programmer's point of view, the important difference is that a public render parameter is declared in the portlet.xml deployment descriptor and therefore becomes an external interface of the portlet.
<public-render-parameter>
<identifier>item</identifier>
<qname xmlns:x="http://sun.com/params">x:item</qname>
</public-render-parameter>
<supported-public-render-parameter>item</supported-public-render-parameter>
//setting value to public render parameter
response.setRenderParameter("item", item);
//retrieveing value from public render paramete
request.getParameter("item")
request.getPublicParameterMap().get("item")
Obviously, there are cases in which this sort of global information sharing is not desired. A common example is the case where you have two pairs of collaborating portlets such as a navigator and a viewer on separate pages. You want the navigator on page 1 to control the viewer on the same page but without affecting the viewer on the other page. In WebSphere Portal V6.1, you can control this behavior by limiting the sharing scope for public parameters to a page. To do so, go to Edit page settings for that page and set param.sharing.scope (under Advanced options - I want to set parameters) to a non-empty value such as scope1. All portlets that are placed on that page now use their own shared values for their declared render parameters, so they can still share information, but they cannot affect portlets on other pages.
The public.param.scope page setting
When you set the page setting to the same value scope1 for another page, that page also becomes part of the same sharing scope. Generally, two portlets share the value for a public render parameter if and only if the following conditions are met:
· They declare the parameter with the same global name in their portlet.xml deployment descriptor.
· They are placed on the same page or on pages that have the same value for the param.sharing.scope setting.
The global sharing scope that we have seen in the beginning is a special case where the page setting is empty.
3) Resource Serving - Direct Portlet access through ResourceURLs
Resource serving gives you full control over all aspects of the HTTP protocol. WebSphere Portal writes out all response properties that you specify on a resource response as HTTP headers, so you can control language, content type, and other information for the provided content. The flip side is that, in contrast to normal page requests, the portal does not provide any default header information for the response; all information must be explicitly set during resource serving.
How to Create a Resource URL ?
A resource url can be generated by invoking createResourceURL() method of the RenderResponse object in render phase of the portlet or doView() method of the Generic Portlet.
The resouce url can also be created inside serveResource() method of the Portlet. In that case portlet developers need to call createResourceURL() method on ResouceResponse object passed to the serveResource() method.
In the following code snippet we will create a resource url inside the doView() and then we will set that url as a IMG source in the rendered output.
public void doView(RenderRequest request, RenderResponse response) throws PortletException, IOException {
response.setContentType("text/html");
PrintWriter writer = response.getWriter();
PrintWriter writer = response.getWriter();
ResourceURL resURL = response.createResourceURL();
resURL.setResourceID("image");
writer.println("<IMG src=\"" + resURL+ "\" >");
resURL.setResourceID("image");
writer.println("<IMG src=\"" + resURL+ "\" >");
//Render other markups
}
So when the browser loads the portal page to show the view mode of the portlet, it automatically sends the request for the resURL which goes through portal and finally serveResouce() of GenericPortlet is called to serve the resource (in this case "image").
The serveResource() for this example should look like below
public void serveResource(ResourceRequest resRequest, ResourceResponse resResp) throws PortletException, IOException {
String resourceID = resRequest.getResourceID();
resResp.setContentType("image/png");
byte[] b = getImage("MyImage.PNG"); //Returns image bytes
resResp.getPortletOutputStream().write(b);
byte[] b = getImage("MyImage.PNG"); //Returns image bytes
resResp.getPortletOutputStream().write(b);
}
4) Portlet Filters
A portlet filter is a Java technology-based component that can be used to modify the content of the portlet request and portlet response before or after any life cycle method of the portlet.
The portlet API defines the following interfaces for creating portlet filters:
· javax.portlet.filter.ResourceFilter - For serveResource method
· javax.portlet.filter.RenderFilter - For render method
· javax.portlet.filter.ActionFilter - For processAction method
· javax.portlet.filter.EventFilter - For processEvent method
Very similar to Servlet Filters both in definition and API
● Filters can be applied to multiple lifecycles
● Filters can be chained like Servlet Filters
● Wrapper classes for request and response objects
● Wildcards can be used for mapping to multiple portlets
<filter>
<filtername>Log Filter</filtername>
<filterclass>com.acme.LogFilter</filterclass>
<lifecycle>ACTION_PHASE</lifecycle>
<lifecycle>EVENT_PHASE</lifecycle>
<lifecycle>RENDER_PHASE</lifecycle>
<lifecycle>Resource_PHASE</lifecycle>
</filter>
<filtermapping>
<filtername>Log Filter</filtername>
<portletname>*</portletname>
</filtermapping>
public void doFilter(RenderRequest request, RenderResponse response, FilterChain chain) throws PortletException, IOException {
String phase = (String)request.getAttribute(PortletRequest.LIFECYCLE_PHASE);
// filter pre-processing
chain.doFilter(request, response);
// filter post-processing
}
5) Portlet URL listeners
In some cases, we want to centrally manage setting specific properties on portlet URLs or to enhance the portlet URL creation of existing portlets.
We need to register such a listener with the listener element in the portlet deployment descriptor, and class needs to implement the PortletURLGenerationListener interface that defines a callback method for each type of portlet URLs: action, render, and resource.
<listener>
<listener-class>com.util.MyListener</listener-class>
</listener>
public class MyListener implements PortletURLGenerationListener {
public void filterActionURL(PortletURL arg0) {
System.out.println("MyListener.filterActionURL()"+arg0.getParameterMap());
}
public void filterRenderURL(PortletURL arg0) {
System.out.println("MyListener.filterRenderURL()"+arg0.getParameterMap());
}
public void filterResourceURL(ResourceURL arg0) {
System.out.println("MyListener.filterResourceURL()"+arg0.getParameterMap());
}
}
*6) Portlet-managed modes
In JSR 168, the portlet could leverage only portlet modes that are supported by the portal framework running the portlet. 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
<custom-portlet-mode>
<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.
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
*7) Extended Caching
Make sure portlet caching is enabled - http://wpcertification.blogspot.com/2009/03/enabling-potlet-fragment-caching.html
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.
<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. Action or Event request will expire cache.
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.
protected void doView (RenderRequest request, RenderResponse response) throws PortletException, java.io.IOException { if ( request.getETag() != null ) { // validation request if ( markupIsStillValid(request) { // markup is still valid response.getCacheControl().setExpirationTime(30); response.getCacheControl().setUseCachedContent(true); // no need to write any output return; } } // create new content with new validation tag response.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 token return 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.
8) Closing the gap to the servlet programming model
i)Support for page <head> contributions and Cookies
RenderHeaders
● Configured with Container runtime option
javax.portlet.renderHeaders (default false)
● Override the GenericPortlet.doHeaders method for:
● providing page <head> section element contributions, like CSS and Javascript <script> tags
● other HTTP/Portal specific response header properties
● setting Cookies
● setting nextPossiblePortletMode
● API
org.w3c.dom.Element PortletResponse.createElement(String tagName)
PortletResponse.addProperty(String key, org.w3c.dom.Element element)
PortletResponse.addProperty(String key, String value)
MimeResponse.addProperty(javax.servlet.http.Cookie cookie)
RenderResponse.setNextPossiblePortletModes(Collection<PortletMode> modes)
Contributing to <head> example:
protected void doHeaders(RenderRequest request, RenderResponse response) {
org.w3c.dom.Element loadAjaxJS = response.createElement("script");
loadAjaxJS.setAttribute("type", "text/javascript");
loadAjaxJS.setAttribute("src", "wicketajax.js");
response.addProperty(MimeResponse.MARKUP_HEAD_ELEMENT, loadAjaxJS);
}
In V1.0, the servlet HttpSession listeners can be also used for the PortletSession as the PortletSession is a façade on top of the HttpSession. Version 2.0 extends the list to all servlet life-cycle listeners defined in the Java Servlet Specification V2.5, and it enforces a correspondence between portlet and servlet objects:
· javax.servlet.ServletContextListener. For notifications about the servlet context and the corresponding portlet context.
· javax.servlet.ServletContextAttributeListener. For notifications about attributes in the servlet context or the corresponding portlet context.
· javax.servlet.http.HttpSessionActivationListener. For notifications about the activation or passivation of the HTTPSession or the corresponding PortletSession.
· javax.servlet.http.HttpSessionAttributeListener. For notifications about attibutes of the HTTPSession or the corresponding PortletSession.
· javax.servlet.http.HttpSessionBindingListener. For notifications about binding of the object to the HTTPSession or the corresponding PortletSession.
· javax.servlet.ServletRequestListener. For notifications about changes to the HTTPServletRequest or the mirrored portlet request of the current Web application.
· javax.servlet.ServletRequestAttributeEvent. For notifications about changes to the attributes of the HTTPServletRequest or the mirrored portlet request of the current Web application.
A servlet request listener can distinguish a plain servlet request targeted to a servlet from a wrapped servlet request targeted to a portlet by looking at the request attribute javax.portlet.lifecycle_phase. This attribute is set on a request targeted to a portlet, indicating the current portlet life-cycle phase of this request.
Access to all these life-cycle listeners gives you many hook points for managing objects related to these lifecycles, which is a great feature for many frameworks. These hook points, though, come with a cost, especially the request lifecycle listeners, which add significant processing overhead for each request. Therefore, use them with care.
9) Better web framework support
● Allow servlet dispatching, including using forwards, from:
• processAction
• processEvent
• render
• serveResource
● Optionally providing a PORTLET_SCOPE session to Servlets container option: javax.portlet.servletDefaultSessionScope
● Extended JSP tag library using new taglib uri
"http://java.sun.com/portlet_2_0"
10) 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-
<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.
11) Some more new features
i) 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
ii) Namespacing
● getNamespace() method now available on all Portlet Request classes (previously only on RenderRequest)
● Provides a unique value for the current Portlet Window
● Value may be used to prefix Javascript functions / variables or other items within a portal page that must be unique
● Will return the same value for the lifetime of the Portlet Window
Important Links & Technical articles -
http://blogs.sun.com/deepakg/entry/jsr286_public_render_parameter_feature
No comments:
Post a Comment