JSR-286 development tutorial: Linking back to the portal with the PortletURL

In this JSR-286 development tutorial we look at the PortletURL and the challenge developers face when trying to link back to the portal, a portal page or a given portlet instance.

The following tutorial, written by Sal Pece, is the fourth in a series of Portlet development tutorials, focussing on the Portlet API's need for the PortletURL, and how this JSR-286 PortletURL component can be used to link back to the portal, portal page, or portlet instance running on a page. 

This tutorial is just one of the many tutorials that make up the JSR-286 Portlet Development Tutorial and Technology Guide. These tutorials and tips are designed to help developers  looking to develop JSR-286 programming talents to become adept at  basic, intermediate and advanced level portlet programming topics, helping the software professional to create portlet-based software that can be quickly and easily deployed to any of the standards-based portal servers on the market

Forwarding to a JSP to spit out a little Hello World message, as we did in the previous tutorial, isn't going to win you any Programmer of the Year awards. On the other hand, using a JSP to generate an HTML form that helps process input from a user would be a fastidious application of any developer's programming talents.

Creating basic HTML forms for user input

To grab input from a user, we simply use HTML forms. For example, if we wanted the user to guess a magic number between one and ten, we might provide an HTML form that looks something like figure 4-1. Using HTML forms to obtain input from a user is nothing new to a web developer, but using forms in a portlet environment does presents a few unique and annoying challenges. Here we will take a look at exactly what those problems are, and see how the Portlet API helps us address those issues so they don't cause any problems when we deploy our form based and hyperlinked applications to the portal.

The NumberGuesserPortlet

For this tutorial, we're going to develop the NumberGuesserPortlet. The idea is that when the portlet is first viewed, it will ask the user to guess a magic number between one and ten, as seen in figure 4-1.

 Figure 4-1. Input form for the NumberGuesserPortlet

When the user clicks Guess!!!, the NumberGuesserPortlet will check to see what the number was that the user typed in, and tell the user whether they guessed the magic number correctly or not. For this example, we will hard code the magic number to be 5.

The Portlet will then generate a response, telling the user if they guessed the magic number correctly or not, as seen in figure 4-2.

Figure 4-2. Output from the NumberGuesserPortlet

The response, which tells the user if they guessed correctly, will be generated through print statements in the portlet. The form that takes input from the user will be implemented as a JSP, very creatively named numberguesser.jsp

The Try Again link will then return the user back to the original screen, prompting the user to guess the magic number again. And remember, all of this is being performed within the confines of a portlet window, a window that could potentially be sharing screen space with two, three or even twenty other portlets on the page. Furthermore, a portlet can be placed on any number of different pages in the portal. This introduces a level of complexity to your portlet applications that you would never encounter in a typical servlet and JSP based application.

Using a JSP to generate an HTML form that helps process input from the user would be a fastidious application of any developer's programming talents. 

Linking back to a portlet

Programming portlets presents the web developer with many new challenges, not the least of which is figuring out how to invoke a specific portlet instance from an HTML form. A portlet can be placed on a variety of different web pages at runtime, and none of these portlets are uniquely addressable. The closest thing we can do is address a specific portal page using the page's URL, but even then there might be several instances of a given portlet on that page, and even then we won't know the name of the page a portlet is on until runtime. How can we invoke a web-based resource when we don't know the web address of the page on which it will appear?

Another challenge is making sure that the data submitted through our form goes to our portlet, and our portlet only. We don't want other portlets on the page using data submitted from our form. Fortunately, the Portlet API addresses these very challenges.

When a user clicks the submit button on a form, there must be a resource sitting on the server that is ready to process the user's request. With typical Servlet/JSP applications, form submissions are forwarded to a servlet, and the name of the servlet is specified as the action of the form; however, we can't do that with a portlet.

For example, if our number guessing application was implemented as a servlet, there would be an HttpServlet, perhaps named NumberGuesserServlet, that would respond to the submission of the form, and subsequently extract the user's input. The servlet would be the target of the form's action attribute, and the form tag would look like this:

<FORM action = "NumberGuesserServlet">

But a portlet can't make a direct call back to itself that easily. The best a portlet could do is make a call back to the page the portlet is displayed on, but even that's impossible to configure in an HTML form, because at development time, we don't know which page, or on how many pages, our portlet will potentially appear.

Submitting HTML forms to a portlet

Notice how the action attribute of the HTML form element in figure 4-2 points to a question mark. Typically, this would point to a Servlet or a CGI script that handles the submission of a form. How can we direct the submission of our form back to our portlet? We don't know the name of the page our portlet may appear on at runtime. This makes hard coding a target for the action attribute of the form element impossible. In this case, the action attribute of the form has been left as a question mark (?).

<FORM   action = "?"  >
I'm thinking of a number between 1 and 10.<BR><BR>
<I>What is it? </I>
<INPUT type="text" name="number" size="10">
<INPUT type="submit" name="SUBMIT" value="Guess!!">
</FORM>

The PortletURL object

Life would be simple if we could tell a form to directly call the NumberGuesserPortlet, but we can't. When a request is made to the portal server, a single portlet can't be invoked directly. Instead, the user must request a portal page, and the portal server takes care of rendering all of the portlets that are part of that page.

So, how do we make a new request to the portal to have our portlet, and the portal page with which it is associated, re-invoked? The answer is to have the  Portlet API's RenderResponse object create a PortletURL object, which essentially represents a link back to the current portal page, along with all of the portlets that appear on that page.

response.createRenderURL(); //creates a link back to the portal

The PortletURL interface

The PortletURL interface represents a URL that references the portlet itself.

A PortletURL is created through the RenderResponse or ResourceResponse. Parameters, a portlet mode, a window state and a security level can be added to PortletURL objects.

public interface PortletURL extends BaseURL

 

Figure 4-3. Methods of the PortletURL Interface

There are two types of PortletURLs:

  1. Action URLs, they are created with createActionURL, and trigger an action request followed by a render request.
  2. Render URLs, they are created with createRenderURL, and trigger a render request.

The string representation of a PortletURL does not need to be a valid URL at the time the portlet is generating its content. It may contain special tokens that will be converted to a valid URL, by the portal, before the content is returned to the client.

-Portlet API JavaDoc

Creating the RenderURL link

To create a link back to a portlet from within an HTML form, we need to dynamically generate the action attribute. This can be done using a JSP expression:

<FORM  ACTION = "<%=renderResponse.createRenderURL()%>" >

Unfortunately, right out of the box, this line of code will not successfully compile. A little taglib directive, along with a <portlet:defineObjects/> custom tag must be added to the JSP to make everything kosher.

A refresher on portlet custom tags

While all JSPs understand that the word response, when used inside of an expression or a scriptlet, is a reference to the HttpServletResponse object, a standard JSP has no knowledge of what a portletResponse object is, even if the reference occurs in a JSP that is packaged as part of a portlet application. To get the Java compiler to understand that when we say renderResponse in our JSPs, that we are referring to a subtype of the PortletResponse object, we need to add two lines to the top of our Java Server Page:

  1. %@taglib uri="http://java.sun.com/portlet" prefix="portlet"%>
  2. portlet:defineObjects/>

The first line is the taglib, also known as a tag library directive. This taglib directive indicates that the JSP is going to employ the services of the portlet custom tag library.

The second line, <portlet:defineObjects/>, is an actual portlet custom tag. The sole purpose of the <portlet:DefineObjects/> tag is to expose three important portlet API objects:

  • The RenderRequest object: renderRequest
  • The RenderRepsonse object: portletResponse
  • The PortletConfig object: portletConfig

With local access to the renderRequest, renderResponse, and portletConfig objects, the developer has access all of the other important portlet API components, including the PortletSession, PortletContext, PortletURL and others.

The following code shows what a JSP that contains all of the required Portlet API artifacts, along with a form that uses a PortletURL as the target of the action attribute, would look like:

<%@taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet"%>
<portlet:defineObjects />
<FORM action="<%=renderResponse.createRenderURL()%>">
      I'm thinking of a number between 1 and 10.<BR>
      <BR>
      <I>What is it?</I>
      <INPUT name="number" type="text" size="10" />
      <INPUT name="submit" value="Guess!!" type="submit" />
</FORM>

Form handling with the Portlet 2.0 API

With an HTML form that contains a textfield named 'number', all we have to do to figure out what a user typed into that textfield is call the getParameter method of the portletRequest object, and provide the name of the textfield, which in this case 'number'. The input of the user will be returned as a String. Again, anything you want to know about the user, which includes what the user typed into a given textfield, is obtained through the PortletRequest object, or more specifically during the rendering phase, the RenderRequest.

With the taglib directive and the <portlet:defineObjects/> tag added to a JSP, any expression that uses objects defined in the portlet API will compile and run successfully, including references to the action attribute of a form that references the portletResponse and the createRenderURL method.

 <FORM  ACTION = "<%=renderResponse.createRenderURL()%>" >

Coding the NumberGuesserPortlet

The following text shows the code required to enhance our NumberGuesserPortlet to the point where it can extract form data from the user. Notice how this portlet acts largely as a controller, dispatching to the input form named numberguesser.jsp if no data has been submitted, and alternatively, if a number has been submitted, the portlet provides interactive feedback to the user, namely information on whether the user correctly guessed the magic number.

package com.mcnz.portlet;
import java.io.*;import javax.portlet.*;
public class NumberGuesserPortlet extends GenericPortlet {   protected void doView(RenderRequest request, RenderResponse response) throws PortletException, IOException {     String number = request.getParameter("number");     if (number == null) {       String url = "/numberguesser.jsp";      getPortletContext().getRequestDispatcher(url).include(request,response);     } else {       response.setContentType("text/html");       PrintWriter out = response.getWriter();       out.print("You guessed " + number);       out.print("<BR>The number was 5");       out.print("<A href=\"");       out.print(response.createRenderURL());       out.print("\">Try Again</A>");     }   } }


When the code is compiled, and the NumberGuesserPortlet is deployed to the portal server, the interaction in the portlet window looks as follows:

Figure 4-4. The NumberGuesserPortlet at runtime.

Summary

There are many benefits to using a portal server to manage the delivery of content and applications to your end users, but as with everything in life, where there are benefits there are also drawbacks. With regards to developing portlets, sometimes things that are relatively easy to do with the Servlet and JSP API become a tad more complicated with portlets, and that is certainly the case when it comes to working with creating links that point back to a given portlet instance. Of course, if you understand the issues that come with the unpredictability as to where a portlet might appear in a given set of portal pages, and you match that understanding with the knowledge of how to create a PortletURL object, or even encode a namespace for JavaScript and fields on an HTML form, the added complexity that arises from using a portal server can be dealt with quite easily.

How has the JSR-286 Portlet API change how you develop and deliver your web applications? Let us know.

Recommended Titles

Liferay Portal Systems Development By Jonas X. Yua
Liferay in Action By Richard Sezov
OSGi in Action By Richard Hall
Enterprise OSGi in Action By Holly Cummins
The Well-Grounded Java Developer By Martijn Verburg

Next Steps

New to Git and distributed version control? Here are some Git examples and Jenkins-Git integration tutorials designed to help you master the popular source code versioning tool.

Dig Deeper on Front-end, back-end and middle-tier frameworks

App Architecture
Software Quality
Cloud Computing
Security
SearchAWS
Close