Articles
Java Platform, Enterprise Edition
|
| 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.
| |
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:
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.
| |
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:
portlet.xml file
<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;
}
}
|
<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>
<<portlet-class>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>
|
<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>
<<portlet-class>com.sun.portal.portlet.mapevent
.ContinentMapPortlet</portlet-class>>
. . .
. . .
<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>
<<portlet-class>com.sun.portal.portlet.mapevent
.ContinentMapPortlet</portlet-class>>
...
<supported-processing-event xmlns:x="http:sun.com/
mapevents">x:Continent</supported-processing-event>
</portlet>
. . .
</portlet-app>
|
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);
}
. . .
}
|
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
|
| |
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:
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>
|
<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>
|
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
|
| |
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:
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:
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() {
...............
}
}
|
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
|
| |
The resource serving feature enables a portlet to serve a resource. Portlets can create two kinds of resource links to serve requests:
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:
createResourceURL() method of the
RenderResponse object in the render phase of the portlet.
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 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.
| |
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:
This article has also provided sample applications for each of these features and shows how use the features in the portlets.
| |