JSR-286 development tutorial: Mastering request-response programming
This portlet programming tutorial shows how the PortletRequest and Portlet Response are used when developing JSR-286, request-response based apps.
The following tutorial is the second in a series of JSR 286 development learning resources appearing on TechTarget's TheServerSide. These "quickstart" tutorials and tips are intended to help any application developer acquire coveted portlet programming skills and become quickly adept at both introductory and expert portal programming topics, which allow the developer to build portlet-based applications that can be effectively deployed to any of the standards-based portal servers available on the market today.
JSR-286 portlets are Java-based components, and like all Java-based components, they can have any number of business methods and instance variables defined within. However, there is one method that all portlets must define if they want to be rendered on a portal page, and that ever-important method is the doView method.
The view mode and the doView method
When a portlet is initially displayed on a page, it is said to be in view mode. The view mode represents the normal way that a portlet looks when it appears on a portal page.
The doView method of a portlet corresponds to the view mode of a portlet. The way that a portlet, in its normal mode, should look on a page, is coded into the doView method. Since it must be possible to view a portlet that is placed on a portal page, every portlet must have a doView method.
public void doView(RenderRequest request, RenderResponse response) throws PortletException, IOException
The doView method of a portlet is fed the RenderRequest and RenderResponse objects, and also throws the intolerable PortletException and IOException.
Along with a concrete implementation of the doView method, to be rendered on a page, a portlet must also define support for the view mode within its deployment descriptors.
<supports> <mime-type>text/html</mime-type> <portlet-mode>view</portlet-mode> </supports>
The PortletRequest vs. the RenderRequest
Mastering portlet programming boils down to understanding the request-response cycle in which every portlet and portal server must participate.Cameron McKenzie, editor of TheServerSide (@potemcam)
A portlet's primary responsibility is to handle a Web-based request-response cycle. Information about the incoming client request is encapsulated in a PortletRequest object, which is then passed to the pertinent do<mode> method.
The JSR-268 specification defines a PortletRequest data type, but it is actually a subtype of the PortletRequest that is passed into the doView method. This special subtype of the PortletRequest is the RenderRequest, which sort of makes sense, as the doView method is invoked when a portlet is required to render content to be displayed on the client device.
While the doView method of a portlet is passed a RenderRequest, as opposed to the more generic PortletRequest, the RenderRequest only defines a single new method used with caching, named getETag(), so the distinction is largely academic. All of the other methods available to the RenderRequest, such as getLocale() and isSecure(), are actually defined in the PortletRequest interface.
Inspecting the incoming request
Any information a developer is allowed to know about the incoming client request is obtained through the request object. Some of the salacious things we can find out about the user through the PortletRequest object include:
- The preferred language of the user;
- Any headers served up by a client's browser;
- What a user typed into a textfield;
- Which radio button a user selected;
- The type of browser the client is using; and
- The color of the shirt the user is wearing.
Okay, maybe the PortletRequest can't tell you the color of the shirt your user is wearing, but it can tell you practically anything else. The PortletRequest makes interrogating your client way too easy.
The PortletResponse object
While the PortletRequest is used to discover information about the incoming request, the PortletResponse, or more accurately for methods used during the rendering phase of a portlet, the RenderResponse object, is typically used to send something back, or do something to, the client.
For example, to send HTML back to the client, a PrintWriter can be obtained from the RenderResponse object through the call response.getWriter().
Other salacious things we can do to with a PortletResponse object include:
- Setting the content type for the response;
- Creating a link back to the current portlet; and
- Obtain a PrintWriter and output content.
PortletResponse, MimeResponse & RenderResponse
Only seven methods are defined in the PortletResponse interface, with the setProperty, createElement and overloaded addProperty methods simply being used for setting response headers. The RenderResponse itself only defines three methods, namely setTitle, setNextPossiblePortletModes and the overloaded setContentType. However, the RenderResponse does not inherit directly from the PortletResponse, but instead inherits from the MimeReponse, which itself inherits from PortletResponse. The MimeResponse is a new interface introduced in the Portlet 2.0 API, so if you've been concentrating on JSR 168 development, this interface will be new to you.
The MimeResponse is a new interface that collects and localizes many of the RenderResponse methods that existed in previous versions of the Portlet API, but collects them together in a more logical component, allowing various response objects that are not interested in returning MIME content to the end user to be free from having to implement nonsensical methods. It is in the MimeResponse interface that methods for obtaining the PrintWriter and managing the response stream, methods like getWriter(), setBufferSize() and flushBuffer(), are defined.
Figure 2. The RenderResponse hierarchy.
A note about content creation within a portlet
It should be noted that only an HTML snippet is sent back to the client through a do<mode> method (doView, doEdit, doConfig, doHelp, and so on).
A portlet should never print out <HTML> or <BODY> tags. The portal server takes care of the overall page layout through a theme. The job of a portlet is to simply render a snippet of markup language that will be displayed in a predefined segment of the overall portal page.
Furthermore, any HTML tags that are opened in a portlet should be closed by that same portlet. Make sure the snippet of markup sent back to the client is well formed, and doesn't leave any dangling HTML tags, such as a <TABLE> tag that doesn't have a matching </TABLE> tag.
Looking at another simple portlet
Here's a simple, yet highly cosmopolitan portlet that determines a user's preferred language and prints out an undiscriminating message.
Figure 3. The image shows the rendering of the above CountrySnooperPortlet.
Looking at the code for the CountrySnooperPortlet, notice how a method of the PortletRequest, through the RenderRequest, is used to find information about the user's country of origin. Also notice how the RenderResponse, through the MimeResponse, provides access to a PrintWriter that allows us to send content back to the user.
Generally speaking, anything you want to know about the client is in the request, and anything you want to do to the client is done through the response.
Packaging portlets together
If the CountrySnooperPortlet is packaged along with other portlets in the same portlet war file, all of the portlets contained in that war file would be said to be part of the same portlet application. If the CountrySnooperPortlet was packaged along with the HelloWorldPortlet defined earlier, both would be said to be part of the same portlet application, and we could also say that both portlets share a common PortletContext.
All portlets defined within a portlet application must be declared in the portlet deployment descriptor, portlet.xml. Our CountrySnooperPortlet is no exception.
<portlet> <portlet-name>CountrySnooperPortlet </portlet-name> <display-name xml:lang="en">CountrySnooperPortlet</display-name> <portlet-class> com.mcnz.portlet.CountrySnooperPortlet </portlet-class> <supports> <mime-type>text/html</mime-type> <portlet-mode>view</portlet-mode> </supports> <portlet-info> <title>The Country Snooper Portlet </title> <short-title>Snooper </short-title> <keywords>Snoop Headers</keywords> </portlet-info> </portlet>
Notice how a few new tags have been added to the definition of the CountrySnooperPortlet, as compared to the HelloWorldPortlet created earlier. There is a significant amount of metadata that can be configured in the deployment descriptor, and in this case, we have added a short-title and keywords. These are optional but useful, with the short-title being useful for displaying portlets on handheld devices where screen size is limited, with the keywords being used to help clients search and find portlets when a portal's resource catalog becomes large.
<?xml version="1.0" encoding="UTF-8"?> <portlet-app xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd" version="2.0" xmlns:xsi="https://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="com.mcnz.portlet.HelloWorldPortlet.7aed100ae3"> <portlet> <portlet-name>HelloWorldPortlet</portlet-name> <display-name xml:lang="en">HelloWorldPortlet</display-name> <portlet-class>com.mcnz.portlet.HelloWorldPortlet</portlet-class> <supports> <mime-type>text/html</mime-type> <portlet-mode>view</portlet-mode> </supports> <portlet-info> <title>Hello World Portlet</title> </portlet-info> </portlet> <portlet> <portlet-name>CountrySnooperPortlet </portlet-name> <display-name xml:lang="en">CountrySnooperPortlet </display-name> <portlet-class>com.mcnz.portlet.CountrySnooperPortlet</portlet-class> <supports> <mime-type>text/html</mime-type> <portlet-mode>view</portlet-mode> </supports> <portlet-info> <title>The Country Snooper Portlet </title> <short-title>Snooper </short-title> <keywords>Snoop Headers</keywords> </portlet-info> </portlet> <default-namespace>http://portletbook/</default-namespace> </portlet-app>
Another portlet example: Inspecting headers
Much of the delicious information that comes to the server about the client comes in the form of HTTP headers. Headers, which come in as name-value pairs, represent information that a Web browser surreptitiously sends to the server on every client request.
Using the JSR-268 Portlet API, some creative use of the enumeration class, and a little Haitian Voodoo sprinkled in for good measure, looping through HTTP headers and seeing what type of data is being sent to the server is very easy to do. Here's a portlet that uses the getPropertyNames() method of the RenderRequest object to do just that.
package com.mcnz.portlet; import java.io.*;import javax.portlet.*; import java.util.*; public class GettingHeadersPortlet extends GenericPortlet { protected void doView(RenderRequest request, RenderResponse response) throws PortletException, IOException { response.setContentType("text/html"); PrintWriter out = response.getWriter(); out.print("<B>These headers were sent:</B><BR/> "); Enumeration<String> e = request.getPropertyNames(); while (e.hasMoreElements()){ String name = e.nextElement().toString(); String value = request.getProperty(name); out.print("<BR/>"); out.print(name + ": " + value); } } }
Debriefing the GettingHeadersPortlet
Taking the time to try and understand exactly how the GettingHeadersPortlet works will pay off dividends in the future. Code that loops through an enumeration of named objects and then accesses corresponding values will come up time and time again, not only in this tutorial, but in day-today portlet- and servlet-based programming as well.
The first line of code in the doView method asks the request object to return all of the various header names in the form of a very flexible collection type called an enumeration.
java.util.Enumeration enum = request.getPropertyNames();
Once we have all of the header names in the enumeration, we can move through the collection of names one at a time, using the hasMoreElements() method of the enumeration class.
while (enum.hasMoreElements( )) { //do something }
As we loop through the enumeration of header names, we can use the nextElement() method of the enumeration class to grab the a header name.
String name = enum.nextElement( );
Once we have the name of the header, we can grab the associated value.
String value = request.getProperty(name);
Of course, if we knew the name of a header of interest, we could name it explicitly. For example, user-agent is the name of a header that tells you information about the type of Web browser a client is using. We could grab the value associated with the user-agent header by making the following call:
String browser = request.getProperty("user-agent");
The above line of code might return the following:
(compatible; MSIE 6.0; Windows NT 5.0 )
Headers and other enumerable elements
The results of our completed GettingHeadersPortlet is a portlet that lists all the header names and corresponding values that are sent to a Web server from a Web-based client.
The header names include accept, referrer, accept-language, user-agent, host, connection and cache-control. Different browsers and device types will send a different set of headers, so the listing will vary from one client to another. Figure 5 shows the values associated with these various header names.
Figure 5. The rendering of the GettingHeadersPortlet.
Many objects in both the Servlet and Portlet API return a listing of names as an enumeration, which can be looped through by using code very similar to that in our GettingHeadersPortlet. Some of those enumerable objects include:
- Initialization Strings stored in the PortletContext
- Objects placed in the PortletSession
- Initialization String values stored in the PortletConfig
Portlet API exceptions
When coding the doView method of a Portlet, be aware of two potential exceptions that might be thrown. The first exception that might occur is the IOException. A portlet has to deliver content to the portal, and subsequently to the end user, and if any ports or sockets get messed up along the way, an IOException can potentially be thrown. This is also thrown when a portlet takes too long to render and the portal server decides it will not wait any longer for the portlet to return any yummy markup.
The other exception the doView method of a portlet might throw is the PortletException. The PortletException is a very general exception, indicating that somehow, at some point in time, the portlet was unable to complete its processing successfully. All of the other exceptions defined in the portlet API use the PortletException as their parent class, including:
- LegacyPortletExceptionWrapper
- PortletModeException
- PortletSecurityException
- ReadOnlyException
- UnavailableException
- ValidatorException
- WindowStateException
- WrapperPortletException
Figure 6. Class diagram for the PortletException class.
<?xml version="1.0" encoding="UTF-8"?> <portlet-app
xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd" version="2.0" xmlns:xsi="https://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="com.mcnz.portlet.HelloWorldPortlet.7aed100ae3"> <portlet> <portlet-name>HelloWorldPortlet</portlet-name> <display-name xml:lang="en">HelloWorldPortlet</display-name> <portlet-class>com.mcnz.portlet.HelloWorldPortlet</portlet-class> <supports> <mime-type>text/html</mime-type> <portlet-mode>view</portlet-mode> </supports> <portlet-info> <title>Hello World Portlet</title> </portlet-info> </portlet> <portlet> <portlet-name>CountrySnooperPortlet </portlet-name> <display-name xml:lang="en">CountrySnooperPortlet </display-name> <portlet-class>com.mcnz.portlet.CountrySnooperPortlet</portlet-class> <supports> <mime-type>text/html</mime-type> <portlet-mode>view</portlet-mode> </supports> <portlet-info> <title>The Country Snooper Portlet </title> <short-title>Snooper </short-title> <keywords>Snoop Headers</keywords> </portlet-info> </portlet> <portlet> <portlet-name>GettingHeadersPortlet </portlet-name> <display-name>GettingHeadersPortlet </display-name> <portlet-class>com.mcnz.portlet.GettingHeadersPortlet</portlet-class> <supports> <mime-type>text/html</mime-type> <portlet-mode>view</portlet-mode> </supports> <portlet-info> <title>Getting Headers Portlet</title> </portlet-info> </portlet> <default-namespace>http://portletbook/</default-namespace> </portlet-app>
Summary
In the end, understanding portlet programming boils down to understanding the request-response cycle in which every portlet and portal server must participate. By getting familiar with the uses for the PortletResponse object and the PortletRequest object, a developer has moved one step closer to mastering development with JSR 286, the Portlet 2.0 API.
Do you have any quick tips for mastering Portlet API development? Let us know.