Skip to main content

skip to main content

developerWorks  >  WebSphere  >

IBM WebSphere Developer Technical Journal: WebSphere Portal V4 Programming

Creating Common Portlet Controls Using Custom Tag Libraries

developerWorks
Document options

Document options requiring JavaScript are not displayed


Rate this page

Help us improve this content


Level: Intermediate

Michael Wanderski (mwanders@us.ibm.com), Advisory Software Engineer, IBM Pervasive Computing Development

10 Sep 2003

This article introduces the concept of custom JSP tag library development as a tool for creating visual controls for portlet development.

Introduction

Through content aggregation and personalization, IBM ® WebSphere® Portal V4 allows you to surface Web content and applications through a consolidated, easy-to-navigate interface. Users can selectively add applications to their portal-based desktop based on a variety of criteria, such as personal interests, corporate application access, departmental postings, etc.

Portals are typically viewed through rich browsers like Microsoft® Internet Explorer (IE) or Netscape NavigatorTM. These clients allow users to simultaneously view applications that can be a combination of many Web-based technologies such as HTML content, JavaTM applets and Macromedia FlashTM. These applications are surfaced through portal applications, called portlets, which allow the seamless integration of these Web-based technologies into the overall portal container framework through programmatic interfaces and portal-based services. A sample IBM WebSphere Portal screen is shown in Figure 1.


Figure 1. Sample WebSphere Portal server
Sample WebSphere Portal server

In this article, we will introduce custom JSP tag library development as a tool for creating visual controls for portlet development.

This article assumes at least a minimal level of IBM WebSphere Portal programming experience, as well as a basic knowledge of JSP and tag library programming. We will briefly review portlet programming through an example, and then build on that example to show the benefits of integrating tag library techniques.



Back to top


Review of portlet development

Let's start by reviewing a generic portlet example, which shows the basic constructs of portlet development. The portlet in Listing 1 simply displays the message "hello world", but will be expanded to incorporate the custom JSP tags in later sections. If you are unfamiliar with the portlet API, or with portlet installation and deployment, see the documentation in the WebSphere Portal InfoCenter (See Resources) for a complete API reference.


Listing 1. GenericPortletHtmlController.java
package com.ibm.wps.portlets.GenericPortlet;

import java.io.*;
import com.ibm.wps.portlets.*;
import org.apache.jetspeed.portlet.*;

public class GenericPortletHtmlController extends AbstractMVCController {

     protected static final String JSP_Directory = "/WEB-INF/GenericPortlet/";
     protected static final String JSP_View = "/GenericPortletView.jsp";

     protected PortletContext context = null;

     public void init(PortletConfig config) throws UnavailableException {
          super.init(config);
          context = config.getContext();
     }

     public void doView(PortletRequest request, PortletResponse response)
          throws PortletException, IOException { 
               context.include(
                    JSP_Directory + "html" + JSP_View, request, response);
     }
}

We first create a simple portlet controller class that extends the AbstractMVCController class. The only job the controller has in this simple example is to call the JSP to render the portlet view that resides in the portlet WAR in the \WEB-INF\GenericPortlet\html directory.


Listing 2. GenericPortletView.jsp
<%@ page language="java" contentType="text/html;charset=utf-8" %>

<%@ taglib uri="/WEB-INF/tld/portlet.tld" prefix="portlet" %>

<portlet:init/>

Hello World

We then create the simple portlet view using the JSP in Listing 2 that displays the message, "Hello World". In subsequent sections, we will use this portlet to integrate a custom tag library control.



Back to top


Creating a visual portlet service using a custom tag library

To create our visual portlet service, let's assume that the concept of a slideshow (a sequence of pictures through which you can navigate) is a control that would be useful to many portlet developers. Keeping this example as simple as possible, let's assume the slideshow control is straightforward and takes as input a set of URLs that point to images. The control will have two buttons, Next and Previous, for moving through the images in the slideshow. Figure 2 shows what the slideshow control will look like when pointing to the IBM logo. For the remainder of this discussion, we will assume you are familiar with custom tag library programming so we can jump right into the tag development. If you'd like further information on custom tag library programming, please refer to the Resources section.


Figure 2. Generic portlet JSP view
Generic portlet JSP view
  1. The first thing to do is create the tag library descriptor file (TLD) that will define the interface used to construct an instance of the slideshow control in a portlet JSP (Listing 3).

    Listing 3. Tag library descriptor file (mytags.tld)

    <?xml version="1.0" encoding="UTF-8"?>
    
    <!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN" 
    "http://java.sun.com/j2ee/dtds/web-jsptaglibrary_1_1.dtd">
    
    <taglib> 
         <tlibversion>1.0</tlibversion>
         <jspversion>1.1</jspversion>
         <shortname>MyTags</shortname>
         <uri></uri>
         <info>Custom tags used for the Portal</info>
         <tag>
              <name>addControl</name>
              <tagclass>com.ibm.wps.tags.AddControl</tagclass>
              <bodycontent>JSP</bodycontent>
              <info>Create an instance of the Control</info>
              <attribute>
                   <name>name</name>
                   <required>yes</required>
                   <rtexprvalue>true</rtexprvalue>
              </attribute>
              <attribute>
                   <name>jsp</name>
                   <required>yes</required>
                   <rtexprvalue>true</rtexprvalue>
              </attribute>
              <attribute>
                   <name>jspContext</name>
                   <required>no</required>
                   <rtexprvalue>true</rtexprvalue>
              </attribute>
         </tag>
         <tag>
              <name>addParam</name>
              <tagclass>com.ibm.wps.tags.AddParam</tagclass>
              <bodycontent>JSP</bodycontent>
              <info>Add a parameter to the Control</info>
              <attribute>
                   <name>param</name>
                   <required>yes</required>
                   <rtexprvalue>true</rtexprvalue>
              </attribute>
         </tag>
    </taglib>

  2. In this file, called mytags.tld, we've defined two cooperating tags. The first, <addControl>, is used to construct an instance of the slideshow control and takes as input three attributes as described in the table below:

    Attribute NameDescription
    NameIs used to provide a globally unique name for the control. This is especially important since multiple instances of the control can be inserted on the same page, which is typical of the portal server environment.
    jspThe control is actually rendered using a JSP, as will be shown later. This allows all the tag library code to be reusable for different types of controls you might develop. The rendering of the visual aspects of the control is deferred to the JSP that can be dynamically assigned.
    jspContextThe JSP context allows the location of the JSP doing the rendering to be passed as a parameter. This allows the control to run in many environments (e.g. portal server, Standalone EAR, etc).

  3. The second tag is called <addParam> and allows a variable number of parameters to be passed in to the control. We will leverage this ability to pass a list of image URLs for the slideshow control. The tags can be used together as in the example in Listing 4:

    Listing 4. Example tag usage

    <mytags:addControl name="MyControl" jsp="control.jsp" jspContext="/">
    
         <mytags:addParam param="http://www.yourco.com/image1.gif/>
         <mytags:addParam param="http://www.yourco.com/image2.gif/>
         <mytags:addParam param="http://www.yourco.com/image3.gif/>
    
    </mytags:addControl>

  4. We now define the implementation of the <addControl> tag:

    Listing 5. AddControl.java

    package com.ibm.wps.tags;
    
    import java.util.*;
    import javax.servlet.*;
    import javax.servlet.jsp.*;
    import javax.servlet.jsp.tagext.*;
    
    public class AddControl extends BodyTagSupport {
    
         private String name = null; 
         private String jsp = null;
         private String jspContext = "/";
         private Control aControl = null;
    
         public void setName(String name) { this.name = name; }
         public String getName() { return name; }
    
         public void setJsp(String jsp) { this.jsp = jsp; }
         public String getJsp() { return jsp; }
    
         public void setJspContext(String jspContext) { this.jspContext = jspContext; }
         public String getJspContext() { return jspContext; }
    
         Control getControl() {
              return aControl;
         }
    
         public int doStartTag() throws JspException {
    
              pageContext.setAttribute("com_ibm_wps_control", name,
                   PageContext.REQUEST_SCOPE);
    
              aControl = new Control(name);
    
              pageContext.setAttribute("com_ibm_wps_control_" + name,
                   aControl, PageContext.REQUEST_SCOPE);
    
              try { pageContext.getOut().flush(); } 
                   catch (Exception e) { throw new JspException(); }
    
              return EVAL_BODY_TAG;
         }
    
         public int doEndTag() {
    
              ServletContext servletContext = pageContext.getServletContext();
              servletContext = servletContext.getContext(jspContext);
    
              RequestDispatcher dispatcher = 
                   servletContext.getRequestDispatcher("/" + jsp);
    
              if (dispatcher != null) {
                   try {
                        dispatcher.include(pageContext.getRequest(), 
                             pageContext.getResponse());
                   }
                   catch (Throwable t) {
              }}
    
              return EVAL_BODY_INCLUDE;
         }
    }

    The AddControl class extends the BodyTagSupport class because it will interact with the body contents of the tag, specifically to get any parameters that have been specified with the <addParam> tag. The class then defines getter and setter methods for each of the three attributes (i.e. name, jsp, jspContext). Also, notice there is a declaration of a Control class. This will be used to hold all the information about the control (e.g. image URLs) and be passed to the JSP for rendering.

    In the doStartTag method, a new Control instance is created and associated with attributes on the page context in the request scope. This simply allows the Control to be passed to the JSP as a request attribute. The EVAL_BODY_TAG constant is then returned to indicate the body contents of the tag should be evaluated.

    After the body has been evaluated, the doEndTag method prepares to pass control to the JSP specified in the attributes of the tag. This is done by switching the servlet context to where the resource is located, using the jspContext attribute. Then, a request dispatcher is created using the specified JSP attribute and includes the JSP to render the control.

  5. We now define the implementation of the <addParam> tag below:

    Listing 6. AddParam.java

    package com.ibm.wps.tags;
    
    import java.util.*;
    import javax.servlet.jsp.*;
    import javax.servlet.jsp.tagext.*;
    
    public class AddParam extends TagSupport {
    
         private String param = null;
    
         public String getParam() {
              return param;
         }
    
         public void setParam(String param) {
              this.param = param;
         }
    
         public int doStartTag() throws JspException {
    
              AddControl ancestorTag =
                   (AddControl)findAncestorWithClass(this, AddControl.class);
    
              Vector params = (Vector)ancestorTag.getControl().getParams();
              if (params != null) params.add(param);
    
              return EVAL_BODY_INCLUDE;
         }
    
         public int doEndTag() {
    
              return EVAL_PAGE;
         }
    }

    The AddParam class extends the TagSupport class and provides a getter and setter method for the param attribute. The doStartTag method only has one purpose, which is to record the parameter specified in the parent tag. It does this by asking the ancestor tag, addControl, for the instance of the Control object and then adding the parameter to it.

  6. We now define the Control object, used to contain all the relevant information of the control that will be passed to the JSP that does the control rendering.

    Listing 7. Control.java

    package com.ibm.wps.tags;
    
    import java.util.*;
    
    public class Control {
    
         private String name = null;
         private Hashtable attributes = new Hashtable();
    
         private Vector params = new Vector();
    
         public Control(String name) {
              this.name = name;
         }
    
         public String getName() {
              return name;
         }
    
         public Vector getParams() {
              return params;
         }
    
         public void setAttribute(String key, Object val) {
              attributes.put(key, val);
         }
    
         public Object getAttribute(String key) {
              return attributes.get(key);
         }
    }

    The Control class is constructed using a unique name and contains a vector, called params, to hold any parameters set using the <addParam> tag. In addition, we have defined an attributes hashtable to hold other resources to pass to the rendering JSP. (The benefit of this hashtable will become apparent in an upcoming section.)

    Notice how, up until this point, we have taken care to make all the implementation details very generic so that the tag code is reusable for any controls you might develop. By creating a new JSP to render the control and then passing this control and any relevant parameters, you can very easily develop your own control.

  7. Now, in Listing 8, we will define the JSP used to render the slideshow control.

    Listing 8. control.jsp

    <%@ page buffer="none" autoFlush="true" %>
    
    <%@ page import="java.util.*" %>
    <%@ page import="com.ibm.wps.tags.*" %>
    
    <jsp:useBean id="com_ibm_wps_control" class="String" scope="request"/>
    
    <% Control aControl =
         (Control)request.getAttribute("com_ibm_wps_control_" + com_ibm_wps_control); %>
    <% String aName = aControl.getName(); %>
    
    <script language="javascript"> 
    
         <% String main = ""; %>
         <%= aName %>_images = new Array;
    
         <% int i = 1; %> 
         <% for (Enumeration en = aControl.getParams().elements();
              en.hasMoreElements(); i++) { %> 
                   <% String url = (String)en.nextElement(); %>
                   <% if (i == 1) main = url; %>
                   <%= aName %>_images[<%= i %>] = new Image;
                   <%= aName %>_images[<%= i %>].src = "<%= url %>";
         <% } %>
    
         <%= aName %>_current = 1;
         <%= aName %>_max = <%= i - 1 %>
    
         function <%= aName %>_prev() {
              if (<%= aName %>_current > 1) {
                   <%= aName %>_current = <%= aName %>_current - 1;
              }
              document.getElementById("<%= aName %>_picture").src =
                   <%= aName %>_images[<%= aName %>_current].src;
         }
    
         function <%= aName %>_next() {
              if (<%= aName %>_current < <%= aName %>_max) {
                   <%= aName %>_current = <%= aName %>_current + 1;
              }
              document.getElementById("<%= aName %>_picture").src =
                   <%= aName %>_images[<%= aName %>_current].src;
         }
    
    </script>
    
    <table border="0" cellspacing="2" cellpadding="2">
         <tr>
              <td style="text-align:center"> 
                   <a href="javascript:<%= aName %>_prev()">Prev</a>
              </td>
              <td style="text-align:center"> 
                   <a href="javascript:<%= aName %>_next()">Next</a>
              </td>
         </tr>
         <tr>
              <td colspan="2">
                   <img id="<%= aName %>_picture" src="<%= main %>">
              </td>
         </tr>
    </table>

    The JSP first obtains the instance of the Control class then, using the getName method, gets the name of the control. It is important that we used this JSP variable to define any JavaScript functions, variables, etc., because this particular control might be only one of many slideshow controls on the page.

    The JSP then defines the JavaScript associated with the slideshow control, which simply defines the script necessary to replace the image when the Next and Previous buttons are selected. This function is done by iterating through all the URLs provided by calling the getParams method on the Control object. For efficiency, this JavaScript could be defined in a separate cachable script.js file and the functions modified to pass in the name of the editor instance. For simplicity, we have included everything in this one JSP.

    Finally, the JSP renders the control using a <table> tag in combination with <a> tags for the Next and Previous buttons, and a <img> tag for rendering the picture.

    Due to the use of the <%=aName%> scriptlet to effectively scope the control, the JSP is hard to interpret. Therefore, a sample of the rendered JSP contents is shown below using a control name of MyControl.

    Listing 9. Sample output of control.jsp

    <script language="javascript">
    
         MyControl_images = new Array;
         MyControl_images[1] = new Image; MyControl_images[1].src = "image1.gif€;
         MyControl_images[2] = new Image; MyControl_images[2].src = "image2.gif";
         MyControl_images[3] = new Image; MyControl_images[3].src = "image3.gif"; 
    
         MyControl_current = 1;
         MyControl_max = 3
    
         function MyControl_prev() {
              if (MyControl_current > 1) {
                   MyControl_current = MyControl_current - 1;
              }
              document.getElementById("MyControl_picture").src =
                   MyControl_images[MyControl_current].src;
         }
    
         function MyControl_next() {
              if (MyControl_current < MyControl_max) {
                   MyControl_current = MyControl_current + 1;
              }
              document.getElementById("MyControl_picture").src =
                   MyControl_images[MyControl_current].src;
         }
    
    </script>
    
    <table border="0" cellspacing="2" cellpadding="2">
         <tr>
              <td style="text-align:center"> 
                   <a href="javascript:MyControl_prev()">Prev</a>
              </td>
              <td style="text-align:center"> 
                   <a href="javascript:MyControl_next()">Next</a>
              </td>
         </tr>
         <tr>
              <td colspan="2">
                   <img id="MyControl_picture" src="image1.gif">
              </td>
         </tr>
    </table>

    In order for the slideshow control JSP tags to be available to all portlets running in the portal server, the tag library components need to be installed on the portal server application. All the class files can be put in a JAR file and placed under the <AppServer>\lib\app directory. The mytags.tld file can be placed in the <AppServer>\lib\app\WEB-INF\tld directory, and the control.jsp file can be placed in the root context of the portal server in the directory <PortalServer>\app\wps.ear\wps.war. The portal server must then be restarted before the changes take affect.

  8. Returning to the simple GenericPortlet we created earlier, we can tailor GenericPortletView.jsp to make use of the tag library with the following changes:

    Listing 10. GenericPortletView.jsp

    <%@ page language="java" contentType="text/html;charset=utf-8" %>
    
    <%@ taglib uri="/WEB-INF/tld/portlet.tld" prefix="portlet" %>
    <%@ taglib uri="/WEB-INF/tld/engine.tld" prefix="portal" %>
    <%@ taglib uri="/WEB-INF/tld/mytags.tld" prefix="mytags" %>
    
    <portlet:init/>
    <portal:constants/>
    
    <mytags:addControl
         name="MyControl"
         jsp="control.jsp"
         jspContext="<%= wpsBaseURL %>"
         >
    
         <mytags:addParam param="http://www.ibm.com/i/v11/m/en/lanim.gif"/>
         <mytags:addParam param="http://www.lotus.com/art/wtlotswlogo104.gif"/>
         <mytags:addParam param="http://www.google.com/images/logo.gif"/>
    
    </mytags:addControl>

    Notice that we use the engine.tld tag library provided by the portal server to obtain the root context URL of the portal server (i.e. the wpsBaseURL variable), where control.jsp resides. The portlet view now looks as follows:


    Figure 3. Portlet using the slideshow control
    Portlet using the slideshow control

    Notice that the slideshow control does not use the styling of the portal server. This brings us to our next topic.



Back to top


Styling and theme support

The portal server applies the concept of themes to ensure visual conformity across all portlets. This is implemented using a common set of CSS class attributes that portlets must provide in their content. As mentioned earlier, the control we have created can be used within the portal server, or it can be used in a standalone application. You might want to use this control on the current V4 portal server as well as on upcoming releases. Each of these environments will have their own style sheets and thus have their own class attributes defined. Thus, we need a solution that does not hard code the class attribute names directly into the control.jsp that renders the control.

We will accomplish this by providing a level of indirection; instead of directly inserting the CSS class attributes into the control.jsp markup, we will use a properties file to look up the value of more generic class attribute definitions and then insert the names specified in the properties file into the control.jsp. Thus, each environment can provide its own properties file that will do the mapping from the generic class attribute definitions to the specific ones expected by the application's style sheet.

A sample properties file for WebSphere Portal V4 is as follows:


Listing 11. com.ibm.wps.tags.PortletStyles.properties
link=wpsToolBarLink
background=wpsToolBar

Here, we define two abstract CSS definitions, link for the styling of the Next and Previous buttons, and background for the background color of the control. For the portal server environment, we map these attributes to the wpsToolBarLink and wpsToolBar attributes defined in the portal's style sheet, Styles.css.

To make the styling properties file dynamic:

  1. We will add a new attribute to the <addControl> tag as follows:

    Listing 12. mytags.tld -- AddControl

    €¦
         <attribute>
              <name>style</name>
              <required>yes</required>
              <rtexprvalue>true</rtexprvalue>
         </attribute>
    €¦

  2. We then add a getter and setter to the AddControl class, as shown in Listing 13.

    Listing 13. AddControl.java

    public class AddControl extends BodyTagSupport {
    
         €¦
         private String style = null;
         €¦
    
         public void setStyle(String style) { this.style = style; }
         public String getStyle() { return style; }
         €¦
    
         public int doStartTag() throws JspException {
    
              €¦ 
              aControl.setAttribute("styles", new StyleHelper(style));
              €¦
         }
    }

    The Control class does not need to change because we just use the generic notion of an attribute to attach the styling class to the Control so it can be passed to the JSP for rendering.

  3. To help in loading the styling attribute names, we have created a new class called StyleHelper, which is defined as follows:

    Listing 14. StyleHelper.java

    package com.ibm.wps.tags;
    
    import java.util.*;
    
    public class StyleHelper {
    
         private ResourceBundle map = null;
    
         public StyleHelper(String prop) {
              super();
    
              try {
                   map = ResourceBundle.getBundle(prop);
              }
              catch (Exception e) {
                   map = null;
              }
         }
    
         public String getClass(String name) {
    
              StringBuffer sb = new StringBuffer();
              sb.append("class=\"").append(getString(name)).append("\"");
    
              return sb.toString();
         }
    
         private String getString(String resource) {
    
              String className = null;
              try { 
                   if (map != null) {
                        className = map.getString(resource);
              }}
              catch (MissingResourceException e) {
                   className = null; 
              }
              return className == null ? "" : className;
         }
    }

    This new class simply loads the specified properties file, and then, using the getClass method, returns the class string to insert in the JSP markup. For example, using the properties file specified earlier, calling the method passing link as the parameter returns: class='wpsToolBarLink'.

  4. The control.jsp is now modified to use the StyleHelper class as follows:

    Listing 15. control.jsp

    €¦
    <% StyleHelper styles = (StyleHelper)aControl.getAttribute("styles"); %>
    €¦
    
    <table <%= styles.getClass("background") %> border="0" cellspacing="2" cellpadding="2">
         <tr>
              <td style="text-align:center"> 
                   <a <%= styles.getClass("link") %> 
                        href="javascript:<%= aName %>_prev()">Prev</a>
              </td>
              <td style="text-align:center"> 
                   <a <%= styles.getClass("link") %> 
                        href="javascript:<%= aName %>_next()">Next</a>
              </td>
         </tr>
         <tr>
              <td colspan="2">
                   <img id="<%= aName %>_picture" src="<%= main %>">
              </td>
         </tr>
    </table>

  5. Finally, we update the portlet JSP to use the new tag attribute.

    Listing 16. GenericPortletView.jsp

    €¦
    <mytags:addControl
         name="MyControl"
         jsp="control.jsp"
         jspContext="<%= wpsBaseURL %>"
         style="com.ibm.wps.tags.PortletStyles"
         >
    €¦
    

    The slideshow control now inherits the theme from the portal server.


    Figure 4. Portlet using the slideshow control
    Portlet using the slideshow control


Back to top


Conclusion

This article touched on the type of rich controls you could create using a custom tag library that you made available to all portlet developers. We have walked through an end-to-end example, and worked with a template that you can customize to create your own controls. You will find that additional features, such as natural language support, can be added in a manner similar to how styling support was added to the example. Hopefully, these basic concepts will help you begin creating common controls that can be easily -- and successfully -- incorporated into your own development projects.



Resources



About the author

Michael Wanderski is an advisory software engineer at the IBM Research Triangle Park Lab in the Pervasive Computing Division, currently working on WebSphere Portal Server and related products. You can reach Mike at mwanders@us.ibm.com.




Rate this page


Please take a moment to complete this form to help us better serve you.



 


 


Not
useful
Extremely
useful
 


Share this....

digg Digg this story del.icio.us del.icio.us Slashdot Slashdot it!



Back to top