Logo

Unicus Associates - Specializing in the IBM product families - WebSphere and Lotus

  • image 1

     

    The following article appeared in "The Sphere Journal", May/June 2006, (Volume 2, Issue 2)

    Getting started developing portlets with the JSR 168 Portlet API

     

     by Susan Bryant

    Whether you’ve been developing portlets for a while or are just starting out, you need to get JSR 168 on your radar screen. Java Specification Report (JSR) 168 is a set of portlet application programming interfaces (APIs) that represents a powerful first step for the Java community toward an industry standard for writing and hosting portlets and portlet applications. An industry standard means that developers of portlet applications can rely on portal vendors providing consistent and reliable server environments in which their portlet applications can run. Thus, with a little help navigating this API, you can make your portlet applications portable to a wider range of standard-compliant server vendors.

    Because the JSR 168 Portlet API is very similar to the Java Servlet API, experienced Java servlet programmers may think that the portlet API is simple. They’re right; it is simple --- deceptively so. It’s been my experience in teaching portlet development that those who assume JSR 168 is wholly familiar are likely to be lulled into a false sense of security. As a result, developers tend to end up pounding their laptops in frustration and screaming, “That is not what I expected!” Yes, the portlet API is familiar, but it’s just a little bit foreign too. 

    You can find many articles on the Web that provide examples of portlet applications built using the JSR 168 Portlet API, but many of these examples tend to be complicated. Also, they usually assume that you have an advanced knowledge of portlet containers and Web application development. Even experienced developers can become frustrated trying to learn a new and powerful framework with only light explanations of complex applications. Think of the time wasted hunting down the missing subtle pieces of the framework or missing links in the explanations.

    To give you a boost along the JSR 168 learning curve, this article takes a different, more efficient approach. As it guides you through the steps to build a very simple portlet, it demonstrates the basics of portlet development and shows how to hook the fundamental pieces together to get your portlets working and interoperating. The examples are deliberately clear of any code that is not relevant to the topic at hand. With this approach, my intent is to allow you to remain focused instead of having to break your train of thought to figure out how all the parts of a complex sample application fit together.

    So, who will benefit from this article?

    Java Web developers. Learn how to apply the fundamentals of portlet application development to get a portlet up and running quickly.

    Developers familiar with the IBM Portlet API.Note 1Get a quick start on learning the differences between the legacy IBM Portlet API and JSR 168.

    Web developers (non-Java) who have not worked with portlets before. Use this article to get up to speed quickly and gain the knowledge you need to understand more complicated examples.

    Java developers (non-Web). Though you may find the content a bit of a struggle, it is certainly approachable and will help you get started developing portlets. 

    • To keep the lines of learning simple and clean, I don’t address any WebSphere-Portal-specific aspects of JSR 168 in this article. That topic is for another time. 

    I’ll begin with a brief review of portlets and the tools developers use to build them. Then we’ll build a basic portlet and gradually add to its functionality as we explore how to use the JSR 168 Portlet API and exploit its capabilities.

     

    What are portlets?

     

    Portlets are essentially small interfaces into larger applications. Unlike traditional Web applications, in which one primary application uses all or most of the screen real estate, developers write portlets as self-contained interfaces that a larger portal solution can use. Each portlet can be placed on a Web page, either alone or with other portlets, to create a portal site. To make the portlets available to the users of the portal site, portal administrators place the portlets on pages by means of an administrative interface.

    Portlets are contained in portlet applications. A portlet application, like a JavaServer Pages (JSP) page or a servlet, is contained in a Web application. Where a straight Web application (i.e., one that contains JSP pages or servlets) simply has a web.xml file, a portlet application has a web.xml file plus a portlet.xml file and some other configuration files. A portal solution can integrate many portlet applications, each with a different business purpose, into one unified interface and user experience.

     The portlet container provides the runtime environment for portlets: It manages URL namespaces so that portlets and portlet resources don’t conflict or collide in the portal namespace. In addition, much as the standard J2EE Web container manages servlets, the portlet container manages the portlet’s life cycle, rendering, personalization, security, and so forth.

    To set configuration parameters for portlet applications and portlets, developers declare the parameters in deployment descriptor files. Portlets can retrieve these parameters at runtime. Developers can modify configuration parameters via any text editor before deploying an application in the portal. They can even allow a portlet application and its portlets to be reused repeatedly by modifying some of the application configuration data for particular portlet instances. Thus, portlets allow for more deployment flexibility in a corporate environment, and they can help minimize code maintenance.

    Portlet applications and portlets provide rendering support for markup languages; multiple locales (e.g., time zone and preferred language system information); different modes (e.g., view and edit mode); and window states (e.g., minimized and maximized). After the portlet container checks for the preferred locale and markup language of the client device that is requesting a portlet, the portlet container aggregates and renders the response accordingly. 

     

    Portlet-building tools

     

    Portlet applications are primarily Java applications,Note 2and you can use any Java development tool to create them, though not all tools are created equal. For example, you could use your favorite text editor or Java integrated development environment (IDE), including Eclipse.Note 3To test a portlet application, you need a portlet container (generally, part of your portal server product) to provide the context in which your application runs.

    Rational Application Developer (RAD) is a powerful IDE from IBM that contains several productivity tools, including development wizards for creating skeletons of application artifacts. Many of these wizards are targeted specifically for developing and testing applications for the WebSphere Application Server and WebSphere Portal environments. Several server test environments are included with the tool, including WebSphere Portal. 

    You can develop the code examples I use in this article with any of the tools mentioned here. Based on my own preferences, I used RAD 6 with the WebSphere Portal 5.1 test environment to create and test the example code.

     

    Creating a basic portlet

     

    This section takes you through the building of a basic JSR 168 portlet named “SphereExamples.” For purposes of this demonstration, the sample portlet is part of a Web application named “SphereExamplesPortletView,” which is simply a JSP page that renders the portlet. 

     If you use RAD, you can have a portlet productivity wizard (for example, the Portlet Project wizard) create many of the necessary code objects (with skeleton code) for you. However, if you don’t thoroughly understand the code objects that the tool creates, you can become lost very quickly. So in building the example portlet, we’ll use the RAD Portlet Project (JSR 168) wizard and examine each artifact as we create it; then we’ll pull them all together. Please note that I am not providing step-by-step instructions here, because building portlets with a RAD wizards is a topic for an article in itself. For more detailed information on working with the RAD portlet wizards, I recommend reading Ken Rokicki’s article, “Quick and easy portlet development with RAD 6.”

    Figure 1 shows how to open the Portlet Project (JSR 168) wizard in RAD.

     

    Figure 1                      Opening the Portlet Project (JSR 168) wizard

     

     

     

    When you get to the screen shown in in Figure 2, you need to select the portlet type. Note that we’re creating a Basic JSR 168 portlet for this example. 

     

    Figure 2                      Selecting the portlet type

     

     

    After selecting the portlet type, you need to name the packaging for the Java code in the project. In Figure 3, note carefully how we assign the Portlet name (SphereExamples), Display name (SphereExamples portlet), Package prefix (sphereexamples), and Class prefix (SphereexamplesPortlet). Best practice is to give all of these elements the same name with the same case-sensitive spelling. However, for learning purposes, it serves us better to have these different spellings, so that when we examine various code artifacts, we’ll know right away what kind of thing we’re looking at.

     

    Figure 3                      Setting the portlet and portlet package names
    Design: Please spell SphereExamples with a cap ‘E’ in the Portlet name field and in the two Display name fields.

     

     

    RAD now creates some code artifacts, including the Java class file for the portlet.  On your project workbench, you can find the Java class file as shown in Figure 4.

     
    Figure 4                      The Java class file that RAD creates for the portlet

     

     

     

    Double-click on the SphereexamplesPortlet.java class file to open it in the editor.  It should look similar the code shown in Figure 5.

     

    Figure 5                      The SphereexamplesPortlet.java class file

     

    package sphereexamples;

    import java.io.*;

    import javax.portlet.*;

     

    /**
     *
     * A sample portlet based on GenericPortlet
     *
     */
    public class SphereexamplesPortlet extends GenericPortlet {

             public static final String JSP_FOLDER    = "/sphereexamples/jsp/";    // JSP folder name

             public static final String VIEW_JSP      = "SphereexamplesPortletView";         // JSP file name to be rendered on the view mode

     

             public static final String SESSION_BEAN  = "SphereexamplesPortletSessionBean";  // Bean name for the portlet session

             public static final String FORM_SUBMIT   = "SphereexamplesPortletFormSubmit";   // Action name for submit form
             public static final String FORM_TEXT     = "SphereexamplesPortletFormText";     // Parameter name for the text input

             /**
              * @see javax.portlet.Portlet#init(javax.portlet.PortletConfig)
              */
             public void init(PortletConfig config) throws PortletException{
                      super.init(config);
             }

             /**
              * Serve up the <code>view</code> mode.
              *
              * @see javax.portlet.GenericPortlet#doView(javax.portlet.RenderRequest, javax.portlet.RenderResponse)
              */
             public void doView(RenderRequest request, RenderResponse response) throws PortletException, IOException {
                      // Set the MIME type for the render response
                      response.setContentType(request.getResponseContentType());

                      // Check if portlet session exists
                      SphereexamplesPortletSessionBean sessionBean = getSessionBean(request);
                      if( sessionBean==null ) {
                               response.getWriter().println("<b>NO PORTLET SESSION YET</b>");
                               return;
                      }
            

                      // Invoke the JSP to render
                      PortletRequestDispatcher rd = getPortletContext().getRequestDispatcher(getJspFilePath(request, VIEW_JSP));
                      rd.include(request,response);
             }

             /**
              * Process an action request.
              *
              * @see javax.portlet.Portlet#processAction(javax.portlet.ActionRequest, javax.portlet.ActionResponse)
              */
             public void processAction(ActionRequest request, ActionResponse response) throws PortletException, java.io.IOException {
                      if( request.getParameter(FORM_SUBMIT) != null ) {
                               // Set form text in the session bean
                               SphereexamplesPortletSessionBean sessionBean = getSessionBean(request);
                               if( sessionBean != null )
                                        sessionBean.setFormText(request.getParameter(FORM_TEXT));
                      }
             }

             /**
              * Get SessionBean.
              *
              * @param request PortletRequest
              * @return BasicPortletSessionBean
              */
             private static SphereexamplesPortletSessionBean getSessionBean(PortletRequest request) {
                      PortletSession session = request.getPortletSession();
                      if( session == null )
                               return null;
                      SphereexamplesPortletSessionBean sessionBean = (SphereexamplesPortletSessionBean)session.getAttribute(SESSION_BEAN);
                      if( sessionBean == null ) {
                               sessionBean = new SphereexamplesPortletSessionBean();
                               session.setAttribute(SESSION_BEAN,sessionBean);
                      }
                      return sessionBean;
             }

             /**
              * Returns JSP file path.
              *
              * @param request Render request
              * @param jspFile JSP file name
              * @return JSP file path
              */
             private static String getJspFilePath(RenderRequest request, String jspFile) {
                      String markup = request.getProperty("wps.markup");
                      if( markup == null )
                               markup = getMarkup(request.getResponseContentType());
                      return JSP_FOLDER+markup+"/"+jspFile+"."+getJspExtension(markup);
             }
            
             /**
              * Convert MIME type to markup name.
              *
              * @param contentType MIME type
              * @return Markup name
              */
             private static String getMarkup(String contentType) {
                      if( "text/vnd.wap.wml".equals(contentType) )
                               return "wml";
                      return "html";
             }

             /**
              * Returns the file extension for the JSP file
              *
              * @param markupName Markup name
              * @return JSP extension
              */
             private static String getJspExtension(String markupName) {
                      return "jsp";
             }

    }

     

     

     

    Note in the code that the Java class extends javax.portlet.GenericPortlet (shown in bold font). The javax.portlet package contains the JSR 168 objects. The Java portlet class inherits from this generic class much of the basic behavior that makes the Java code JSR 168 Java portlet code.

     

     

    Note!

     

    For brevity, most * of the code examples that follow do not include import statements. In your own code, however, remember that you must include import statements in order to use JSR 168 objects and other Java API objects. To import all JSR 168 objects, you can use a statement like

    import javax.portlet*

    Or, you can import one object at a time.

     

     

    RAD creates the .java class code for us, so some of it may not be necessary for our purposes. We’ll focus only on the parts of this file necessary to achieving our primary goal, which is to display a simple Welcome portlet, provide some basic help text for the user, and allow the user to edit a value. 

    The GenericPortlet Java class implements the Portlet and PortletConfig interfaces and defines the mode-specific render methods doView(), doEdit(), and doHelp(). (Note that Figure 5 only shows the code for the doView method.) We’ll implement our business logic by overriding these methods.

     

    Portlet modes and rendering: The view mode

     

    Portlets can be put into different modes (view, edit, or help) either programmatically or through a user interaction with the portlet. By implementing logic in the appropriate mode-specific methods, you can cause the portlet container to use these methods to render the portlet according to the mode the client requests. For example, to define the page that renders the portlet in view mode, implement the doView() method of the portlet, as shown in Figure 6. Note that the SphereExamplesPortletView.jsp page this code invokes can be as simple as this:

     

    <H3 style="margin-bottom: 3px">Welcome!</H3>

    This is a sample <B>view mode</B> page.

     

     

    Figure 6    The doView() method invokes the JSP page that renders the SphereExamples portlet.

    ______________________________________________________________________

    public void doView(RenderRequest request, RenderResponse response) throws PortletException, IOException {

                 

    // Invoke the JSP to render

    PortletRequestDispatcher rd = getPortletContext().getRequestDispatcher(getJspFilePath(request, “/sphereexamples/jsp/html/SphereExamplesPortletView.jsp”));

         rd.include(request,response);

        

    }

    _________________________________________________________________

     

    Every portlet must at least provide support for view mode. Notice that you don’t need to implement any special JSP tags or processing to create a portlet. However, because developers generally accomplish portlet rendering with a JSP file, the rendered page itself can benefit from the skills of a good JSP programmer. (I know what you’re wondering, and the answer is yes: to become a proficient portlet developer, you also need to learn the ins and outs of JSP programming.)

    The doView() method in Figure 6 is very simple and should look very familiar to any Java servlet developer. This portlet simply uses the include() method of PortletRequestDispatcher to call a page to render. The request dispatcher is obtained from the implicit PortletContext object by calling the getPortletContext() method. The PortletContext object gives access to the services of the portlet container.

     Also shown in Figure 6, the portlet uses the fully qualified name of the JSP page, including path. The RenderRequest and RenderResponse object instances passed to the doView() method represent handles to the request and response streams provided by the container. Generally, you can use these objects much the same way you use HTTPServletRequest and HTTPServletResponse objects in a servlet context. You can use them to store and retrieve data parameters or to glean information about a request. Because portlets write only a portion of the content of the JSP page and the portlet container manages the overall portal namespace, portlets are not allowed to send redirects or forwards. A portlet sending a traditional redirect or forward might cause the portlet to render outside of the portal (possibly in a new window, depending on the browser) thereby breaking the portal model. 

     

     

    Note!

     

    Your development goal should be to always play by the framework rules to guarantee that your code works within the given framework. This way of working is the most efficient.  If you’re spending your development time circumventing the framework, it’s possible that you have chosen the wrong framework for the project.

     

     

    Note that “rendering” in JSR 168 actually consists of two phases: the action processing phase and the render phase. I explain this architecture and its benefit in detail later in the section titled “Using action handling and the PortletPreferences object to store and retrieve data.”

    Obviously, before your portlet code calls the include() method to hand off rendering to the JSP page, you can have any amount of processing logic executed, including conditional testing and the instantiation of  Enterprise JavaBeans (EJB) components. You can also have any amount of JSP processing performed. However, I promised to keep this discussion focused and simple, so we’ll move on.

    Now, how does the doView() method get triggered? As mentioned, part of the portlet container contract for JSR 168 is to handle the life cycle and rendering of portlets. And, much as a servlet container calls the doGet() method from the service() method of a Java servlet in response to a URL GET request, the portlet container calls doView() from the portlet’s render() method in response to a request to render the portlet in view mode. When the page that the portlet is placed on is requested, the portlet itself is requested as a result. Generally, the portlet code developer does not overwrite the inherited render() method when writing a portlet, just as a servlet developer does not overwrite the inherited service() method when writing a Java servlet. 

    To initialize portlets, the container calls each portlet’s init() method once, when it first puts the portlet into service. The portlet container also calls each portlet’s destroy() method once, as it takes the portlet out of service. The portlet’s init() and destroy() methods are similar to the init() and destroy() methods in the Java servlet container. In the example portlet, we could implement the init() or destroy() methods; however, for the purpose of demonstrating how to handle the doView() method, it isn’t necessary to implement those methods.

    Each time a request is made to render a portlet, the portlet’s render() method is called. The render() method dispatches the request to the more specific doView(), doEdit(), or doHelp() methods. A simple browser request to view the portlet causes the portlet container to dispatch the request to the doView() method, which in turn renders the portlet using the designated JSP page. It’s as simple as that! Likewise, if the portlet were being rendered in edit mode, the developer would implement the doEdit() method for rendering in that mode (in help mode, he or she could implement the doHelp() method).

     

    Packaging the portlet

     

    Portlet application packaging is very similar to J2EE Web application packaging. In fact, a portlet application is an extension of a J2EE Web application. As mentioned earlier, a portlet application requires a web.xml file just as a straight Web application does. You need to include a web.xml deployment descriptor file in the portlet package to contain the required placeholder for the portlet application. Figure 7 shows the web.xml file deployment descriptor for our very simple Web application, which contains a very simple portlet.

     

    Figure 7          The web.xml file for a simple Web application that contains a portlet

     

    <?xml version="1.0" encoding="UTF-8"?>

    <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd">

    <web-app id="WebApp_ID">

         <display-name>SphereExamples</display-name>

        

    </web-app>

     

      

    Nothing more is required in web.xml for our portlet application. However, the application also has a deployment descriptor called portlet.xml, which we use to package and configure the portlet application as well as its portlets. Figure 8 shows the portlet.xml file for our simple portlet application.

     

    Figure 8                      The portlet.xml file for a simple portlet application

     

    <?xml version="1.0" encoding="UTF-8"?>

    <portlet-app

    xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd" version="1.0"

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd" id="sphereexamples.SphereExamplesPortlet.da9f2c1260">

          <portlet>

                      <portlet-name>SphereExamples</portlet-name>

                      <display-name>SphereExamples portlet</display-name>

                      <display-name xml:lang="en">SphereExamples portlet</display-name>

                      <portlet-class>sphereexamples.SphereExamplesPortlet</portlet-class>

                      <init-param>

                                  <name>wps.markup</name>

                                  <value>html</value>

                      </init-param>

                      <expiration-cache>0</expiration-cache>

                      <supports>

                                  <mime-type>text/html</mime-type>

                                  <portlet-mode>view</portlet-mode>

                                 

                      </supports>

                     

                      <portlet-info>

                                  <title>SphereExamples portlet</title>

                      </portlet-info>

          </portlet>       

    </portlet-app>

     

     

    Place both deployment descriptor files in the WEB-INF folder of the application. WEB-INF is a standard J2EE folder containing the necessary configuration files for a J2EE application, including portlet application configuration files (such as the portlet.xml file). If you’re using RAD, the WEB-INF folder appears under the expanded WebContent folder (below your project folder), as shown in Figure 9.

     

    Figure 9                      The WEB-INF folder

     

    There are no dependencies between these two deployment descriptors files as there are in the IBM Portlet API. The portlet.xml deployment descriptor file simply describes the portlet application and each portlet contained therein.

     Our simple portlet application has only one portlet (SphereExamples), which so far does just four things: supports view mode, provides one initialization parameter, supplies a title for the portlet, and configures the expiration cache.  Let’s look more closely at the initialization tag and expiration cache tag in Figure 8.

    The RAD tool placed the <init-param> tag in portlet.xml with a value of wps.markup. WebSphere Portal uses this tag to indicate that the markup language is HTML. The tag is not required, but it gives us a window into the possibility of defining configuration parameters for each instance of the portlet.

    The <expiration-cache> tag notifies the portlet container of the caching strategy for the portlet. It can have one of the following values:

     0 — indicates that the portlet content is never cached.

    -1 — signifies that the portlet’s cache never expires.

    a positive integer — configures this portlet’s content to be cached (for improved performance) by the container for x number of seconds, determined by the value.

    In this case, the value is 0.

    Figure 10 shows the rendering of this simple portlet in the WebSphere Portal test environment in RAD 6.

     Figure 10        The example portlet rendered in the WepSphere Portal test environment

     

    You may be thinking, “Gee, that was easy. But will my portlet ever need more than one mode, and if so, how does a portlet switch modes?”

     

    Switching modes: How to switch to the help mode

     

    To learn how to switch portlet modes, we’ll start with the help mode since its purpose is relatively easy to discern. A developer can use the help mode to provide a user with easily accessible assistance. To provide a help page, implement the doHelp() method and change the portlet.xml file to notify the container that the help mode is supported. If you don’t make this change in portlet.xml, the container doesn’t render the appropriate icon in the user interface (UI), and users can’t access your beautifully detailed help description.

     

    Figure 11 shows the example doHelp() method, which looks similar to the previous doView() method. Note that doHelp() includes the page SphereExamplesPortletHelp.jsp, which for this example looks like this:

     

    <H3 style="margin-bottom: 3px">Welcome!</H3>

    This is a sample <B>help mode</B> page.

     

    To add support for the help mode to the example portlet.xml file we’ve been using (refer to Figure 8), modify the file as shown in Figure 12 (the changes are in bold). Note that the polished default title now reads “The Sphere Examples.” (We could change the portlet application title in the web.xml file as well, but let’s keep things simple.  Generally, only the portal administrator sees the portlet application title when he or she installs the application in the portal; the average user doesn’t usually see it.)

     

     

    Figure 11                    The doHelp() method

     

     

    /**

           * Serve up the <code>help</code> mode.

           *

           * @see javax.portlet.GenericPortlet#doHelp(javax.portlet.RenderRequest, javax.portlet.RenderResponse)

           */

          protected void doHelp(RenderRequest request, RenderResponse response) throws PortletException, IOException {

                     

     

                      // Invoke the JSP to render

                      PortletRequestDispatcher rd = getPortletContext().getRequestDispatcher("/sphereexamples/jsp/html/SphereExamplesPortletHelp.jsp");

                      rd.include(request,response);

          }

     

     

     

    Figure 12                    The example portlet.xml file ¾ modified to support help mode

     

    <?xml version="1.0" encoding="UTF-8"?>

    <portlet-app xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd" version="1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd" id="sphereexamples.SphereExamplesPortlet.da9f2c1260">

          <portlet>

                      <portlet-name>SphereExamples</portlet-name>

                      <display-name>SphereExamples portlet</display-name>

                      <display-name xml:lang="en">SphereExamples portlet</display-name>

                      <portlet-class>sphereexamples.SphereExamplesPortlet</portlet-class>

                      <init-param>

                                  <name>wps.markup</name>

                                  <value>html</value>

                      </init-param>

                      <expiration-cache>0</expiration-cache>

                      <supports>

                                  <mime-type>text/html</mime-type>

                                  <portlet-mode>view</portlet-mode>

                                  <portlet-mode>help</portlet-mode>

                                 

                      </supports>

                     

                      <portlet-info>

                                  <title>The Sphere Examples</title>

                      </portlet-info>

          </portlet>       

    </portlet-app>

     

    When we use the same WebSphere Portal test environment as before and redeploy the application after making the changes, RAD dynamically deploys and reloads the changed code. There’s no need to restart the server. The results are subtle, as you see in Figure 13. The new portlet title is displayed, and the the container has added a help icon  to the portlet title window.  This icon allows the user to access help mode. Clicking the  icon in a browser causes the portlet container to call the portlet’s doHelp() method and in the example, opens a new window with a help page, as shown in Figure 14.

     

    Figure 13                    Result of changing the default portlet title in portlet.xml

    <<Design: Circle “The Sphere Examples” that appears above “Welcome!” as well as the Help question mark box in the top right of this screen shot.>>

     

     

    Figure 14                    A portlet in help mode opens in a new window.

     

    This relatively simple and straightforward example has revealed some of the power of a portlet container to render context-appropriate information based on the support the portlet developer provides ¾ plus, it has demonstrated the entire process from development through deployment and rendering.

     

    The edit mode and storing portlet-specific data

     

    When you design your application, you may find you need to store and retrieve portlet-specific data. For example, if a portlet retrieves stock quote information, you may need to store a list of the user’s favorite stock symbols and provide an interface for users to customize that list. Or, if the portlet needs to store a user’s e-mail file location to retrieve the user’s mail for rendering, you might allow the user to type in this information once and have the portlet save it for later use.

    To store portlet-specific data, you can use the PortletPreferences object (explained later) and the doEdit() method. The portlet container calls the doEdit() method of a portlet if the container is rendering the portlet in edit mode. You saw how easy it was to provide an interface for users to switch to help mode. Now, let’s take a look at the fundamentals of providing an edit mode in the portlet.

    First, let’s implement the doEdit() method of the portlet class. The implementation can be as simple the one shown in Figure 15.

     

    Figure 15        The doEdit() method

     

     

          /**

           * Serve up the <code>edit</code> mode.

           *

           * @see javax.portlet.GenericPortlet#doEdit(javax.portlet.RenderRequest, javax.portlet.RenderResponse)

           */

          public void doEdit(RenderRequest request, RenderResponse response) throws PortletException, IOException {

          // Invoke the JSP to render

          PortletRequestDispatcher rd = getPortletContext().getRequestDispatcher("/sphereexamples/jsp/html/SphereExamplesPortletEdit.jsp");

          rd.include(request,response);

          }

     

     

    Next, create a simple JSP page in edit mode, like the one shown in Figure 16. As we go along, we’ll modify this page a few times to see different behaviors rendered in edit mode.

     

    Figure 16                    A basic JSP page for edit mode

     

    <%@ page session="false" contentType="text/html" import="java.util.*,javax.portlet.*,sphereexamples.*"%>

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

    <portlet:defineObjects/>

     

    <DIV style="margin: 6px">

     

    <H3 style="margin-bottom: 3px">Welcome!</H3>

    This is a sample <B>edit mode</B> page.

     

    <H3 style="margin-bottom: 3px">Write to the PortletPreferences</H3>

    This is a sample form to demonstrate how to change the PortletPreferences when the portlet is in edit mode.

    <DIV style="margin: 12px; margin-bottom: 36px">

     

     

      <FORM ACTION="<portlet:actionURL/>" METHOD="POST">

        <LABEL>New Value</LABEL><BR>

        <INPUT name="fieldOne" type="text"/><BR>

        <INPUT name="submit" value="Save" type="submit"/>

      </FORM>

     

    </DIV>

     

    <FORM ACTION="<portlet:renderURL portletMode="view"/>" method="POST">

          <INPUT NAME="back" TYPE="submit" VALUE="Back to view mode">

    </FORM>

    </DIV>

     

     

    This page is little more complex than a standard JSP page. Let’s look at some of the portlet-specific code it contains:

    First, we’re using the standard JSR 168 portlet tag library (i.e., declaring taglib and instantiating the portlet JSP objects by calling the <portlet:defineObjects/> tag. RAD validates (at design time, as opposed to runtime) that there is a tag, that any required parameters for the tag are present, and that we’re using the tag in a valid way; otherwise, RAD shows an error.

    Second, since we want this page to submit values (which later will be stored in the PortletPreferences object), we have added a simple HTML form with one field called “fieldOne” to capture the data (this field is labeled “New Value” in the UI).

    Third, we have added another form to the JSP page to allow easy access back to the view mode, even though the container automatically adds a return button to the portlet title bar (shown later). This form’s action attribute determines what happens after the form is posted and saves the user the step of manually clicking a button to switch modes.

    Note that each of the forms we added uses a portlet URL tag to associate the appropriate action with the form submission: <portlet:actionURL/> for the form that captures data, and <portlet:renderURL> for the form that brings the user back to the view mode page. You’ll see how to process these actions a bit later, in the section titled “Using action handling and the PortletPreferences object to store and retrieve data.”

    For now, let’s take a closer look at the second form’s action tag (see the bottom of Figure 16). The <portlet:renderURL> tag has its portletMode attribute set to “view.” By adding this portlet mode to the portlet URL, we provide a mechanism for dynamic mode switching in a JSP page. We could provide the same mechanism in the portlet code by using the javax.portlet.PortletMode object to programmatically switch modes. For example:

     

     

    response.setPortletMode(PortletMode.VIEW);

     

     

    where response is an object of type javax.portlet.ActionResponse and VIEW is an integer field in the PortletMode class. The ActionResponse object is passed to the processAction() method for action handling.

     

    To notify the container that we’re providing support for edit mode in the portlet, modify portlet.xml, as shown in Figure 17.

     

    Figure 17        Portlet.xml modified to support edit mode

    <supports>

                                  <mime-type>text/html</mime-type>

                                  <portlet-mode>view</portlet-mode>

                                  <portlet-mode>help</portlet-mode>

                                  <portlet-mode>edit</portlet-mode>

                      </supports>

    When we redeploy the application, the added functionality is available, although it is not very functional at this point. As you can see in Figure 18, the initial view mode looks a little different now.

     

    Figure 18        The initial view mode after adding support for the edit mode

    <<Design: Circle the pencil icon in the lower right>>

    Because we added support for the edit mode, the container has rendered the  icon (circled in Figure 18). When the user clicks this pencil icon, the edit mode page is displayed, as shown in Figure 19.

     

    Figure 19                    The edit mode page

     

     

    Clicking the “Back to view mode” button returns the portlet to the view mode page, as you would expect. Entering a value into the New Value field and clicking the Save button does nothing except render the portlet again in the same view mode page and clear any value you entered in the field. That’s not exactly what we want to happen. We want to take the value the user enters into this field, store it in the PortletPreferences object, and have access to it at any time. So far, we haven’t stored anything, though. In the next section, you’ll see how to store and the retrieve data.

     

    Using action handling and the PortletPreferences object to store and retrieve data

     

    As mentioned earlier, a JSR 168 container processes portlets in two phases: the action processing phase and the render phase. Up to this point, I’ve only discussed the render() methods of the JSR 168 Portlet API. When a request URL has an action attached to it, the action processing phase triggers the processAction() method of a specific portlet. The container then triggers the portlet’s render() method, which dispatches to the appropriate handler methods, such as doView() and doEdit(). To summarize: 

    Each portlet’s render() method is triggered each time a request is made to render the page. A particular portlet’s processAction() method is triggered only when the URL for that portlet is an action URL.

    As you saw, we triggered an action in the portlet with the first form’s action attribute on the edit mode JSP page (by adding a <portlet:actionURL/> tag  as shown in Figure 16); however, we haven’t handled the action yet. This section of the article covers the fundamentals of action handling in JSR 168 portlets. The action that we’ll handle involves managing portlet settings so you’ll learn about the PortletPreferences object as well.

    First, let’s implement the processAction() method in the example portlet, which on this ascending end of the learning curve can be as simple as the code in Figure 20.

     

    Figure 20                    The processAction() method of the example portlet

     

    public void processAction(ActionRequest request, ActionResponse response)

                                  throws PortletException, java.io.IOException {

     

                      PortletPreferences prefs = request.getPreferences();

                      String value = request.getParameter("fieldOne");

                      System.out.println("In processAction() method");

                      if (value != null) {

                                  prefs.setValue("ExampleValue", value);

                                  prefs.store();

                      }

          }

     

     

    Now, let’s see what happens when the URL of a portlet is an action URL and the portlet is requested. When the edit mode JSP form in the example is submitted, the container first calls the processAction() method before passing control to the render() method. Keep in mind that in a portal with multiple portlets on the page, which is a common scenario, only one processAction() method is called and that is the one for the portlet with the action attached to its URL. All portlet render() methods are called as the page is rendered again. The container guarantees that the action processing phase completes before the render phase begins. 

    Notice in Figure 20 that the request and response objects passed to the processAction() method are of type ActionRequest and ActionResponse respectively. Remember that the doView(), doHelp(), and doEdit() methods receive RenderRequest and RenderResponse objects. It is important to keep in mind that the ActionRequest and ActionResponse objects of a JSR 168 container are not the same as RenderRequest and RenderResponse objects. They are separate objects entirely and do not share data. This model differs from the traditional servlet container, which passes the same HTTPServletRequest and HTTPServletResponse objects through the request/response stream.

    The JSR 168 Portlet API provides methods for setting parameters on the render objects from the action processing phase; you can use these methods when you need to pass data from the action processing phase to the render phase. For example, if your application needs to process logic during the action processing phase (a common scenario), and it needs to make the action processing results available for the render phase, your processAction() method could call one of the setRenderParameter() or setRenderParameters() methods on the ActionResponse object. This would make the data that is set in the action processing phase available to the render phase. You could then retrieve the data during the render phase using the getParameter() or getParameters() methods of the RenderRequest object, in the same way you would retrieve other request parameters. 

    Our processAction() method does little more than store any non-null value entered in the fieldOne field. It stores the value in the portlet’s PortletPreferences object. Depending on the needs of the application, there are a couple of other places where the method could store this data:

    In the portlet session to make it available for the life of the user’s session with this portlet (an example of this usage appears in a later section)

    In the current request/response stream to make it available only until the completed response is sent to the client

    In the example portlet, the PortletPreferences object stores the value because this object represents a handle to a convenient per-portlet, per-user record in the portal’s persistent datastore. When building your own portlets, always look to your application’s data visibility and persistence needs to determine where to store the data.

    To prove that we’ve stored something and can retrieve it, we should retrieve and use what we’ve stored. Let’s modify the doView() method to retrieve the value stored as “ExampleValue” in the PortletPreferences object and display a helpful message if the value hasn’t been set yet). The revised doView() method in Figure 21 reflects our revisions in bold font. The portlet will behave the same as it did before, except that now it will print out a message in the system console to indicate when the method has been called.

     

    Figure 21        Adding render behavior in the Java doView() method

     

    public void doView(RenderRequest request, RenderResponse response)

                                  throws PortletException, IOException {

                       //Set the MIME type for the render response

                      response.setContentType(request.getResponseContentType());

     

                      // Invoke the JSP to render

                     

                      PortletPreferences pref = request.getPreferences();

                      String value = pref.getValue("ExampleValue", null);

                     

                      if( value==null ) {

                                   response.getWriter().println(" <b>No preferences set.  Please press enter edit mode to set your preferences. </b>");

                                   return;

                      }

                      PortletRequestDispatcher rd = getPortletContext().getRequestDispatcher(

                                              "/sphereexamples/jsp/html/SphereExamplesPortletView.jsp");

                      rd.include(request, response);

          }

       

     

    If the PortletPreferences value has not been set, the user is presented with instructions to switch to edit mode to enter the value. You could take this approach with a portlet that requires user-specific information (such as a password or an e-mail location) that users need to enter once, when they first access your portlet. The container stores one PortletPreferences object per portlet, per user. After the improved application is redeployed, the first rendering of the portlet displays, as shown in Figure 22. When the user switches to edit mode (i.e., clicks the pencil icon), enters a value into the field, and clicks Save, he or she automatically returns to the view mode page, as shown in Figure 23.

     

    Figure 22        The user is directed to enter edit mode (i.e., click the pencil icon), if no preferences are set.

     

    Figure 23        After the user saves an entry on the edit page, the user is returned to view mode.

     

    The PortletPreferences object

     

    Now, let’s take a look at using the PortletPreferences object in edit mode to populate the fieldOne field with the current value (if it exists) as the default value. Modify the JSP page that renders the portlet in edit mode, as shown in Figure 24.

     

    Figure 24                    JSP page modified to render in edit mode

     

    <H3 style="margin-bottom: 3px">Welcome!</H3>

    This is a sample <B>edit mode</B> page.

     

    <H3 style="margin-bottom: 3px">Write to the PortletPreferences</H3>

    This is a sample form to demonstrate how to change the PortletPreferences when the portlet is in edit mode.

    <DIV style="margin: 12px; margin-bottom: 36px">

    <%PortletPreferences prefs = renderRequest.getPreferences();

    String value = prefs.getValue("ExampleValue",""); %>

     

      <FORM ACTION="<portlet:actionURL/>" METHOD="POST">

        <LABEL>New Value</LABEL><BR>

        <INPUT name="fieldOne" type="text" value="<%=value %>"/><BR>

        <INPUT name="submit" value="Save" type="submit"/>

      </FORM>

     

     

    </DIV>

    When the <portlet:defineObjects/> tag at the top of the page is called (refer to Figure 16), it makes the renderRequest object available to the JSP page. The renderRequest object maps to the request object, which is a standard object available to all JSP pages. Just as we used the RenderRequest object in the portlet as a parameter to the processAction() method, we can use the request object in the JSP page to retrieve PortletPreferences. Simple JSP syntax then populates the field with the retrieved value if it exists.

     

    Recap

     

    So far, you’ve learned the fundamentals of how to:

  • Render a portlet
  • Enable an end user to switch the portlet mode
  • Render different content for different modes and notify the portlet container that your application supports multiple modes
  • Use a method to add an action to the portlet URL
  • Use a method to store and retrieve portlet data using the PortletPreferences object
  • Play in the portlet processing space provided by the portlet container, in both the action processing phase and the render phase
  • Now, let’s take a look at some of the other features of the JSR 168 Portlet API: validating preferences, loading and storing data during a session, and logging.

     

    Note 1: IBM originally developed its own Java API to support portlet development in WebSphere Portal. Many of the portlet API standards that IBM put forward to the Java community have now become part of the Java industry-standard API, JSR 168, which was finalized in October 2003. IBM has supported this API as part of WebSphere Portal since Version 5.0.2.1 and as a technology preview since early Version 5. JSR 168 is not identical to the IBM Portlet API; however, WebSphere Portal has a portlet container that is fully JSR-168 compliant, and this container can run seamlessly alongside the legacy container. The Portal system administrator and the portlet developer need not concern themselves with any incompatibilities while WebSphere Portal Server runs both containers together. One major difference between the two APIs, of course, is that the IBM Portlet API is IBM specific and cannot be reused in other portal servers.

    Note 2: However, be aware that portlet applications are outside of the J2EE specification and in many cases are not J2EE compliant.  

    Note 3: Eclipse is a free IDE available for download from eclipse.org. (Note: I would only recommend Eclipse with the plug-ins and context for portlet development; without them, Eclipse just isn’t as rapid an application development tool as some others).