|
By Deepak Gothe, Umachitra Damodaran, Manish Gupta, and Satyaranjan D, September 2007
|
|
|
This article describes new features available in the Portlet Container 2.0 Beta software. Portlet Container 2.0 Beta is a part of the Java Application Platform SDK Update 3 Beta software.
Note: See the Portlet Container
2.0 Beta Release Notes and Installation
Instructions for Java Application Platform SDK Update 3 Beta.
Contents
Overview
The article Introducing
Java Portlet Specifications: JSR 168 and JSR 286 describes
portlets as web-based components that enable integration
between applications and portals and thus delivery of applications on
portals. The heart of a portal is a portlet container, and as a servlet
container is to servlets, so is a portlet container is to portlets.
Portlet containers execute portlets and manage their life cycle.
The public review draft of Java Portlet Specification 2.0, Java
Specification Request (JSR) 286 was released recently.
The JSR 286 includes the following new features:
- Events – Enable portlets to communicate with
each other by sending and receiving events
- Public render parameters – Enable portlets to
specify which render parameters they can share with other portlets
- Portlet filters – Enables dynamic transformation
of the content of portlet requests and responses
- Resource serving – Enable portlets to serve
resources within the portlet context
Portlet Container 2.0 Beta implements the above features of
the Java Portlet Specification 2.0. In addition to this, Portlet Container 2.0 Beta provides a portlet driver, a lightweight portlet rendering environment. This
driver simulates some capabilities of a typical portal product like the Sun
Java System Portal Server.
This article describes the new features that are available in
Portlet Container 2.0 Beta along with a sample application to
demonstrate how to write portlets with these features. The steps to
deploy portlets are the same as in Portlet Container 1.0.
To know more about how to deploy portlets, see Understanding
the Portlet Container 1.0 Beta Software and Deploying Portlets.
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 code>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.
Event names are represented as QNames
to identify them uniquely. The event name can be retrieved by using the getQName() method that
returns the complete QName of the event, or by using the getName() method
that only returns the local part of the event name. The value of the
event must be based on the type defined in the deployment descriptor.
To create portlets that use the event feature, follow these
steps:
- Declare the events in the
portlet.xml file
- Set the event definition at the portlet application level. This specifies the event name and the object
type.
Note: The object must be serializable.
<portlet-app xmlns="http://java.sun.com/xml/ns/portlet/
portlet-app_2_0.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/
portlet-app_2_0.xsd
http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd"
id="myPortletApp" version="2.0">
<portlet>
. . .
. . .
</portlet>
<event-definition>
<qname xmlns:x="http:sun.com/mapevents" >x:Continent</qname>
<value-type>com.sun.portal.portlet.mapevent
.Continent</value-type>
</event-definition>
</portlet-app>
@XmlRootElement
public class Continent implements Serializable {
public Continent() {
}
private String name;
private String description;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description)
this.description = description;
}
}
|
- In the portlet section, specify the event name defined above for the portlets that you want to publish in this
event.
<portlet-app xmlns="http://java.sun.com/xml/ns/portlet/
portlet-app_2_0.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/portlet
/portlet-app_2_0.xsd
http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd"
id="myPortletApp" version="2.0">
<portlet>
<description>ContinentPortlet</description>
<portlet-name>ContinentPortlet</portlet-name>
<display-name>ContinentPortlet</display-name>
<com.sun.portal.portlet.mapevent
.ContinentPortlet</portlet-class>
. . .
<supported-publishing-event xmlns:x="http:sun.com/
mapevents">x:Continent</supported-publishing-event>
</portlet>
. . .
</portlet-app>
|
- In the portlet section, specify the event name
defined above for the portlets that want to process in this event.
<portlet-app xmlns="http://java.sun.com/xml/ns/portlet/
portlet-app_2_0.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/
portlet-app_2_0.xsd
http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd"
id="myPortletApp" version="2.0">
. . .
<portlet>
<description>ContinentMapPortlet</description>
<portlet-name>ContinentMapPortlet</portlet-name>
<display-name>ContinentMapPortlet</display-name>
<com.sun.portal.portlet.mapevent
.ContinentMapPortlet>
. . .
. . .
<supported-processing-event xmlns:x="http:sun.com/
mapevents">x:Continent</supported-processing-event>
</portlet>
<portlet>
<description>ContinentInfoPortlet</description>
<portlet-name>ContinentInfoPortlet</portlet-name>
<display-name>ContinentInfoPortlet</display-name>
<com.sun.portal.portlet.mapevent
.ContinentMapPortlet>
...
<supported-processing-event xmlns:x="http:sun.com/
mapevents">x:Continent</supported-processing-event>
</portlet>
. . .
</portlet-app>
|
- Issue an event in the portlet that was specified as a
supported-publishing-event
event in the portlet.
public class ContinentPortlet extends GenericPortlet {
public void processAction(ActionRequest request, ActionResponse response)
throws PortletException,IOException {
QName qname = new QName("http:sun.com/mapevents" , "Continent")
String value = request.getParameter("continent");
Continent continent = new Continent();
continent.setName(value);
ResourceBundle rb = getPortletConfig()
.getResourceBundle(request.getLocale());
continent.setDescription(rb.getString(value));
response.setEvent(qname, continent);
}
. . .
}
|
- Process the event in the portlet that has been specified as
a
supported-processing-event event in the portlet.
public class ContinentInfoPortlet extends GenericPortlet {
public void processEvent(EventRequest request, EventResponse response) {
Event event = request.getEvent();
if(event.getName().equals("Continent")){
Continent payload = (Continent)event.getValue();
response.setRenderParameter("continentDescription",
payload.getDescription());
}
}
. . .
}
public class ContinentMapPortlet extends GenericPortlet {
public void processEvent(EventRequest request, EventResponse response) {
Event event = request.getEvent();
if(event.getName().equals("Continent")){
Continent payload = (Continent)event.getValue();
response.setRenderParameter("continentName", payload.getName());
}
}
. . .
}
|
As an illustration, here is a sample
application, compressed as a ZIP file. You can also download the binary EventingMap.war
file to deploy and run the application.
Figure 1 shows the World Map, Continent Information, and Continent Map
Portlets that participate in the event. Clicking on any continent in
the World Map triggers an event. This event is processed by the
Continent Information and Continent Map Portlets to show the relevant
information.
Figure 1: Sample Application: Events
|
Public Render Parameters
In Java Portlet Specification 1.0 (JSR 168), the render
parameters set in the processAction() method are available only in the
render phase of the same portlet.
By using the public render parameters feature, the render
parameters set in the processAction() method of one portlet are available
in render parameters of the other portlets. Using public render parameters
instead of events avoids the additional process event call.
To enable coordination of render parameters with other
portlets within the same portlet application or across portlet applications, the
portlet can declare public render parameters in its deployment
descriptor using the public-render-parameter element in the
portlet application section. Public render parameters can be viewed and
changed by other portlets or components.
In the portlet section, each portlet can specify the public
render parameters to be shared through the supported-public-render-parameter
element. The supported-public-render-parameter element must
reference the identifier of a public render parameter defined in the
portlet application section in a public-render-parameter
element.
To create portlets that use the public render parameters, follow these
steps:
- Declare the render parameters to be shared in the
portlet.xml file by setting the
public render parameters at the portlet
application level.
<portlet-app xmlns="http://java.sun.com/xml/ns/portlet/
portlet-app_2_0.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/
portlet-app_2_0.xsd
http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd"
id="myPortletApp" version="2.0">
<portlet>
. . .
</portlet>
<public-render-parameter>
<identifier>zip-id</identifier>
<qname xmlns:x="http://sun.com/params">x:zip</qname>
</public-render-parameter>
</portlet-app>
|
- Specify the render parameter that the portlet would
like to share in the portlet section.
<portlet-app xmlns="http://java.sun.com/xml/ns/portlet/
portlet-app_2_0.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/
portlet-app_2_0.xsd
http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd
id="myPortletApp" version="2.0">
<portlet>
<description>WeatherPortlet</description>
<portlet-name>WeatherPortlet</portlet-name>
<display-name>WeatherPortlet</display-name>
<portlet-class>com.sun.portal.portlet.WeatherPortlet</portlet-class>
<supported-public-render-parameter>zip-id</supported-public-
render-parameter>
</portlet>
<portlet>
<description>MapPortlet</description>
<portlet-name>MapPortlet</portlet-name>
<display-name>MapPortlet</display-name>
<portlet-class>com.sun.portal.portlet.MapPortlet</portlet-class>
. . .
<supported-public-render-parameter>zip-id</supported-public-
render-parameter>
</portlet>
. . .
</portlet-app>
|
- Set the render parameter in the
processAction() method:
public class WeatherPortlet extends GenericPortlet {
. . .
public void processAction(ActionRequest req, ActionResponse res)
throws IOException, PortletException {
. . .
res.setRenderParameter("zip", zip);
}
. . .
}
|
Here is a sample
application, compressed as a ZIP file. You can also download the binary WeatherMap.war
file to deploy and run the sample application.
Figure 2 shows the Weather and Map portlets. The Weather portlet sets the zip which is
declared as a Public Render Parameter. This parameter is supported by
both Weather and Map portlets. Any change in the value of zip by
Weather portlet is reflected during the render phase of both weather
and map portlets.
Figure 2: Sample Application: Public Render Parameters
|
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 concept of a portlet filter is
same as that of a servlet filter. The only difference is that a servlet has only one
request handling method, service()
and therefore there is only one type of the servlet filter. A portlet on the other hand has
four types of request handling methods and therefore there are four different types
of portlet filters.
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
Each of the above filter interface contains a single doFilter(*Request,
*Response, FilterChain chain) method which differs in the type of request and response object. For
example, the doFilter() method in ActionFilter takes
the code>ActionRequest and ActionResponse objects
while the doFilter() method of RenderFilter takes instances of the RenderRequest
and RenderResponse.
Each filter interface extends a common base interface called javax.portlet.filter.PortletFilter.
This common base interface contains the following two methods:
init(javax.portlet.filter.FilterConfig
filterConfig) - The init()
method makes sure that every filter
has access to a FilterConfig
object from which it can obtain its
initialization parameters, a reference to the PortletContext which it
can use, for example, to load resources needed for filtering tasks.
destroy() - Signifying the end of service of the filter.
The init() and destroy() methods of a portlet
filter are called only once during their lifetime.
A single filter class can provide
filter functionality for more than one life cycle method. Also, a
single filter can provide filter functionality for more than one
portlet. Multiple filters can be associated with one life cycle
method of a portlet. The doFilter()
method of a portlet filter might
create customized request and response objects by using the *RequestWrapper
and *ResponseWrapper classes
and by passing these wrappers to the doFilter()
method of the FilterChain
object.
To write a portlet filter, follow these steps:
- Write a filter class.
A filter class should implement one or more of the above mentioned four
interfaces and should provide a no argument public constructor. The
filter class should also override the init()
and destroy() methods of the javax.portlet.filter.PortletFilter
interface.
public class HitCounterFilter implements RenderFilter {
public void init(FilterConfig filterConfig) throws PortletException {
...............
}
public void doFilter(RenderRequest renderRequest, RenderResponse
renderResponse, FilterChain filterChain) throws IOException,
PortletException {
...............
}
public void destroy() {
...............
}
}
|
- Add the following XML fragments in the
portlet.xmlfiles after you write the portlet filter class.
<portlet-app xmlns="http://java.sun.com/xml/ns/portlet/
portlet-app_2_0.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/
portlet-app_2_0.xsd
http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd"
id="myPortletApp" version="2.0">
<portlet>
. . .
<filter>
<filter-name>HitCounterRenderFilter</filter-name>
<filter-class>com.sun.portal.portlet.filter
.HitCounterFilter</filter-class>
<lifecycle>RENDER_PHASE</lifecycle>
</filter>
<filter-mapping>
<filter-name>HitCounterRenderFilter</filter-name>
<portlet-name>HelloWorld</portlet-name>
</filter-mapping>
</portlet-app> |
Here is a sample
application, compressed as a ZIP file. Download the PortletFilter.war
file to deploy and run the sample application.
The number of times the sample portlet has been accessed
is logged using a Filter. Access the portlet and check the application
server log file. The log file shows the number of times the portlet has been
accessed.
Figure 3: Sample Application: Portlet Filters
|
Resource Serving
The resource serving feature enables a portlet to serve a resource. Portlets can create two kinds of resource links to
serve requests:
- Direct links to the resources in the same portlet web
application.
Direct links are not guaranteed to pass through the portal server and will not have
portlet context available. So a direct link should be used in
cases where access to the portlet context and access through portal
server is not needed.
- Resource URL links that point back to the portlet.
These links call the serveResource()
method of ResourceServingPortlet.
This way, the portlet can serve a resource that is protected by the portal security and can leverage
the portlet context. Portlet container does not render any output in
addition to the content returned by the serveResource call. Thus, the serveResource() method provides
more control to you by giving access to write directly on
the response object where portal server just acts as a proxy.
The serveResource() method can also be used to implement Ajax use cases by
invoking the resourceURL()
method through the XMLHttpRequest in
client-side JavaScript code. The client-side code of the portlet is then responsible for inserting
either the markup or update the page DOM in a nondisruptive manner for the other components on the page.
A resource URL can be generated in the following ways:
- By invoking the
createResourceURL()
method of the RenderResponse object in
the render phase of the portlet.
- By invoking the
doView()
method of the Generic Portlet.
The resource URL can be created inside serveResource()
method of the ResourceServingPortlet.
In this case, the serveResource()
method should include calls to the createResourceURL()
method on ResouceResponse
object passed to this method.
The presentation logic for the resource serving portlet looks as below:
<%
ResourceURL ajaxResourceURL = renderResponse.createResourceURL();
ajaxResourceURL.setResourceID("invoice");
%>
<script type="text/javascript">
/* Use Dojo.io.bind to asynchronously get invoice content */
function <portlet:namespace/>_loadInvoice(invoice) {
. . .
. . .
var bindArgs = {
url: "<%=ajaxResourceURL%>",
method: "POST",
content: querystring,
handle: function(type, data, evt) {
. . .
. . .
}
}
|
The portlet class that serves the resource looks as below:
public class InvoicePortlet extends GenericPortlet {
..............
public void serveResource(ResourceRequest request, ResourceResponse
response) throws PortletException, IOException
{
response.setContentType("text/html");
String resourceID = request.getResourceID();
if (resourceID.equals("invoice")) {
String invoice = request.getParameter("invoice");
if (invoice == null) {
throw new PortletException("Required parameter,
invoice, is missing.");
} else {
PrintWriter writer = response.getWriter();
writer.print(content);
}
} else if (resourceID.equals("js")) {
String content = getContents(jsPage);
PrintWriter writer = response.getWriter();
writer.print(content);
}
}
................
}
|
Here is a sample
application, compressed as a ZIP file. You can also download the binary InvoiceAjaxPortlet.war
file to deploy and run the sample application.
Figure 4 shows the Invoice Portlet. When the user selects a invoice
number in the drop-down box, an asynchronous request (Ajax call) is
made by the JavaScript client to the portlet. The serveResource() method of
the portlet is invoked to serve the content.
Figure 4: Sample Application: Invoice Ajax Portlet
|
Portlet Policy
Portlet Container 2.0 Beta introduces a Portlet Policy that governs
various policies related to events and public render parameters. The
policy specifies how the events and public render parameters are
distributed. It also specifies the maximum generation of events to
prevent endless loops. If a portlet sends an event to other portlets,
it is considered as one
event generation. If the other portlets send events, that is
considered as two event generations and so on.
The portlet-policy.xml file is located in <javaee.home>/domains/domain1/portlet-container/config.
The schema is located at https://portlet-container.dev.java.net/xml/portlet-policy.xsd
The portlet-policy.xml file looks like this:
<event-distribution>VISIBLE_PORTLETS_ON_PAGE</event-distribution>
<max-event-generation>2</max-event-generation>
</event-policy>
<public-render-parameter-policy>
<public-render-parameter-distribution>VISIBLE_PORTLETS_ON_PAGE</public-
render-parameter-distribution>
</public-render-parameter-policy>
|
The above example shows that only visible portlets on the page will
receive events and public render parameters. This also specifies that the
maximum generation of events permitted is two.
Conclusion
This article describes the features that are included in
the Portlet Container 2.0 Beta software. The Portlet Container
2.0 Beta software includes the following new features from the Java
Portlet Specifications: JSR 286:
- Events
- Public render parameters
- Portlet filters
- Resource serving
This article has also provided sample applications for each of
these features and shows how use the features in the portlets.
References
|