Developing and deploying a Struts application as a WebSphere Portal V5 Portlet

This article describes and illustrates how to use the Jakarta Struts Framework, which is incorporated into the IBM Struts Portlet Framework, to develop and deploy an application as a portlet that runs under IBM WebSphere Portal V5.

Share:

Tim Hanis (hanistt@us.ibm.com), WebSphere Portal Development, IBM

Photo of Tim HanisTim Hanis is a senior software engineer at IBM Research Triangle Park lab in NC. He has extensive experience with WebSphere Portal having worked in both development and in the consulting services organization.


developerWorks Contributing author
        level

Jim Bonanno (bonanno@us.ibm.com), WebSphere Portal Development, IBM

Photo of Jim BonannoJim Bonanno is a Senior Programmer at IBM currently working for the On Demand Software Development team. Jim was previously responsible for integrating the Jakarta Struts framework into the WebSphere Portal product.



Lisa Tomita (tomato@us.ibm.com), Software Services for WebSphere, IBM

Lisa Tomita is a senior consultant for IBM Software Services for WebSphere at the IBM Research Triangle Park Lab in Raleigh, North Carolina.



06 January 2004

Introduction

This article discusses the implementation of a portlet written for IBM ® WebSphere® Portal Version 5 (hereafter called WebSphere Portal) using the Struts Portlet Framework. Struts is an Apache Jakarta project providing a very popular open source framework for building Web applications. WebSphere Portal V5.0 provides the Struts Portlet Framework that supports the deployment of Struts applications as portlets. The Struts Portlet Framework also supports portal modes and devices within a Struts application.

If you read our previous articles which described the implementation of portlets using the state pattern, you will recognize the example application used in this article. It follows the same design considerations, and is developed and deployed, here, using the Struts framework. This application maintains a list of contacts (address book entries) specific to a user logged-in to the portal. The contact list is stored in a database or in memory. The user can view the contacts in a list and, for more detail, individually. The edit mode of the portlet lets a user add, delete, and modify contacts.

The focus of this paper is not on developing a Struts Web application; instead, it is on the implementation and configuration required to deploy the Struts application as a portlet. You see how to create a portlet using the Jakarta Struts Framework, which is incorporated into the IBM Struts Portlet Framework. In WebSphere Portal V5 the Struts Portlet Framework ships Jakarta Struts 1.1.0.

The following products are used either in the development or deployment of the example application:

  • IBM WebSphere Portal Server Version 5.0
  • IBM WebSphere Application Server Version 5
  • A database to persist the contact entries (optional)
  • IBM WebSphere Studio Version 5 (optional)

About the example portlet application

The example application implements standard Create/Read/Update/Delete (CRUD) operations on a set of persisted data. It maintains a list of contacts (for example, a simple address book) for a given user. The application lets the user view the contact entries individually or in a list.

From a visual perspective, our implementation of the contacts list application has the following views (pages):

  • Login view - Displays the user login view
  • Main view - Displays the list of contacts with an option to select a contact for more information
  • Detail view - Displays the selected contact's detail information
  • Main edit view - Displays the list of contacts with options to add, delete, or modify
  • Add contact view - Displays a form view to enter information for a new contact
  • Modify contact view - Displays a form view showing the existing contact element data and allowing updates

The application does not provide an explicit page to delete an entry. Instead, to delete an item, the user selects a contact entry from the main edit page, entry deletion processing occurs, and the main edit page is refreshed. If an error occurs, an appropriate message will be displayed. There is no confirmation page or successful execution page.

Figure 1. Main view
Main view

Designing the portlet application

The previous article series on implementing a portlet using the state pattern and the following discussion are similar because the Struts implementation and the State pattern are fundamentally very similar.

Consider the actions and views that make up this application. Think of an action as some user-level function or behavior that you want to implement for the portlet. For example, you might want an action to add a contact, or to show a particular view. A view is the representation of the application after the user action has been completed. For example, the action of adding a new contact could result in the view of the portlet in edit mode, displaying the main list of contacts.

Next, consider how you want the application to look visually. In this example, you want a main view page that lists the contacts you have created. The page should let users select one of the contacts and get additional detail about this person.

You want the contact list to be unique to each user; therefore, this portlet needs to be on an authenticated page so that a User object is associated with the logged-in user.

You want to enable the user to add, delete, and modify contacts from the list. You can provide a main edit page that lists the contacts and also supports deleting and modifiying selections. You don't need to implement another view for the user to delete a selection; you can simply enable the user to delete a selection, than refresh the main edit page with the revised contact list.

If the user selects a contact to modify, you can display a detail page where the user updates any of the attributes for this contact entry. After the user has completed the modifications, you can re-display the main edit page.

Similarly, from the main edit page, you can let the user add a new contact. You can display another detail page where the user can enter and save the attributes for the contact entry. Again, after the user saves, you redisplay the main edit page with the updated list.

Now, you can determine the state transitions available for this application. The transitions are managed by applying the appropriate actions which cause the application to generate a specific view. In the following diagram the ovals represent views (JSPs) and the rectangles represent actions.

Figure 2. State transition Diagram
State transition Diagram

Implementing the portlet using Struts

This design requires you to implement seven action classes. In the implementation provided with this article, these are defined as:

  • MainViewAction
  • DetailViewAction
  • MainEditAction
  • AddContactAction
  • DeleteContactAction
  • ModifyAction
  • ModifyContactAction

From the state diagram, you see that the Add contact view is really just a direct forward from the Main view to the Add contact JSP. Because there is no business logic needed prior to rendering the Add contact view, you can simply invoke the Add contact JSP directly, and configure it as a global forward instead of an action.

Setting up the development environment

To set up the development environment, you create a Struts based portlet project in WebSphere Studio, as described below.

  1. Import the Struts Portlet Framework into a new Web project.
    • In WebSphere Studio, select Import.
    • Click Browse, and find the PortalStrutsBlank.war.
      In WebSphere Portal V5.0 you will find the PortalStrutsBlank.war file in the <portal_root>\dev\struts\StrutsPortlet\ directory. Importing this war file adds the Struts jar files for both Jakarta Struts and IBM's Struts Portlet Framework to the project lib directory.
    • Click New and give the Web project a name.
      Tip: Do not select J2EE level 1.3. The PortalStrutsBlank.war file is at level 1.2 so it has to import into a 1.2 project. After this import, you will convert the project to 1.3.
    • Click Finish.
    Figure 3. Importing the Struts Portlet Framework
    Importing the Struts Portlet Framework
  2. Add the portal jar files to the project build classpath.
    • Right-click on the newly created Web project in the Navigator pane, and select Properties from the pop-up menu.
    • Select Java Build Path and then the Libraries tab.
    • Select Add Variable, then select WPS_V5_PLUGINDIR in the New Variable Classpath Entry and click Extend.
    • Select portlet-api.jar and select Ok.
    • Select Add Variable, then select WPS_V5_PLUGINDIR in the New Variable Classpath Entry and click Extend.
    • Select wps.jar and select Ok.
    • Select Add Variable, then select WPS_V5_PLUGINDIR in the New Variable Classpath Entry and click Extend.
    • Select wpsportlets.jar and select Ok.
    • Click Ok to save the changes.
  3. Change the newly created project to a J2EE 1.3..
    • Right-click on the project in the Navigator pane and selectProperties from the pop-up menu.
    • Select Weband then change the J2EE Level to 1.3
    • Click Ok to save the change.
  4. Add portlet.tld to the the tag lib folder in the new project.
    • Right-click on the tld folder in the project.
    • Select Import and then File System.
    • On the File system diaglog browse to find the <wp-root>\shared\app\WEB-INF\tld directory and select Ok.
    • Highlight (do not click the checkbox) of tld; then, in the right pane click the checkbox for portlet.tld and click Finish.
  5. If you see the warning Web context root should start with slash(/) character for portlet application debugging, then right-click on the newly created project in the Navigator pane, and select Properties from the pop-up menu. Select Web and then add / to the Context Root.

If you do not use Studio make sure the following jar files, as well as the Struts jar files that you imported, are available in the build path. You can find the files in the following WebSphere Application Server and WebSphere Portal directories:

  • <was-root>/lib/ivjejb35.jar
  • <was-root>/lib/j2ee.jar
  • <was-root>/lib/runtime.jar
  • <was-root>/lib/servletevent.jar
  • <was-root>/java/jre/lib/rt.jar
  • <wp-root>/shared/app/portlet-api.jar
  • <wp-root>/shared/app/wps.jar
  • <wp-root>/shared/app/wpsportlets.jar

Before considering the implementation details of the action classes, beans, forms, or the supporting classes, determine the implementation of the application configuration files. These include the:

  • Web application deployment descriptor, web.xml
  • Struts form validation xml file, validation.xml
  • Portlet deployment descriptor, portlet.xml
  • Struts configuration file, struts-config.xml

You need to update each of these files.

Web Deployment Descriptor - web.xml

A default web.xml file is provided as part of the Portal Struts framework. You need to make application specific changes to the deployment descriptor. First, change the <display-name> to a meaningful name for this application, such as Contacts List. The <servlet id="Unique_Servlet_Name"> id must be unique in your deployed portal environment. Change this value to a unique string.

Struts 1.1 built into the framework support for dividing an application into modules. When using modules, an application's configuration information and directory structure are separated by module. Therefore, instead of a single struts-config.xml file for the application, you have multiple control files with separate directory structure, identified by module-relative portions of the application URI. The Struts Portlet Framework takes advantage of the module support to provide both mode and device (markup) differentiation for the Struts configuration. The search path is used to determine the module and struts-config.xml file to use. It also determines the base directory for locating JSPs. Adding the following init param sets the search path to take markup name and mode into consideration when searching for modules.

<init-param>
  <param-name>ModuleSearchPath</param-name>
  <param-value>markupName, mode</param-value>
</init-param>

You create two modules for this application, one for edit mode and one for help mode. The view mode is configured through the base struts configuration. The two modules are configured as init parameters in this form: /config/<device>/<mode>. Therefore, you have: config/html/edit and config/html/help. Add the param value associated with these module definitions to indicate where to start to search for the Struts configuration file.

    <init-param>
      <param-name>config/html/help</param-name>
      <param-value>/WEB-INF/html/help/struts-config.xml</param-value>
    </init-param>
    <init-param>
      <param-name>config/html/edit</param-name>
      <param-value>/WEB-INF/html/edit/struts-config.xml</param-value>
    </init-param>

Add two application specific init parameters, persist_to_db and datasource, to support the persistence classes that are part of the download. Setting persist_to_db to false causes the use of an in-memory persistence broker. Setting this value to true causes a database broker to be employed. If you use a database broker, you also need to set the datasource. Set the datasource value to the name of this datasource. The datasource value is ignored if you are using in-memory persistence.

Finally, change the welcome file list. The Struts Portlet Framework lets you specify the initial view for the device/mode. This is specified through the welcome file list.

  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>html/help/index.jsp</welcome-file>
    <welcome-file>html/edit/index.jsp</welcome-file>
  </welcome-file-list>

Portlet Deployment Descriptor - portlet.xml

You must also make changes to the default portlet.xml file, which is provided with the blank Struts portlet.

  1. Change the <portlet-app-name> and the <portlet-name> to meaningful names.
  2. Change the <portlet-app uid> to a unique identifier.
  3. Change the href to the relative servlet id that was specified as the web.xml <servlet id> value. For example, if you specified ContactsListPortlet for the servlet id in the web.xml, then you would specify WEB-INF/web.xml#ContactsListPortlet for the portlet href in portlet.xml.
  4. Specify edit and help modes support for html devices as well as view mode.
  5. For the concrete portlet app, specify a unique uid and meaningful names for both the portlet-app-name and the portlet-name.
  6. Change the concrete-portlet href value to match the portlet app <portlet id> value.
  7. Finally, specify meaningful values for the language specific attributes for title, title-short, description and keywords.

Listing 1. portlet.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE portlet-app-def PUBLIC
     "-//IBM//DTD Portlet Application 1.1//EN" "portlet_1.1.dtd">
<portlet-app-def>
  <portlet-app uid="contacts_list_portlet.ContactsListPortlet.b067c2c0"
             major-version="1" minor-version="0">
    <portlet-app-name>Contacts List application</portlet-app-name>
    <portlet id="contacts_list_portlet.ContactsListPortlet"
              href="WEB-INF/web.xml#contacts_list_portlet.ContactsListPortlet"
              major-version="1" minor-version="0">
      <portlet-name>Contacts List portlet</portlet-name>
      <cache>
        <expires>0</expires>
        <shared>NO</shared>
      </cache>
      <allows>
        <maximized/>
        <minimized/>
      </allows>
      <supports>
        <markup name="html">
          <view/>
          <edit/>
          <help/>
        </markup>
      </supports>
    </portlet>
    </portlet-app>
    <concrete-portlet-app uid="contacts_list_portlet.ContactsListPortlet.b067c2c0">
      <portlet-app-name>Contacts List application</portlet-app-name>
      <concrete-portlet href="#contacts_list_portlet.ContactsListPortlet">
        <portlet-name>Contacts List portlet</portlet-name>
        <default-locale>en</default-locale>
        <language locale="en">
          <title>Contacts List portlet</title>
          <title-short>Contacts List</title-short>
          <description>Contacts List portlet</description>
          <keywords>WPS, Struts</keywords>
        </language>
        <config-param>
          <param-name>FilterChain</param-name>
          <param-value>StrutsTranscoding</param-value>
        </config-param>
      </concrete-portlet>
    </concrete-portlet-app>
</portlet-app-def>

Struts configuration files - struts-config.xml

The struts-config.xml file contains the application definitions that the Struts framework uses for control, navigation, and configuration settings, including the form beans that are needed.

As discussed above in the changes to the web.xml file, the param value associated with each of the two application module definitions indicates where to start to search for the module specific Struts configuration files. Therefore, you need to create new struts-config.xml files that will reside in /WEB-INF/html/edit/ and /WEB-INF/html/help/. These files will contain the Struts configuration information specific to its module.

First, change the struts-config.xml file in the /WEB-INF directory to the struts configuration for view mode. You need to create action elements in the xml to support the actions identified in the transition diagram. There are seven actions specified. They can be defined as the following, and will be shown using the class names defined in the example implementation. You also specify appropriate forward settings for each action indicating the JSP used to render the requested view. Again, these rendering states were specified in the transition diagram. You include the appropriate actions and forwards in the module specific configuration files.

Next, create the new struts-config.xml files in the directories specified above.

  <form-beans>
    <form-bean name="contactForm"
        type="com.ibm.sample.contacts.struts.beans.Contact"/>
  </form-beans>

You also specify global forwards, the message resource file, and the validation xml file reference. The global forwards associate a logical name to a URI for references to either JSPs or Actions.

Listing 2. VIEW - /WEB-INF/struts-config.xml

<?xml version="1.0" encoding="ISO-8859-1" ?>

<!DOCTYPE struts-config PUBLIC
          "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN"
          "http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd">

<struts-config>

  <form-beans>
    <form-bean name="contactForm"
        type="com.ibm.sample.contacts.struts.beans.Contact"/>
  </form-beans>

  <global-forwards>
   <forward   name="mainview" path="/mainview.do" />
   <forward   name="notauthenticated" path="/not_authenticated.jsp"/>
   <forward   name="exception" path="/exception.jsp"/>
  </global-forwards>

  <action-mappings>
    <action
      path="/mainview"
    type="com.ibm.sample.contacts.struts.action.MainViewAction"
    name="contactForm"
    scope="request"
    validate="false">
        <forward
          name="mainviewpage"
          path="/main_view.jsp"/>
    </action>

    <action
      path="/detailview"
    type="com.ibm.sample.contacts.struts.action.DetailViewAction"
    name="contactForm"
    scope="request"
    validate="false" >
    <forward
      name="detailviewpage"
      path="/detail_view.jsp"/>
    </action>
  </action-mappings>

  <controller
      processorClass="com.ibm.wps.portlets.struts.WpsRequestProcessor">
  </controller>

  <message-resources
      parameter="com.ibm.sample.contacts.struts.nls.ApplicationResources"/>

  <plug-in className="org.apache.struts.validator.ValidatorPlugIn">
    <set-property property="pathnames"
                     value="/WEB-INF/validator-rules.xml,
                            /WEB-INF/validation.xml"/>
  </plug-in>

</struts-config>

Listing 3. EDIT - /WEB-INF/html/edit/struts-config.xml

<?xml version="1.0" encoding="ISO-8859-1" ?>

<!DOCTYPE struts-config PUBLIC
          "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN"
          "http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd">

<struts-config>

  <form-beans>
    <form-bean name="contactForm"
        type="com.ibm.sample.contacts.struts.beans.Contact"/>
  </form-beans>

  <global-forwards>
   <forward   name="addcontactpage" path="/add_contact.jsp"/>
   <forward   name="mainedit" path="/mainedit.do" />
  </global-forwards>

  <action-mappings>

    <action
      path="/mainedit"
    type="com.ibm.sample.contacts.struts.action.MainEditAction"
    name="contactForm"
    scope="request"
    validate="false" >
    <forward
      name="maineditpage"
      path="/main_edit.jsp"/>
    </action>

    <action
      path="/modify"
    type="com.ibm.sample.contacts.struts.action.ModifyAction"
    name="contactForm"
    scope="request"
    validate="false" >
    <forward
      name="editcontactpage"
      path="/edit_contact.jsp"/>
    <forward
      name="deletecontact"
      path="/deletecontact.do"/>
    </action>

    <action
      path="/modifycontact"
    type="com.ibm.sample.contacts.struts.action.ModifyContactAction"
    name="contactForm"
    scope="request"
    validate="false" >
    <forward
      name="mainedit"
      path="/mainedit.do"/>
    </action>

    <action
      path="/deletecontact"
    type="com.ibm.sample.contacts.struts.action.DeleteContactAction"
    name="contactForm"
    scope="request"
    validate="false" >
    <forward
      name="maineditpage"
      path="/main_edit.jsp"/>
    </action>

    <action
      path="/addcontact"
    type="com.ibm.sample.contacts.struts.action.AddContactAction"
    name="contactForm"
    scope="request"
    validate="false" >
    <forward
      name="addcontactpage"
      path="/add_contact.jsp"/>
    </action>

  </action-mappings>

  <controller
      processorClass="com.ibm.wps.portlets.struts.WpsRequestProcessor">
  </controller>

  <message-resources
     parameter="com.ibm.sample.contacts.struts.nls.ApplicationResources"/>
</struts-config>

Listing 4. HELP - /WEB-INF/html/help/struts-config.xml

<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE struts-config PUBLIC
          "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN"
          "http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd">
<struts-config>
  <action-mappings>
  </action-mappings>
  <controller
      processorClass="com.ibm.wps.portlets.struts.WpsRequestProcessor">
  </controller>
  <message-resources
      parameter="com.ibm.sample.contacts.struts.nls.ApplicationResources"/>
</struts-config>

Struts validation - validation.xml

You do not use Struts input validation for this application. You can delete the default validation specification in validation.xml.

<form-validation>
</form-validation>

Directory structure changes

Recall that you changed the Welcome File List in the web.xml file. This list is used to indicate the starting point for the defined modules. Based from the root context of the web application you need to create directories for edit and help, and include in those directories the index.jsp for each of those modes.

The struts-config.xml files for each module (representing a portal mode) show no additional path information for the JSP files; therefore, in each of those same directories you need to add the appropriate JSPs for that module. In the \html\edit directory you have add_contact.jsp, edit_contact.jsp and main_edit.jsp, as well as the index.jsp file. In the root directory you have detail_view.jsp and main_view.jsp. In the \html\help directory we only have the index.jsp file.

Action class implementations

Next, you implement the Action classes needed for this application. The details of each class are not discussed here because the complete implementation of these classes is available as a download. You need to implement classes that extend org.apache.struts.action.Actionfor each of the actions identified in the state transition diagram and which are defined in the struts-config.xml files.

Look at the code for the DeleteContactAction class, which implements the function to delete a contact. You also see an abstract class which our Action classes extend so you can put common code needed for all the Action class implementations. For example, you add code there to verify that the user is logged in before executing this function.

Listing 5. DeleteContactAction Class

public class DeleteContactAction extends PostLoginAbstractAction {
  public ActionForward performAction(ActionMapping mapping, ActionForm form,
    User user, HttpServletRequest request, HttpServletResponse response)
    throws Exception {

    //  Get the userid and selected oid
    String userid = user.getUserID();
    Contact contact = (Contact) form;
    String oid = contact.getSelectedContact();

    //  Delete the contact
    ContactHelper contactHelper = ContactHelper.getInstance();
    contactHelper.deleteContact(broker, userid, oid);

    //  Render the main edit page
    return (mapping.findForward(MAIN_EDIT));
  }
}

The abstract Action class extends org.apache.struts.action.Action and implements the execute method. This implementation ensures that the user is logged in and then invokes the performAction method of the called Action class, passing the same parameters passed to the execute method plus the user bean that was retrieved during login validation. When you move the application to the portal environment, you will simply remove the login action and change this code to retrieve the User object from the portletRequest object. You can then change any of the application code that accesses the User object to use the wps User object API.

Listing 6. PostLoginAbstractAction Class

public abstract class AbstractAction extends Action implements Constants {

  public ActionForward execute(ActionMapping mapping, ActionForm form,
    HttpServletRequest request, HttpServletResponse response)
    throws Exception {

    try {
      // Initializations
      log = LogFactory.getLog("com.ibm.sample.contacts.struts.action");
      bundle = ResourceBundle.getBundle(RESOURCE, request.getLocale());

      // Initialize the persistence broker
      broker = ContactListBroker.getInstance();
      if (broker == null) {
        String persistToDB = getServlet().getInitParameter(DB);
        String datasource = getServlet().getInitParameter(DATASOURCE);
        if (TRUE.equalsIgnoreCase(persistToDB))
          ContactListDBBroker.initialize(datasource);
       else
          ContactListMemBroker.initialize();
        broker = ContactListBroker.getInstance();
       if (log.isDebugEnabled())
          log.debug("Broker initialized: "+broker);
      }

      // Check for existing session with logged-in user
      HttpSession session = request.getSession();
      PortletRequest portletRequest = (PortletRequest) request;
      User user = portletRequest.getUser();
      if (session==null || user==null)
        throw new AIMMessageException(bundle.getString(UNAUTHENTICATED));

      // Invoke the action class performAction method
      return performAction(mapping, form, user, request, response);

    } catch (Exception e) {
      HttpSession session = request.getSession();
      session.setAttribute(EXCEPTION,e);
      return (mapping.findForward("exception"));
    }
  }

  public abstract ActionForward performAction(ActionMapping mapping,
     ActionForm form, User user, HttpServletRequest request,
     HttpServletResponse response)  throws Exception;

}

The remaining Action class implementations are very similar and, given that this application has minimal logic processing, they are not complex implementations at all.

As shown in the DeleteContactAction class some of the action classes use the services of the persistence classes. The persistence function is provided as part of the download implementation. For now, look at the ContactListBroker interface, which defines the function needed for Contact list processing. The Broker class implementations manage the data access to the data stores--in this case, either a database or an in-memory representation of the data. This function is needed for the portlet, but is tangential to the point of this article, You can see the implementation of the specific brokers in the download.

Listing 7. ContactListBroker Interface

public interface ContactListBroker {

  /**
   * Get the Contact information given a contact number
   * @param owner java.lang.String
   * @param contactOID java.lang.String
   * @return ContactForm
   */
  public ContactForm getContact(String owner, String contactOID)
    throws AIMException;

  /**
   * Create a Contact
   * @param ContactForm
   */
  public void saveContact(Contact contact)
    throws AIMException;

  /**
   * Delete a Contact
   * @param owner java.lang.String
   * @param contactOID java.lang.String
   */
  public void deleteContact(String owner, String oid)
    throws AIMException;

  /**
   * Get the contact list.
   * @param owner java.lang.String
   * @return List of ContactForm objects
   */
  public List getContactList(String owner)
    throws AIMException;

}

Application beans

We have two beans defined for this application. We have a contact form bean as defined in our struts-config.xml file. It extends org.apache.struts.validator.ValidatorForm and contain properties (with accessors) that map to the columns in the contacts database table. See the section on setting up the database for our example table definition. The other bean is a helper class that provides common function to add a contact and to delete a contact. Again this code is available in the download and is not shown here.

Application resource properties

The message properties file is defined in the struts-config.xml file. Ours is named ApplicationResources.properties and is located in a resources directory. This resource bundle provides displayable strings that are encapsulated in bundles so that the application can be more easily translated.

Java Server Page (JSP) files

The remaining components of the application are JavaTM Server Page (JSP) files. Based on the state transition diagram, and as you defined in the struts-config.xml file, you need to implement five JSPs to render a page for:

  1. Adding a contact
  2. Editing a contact
  3. Main view page
  4. Main edit page
  5. Detail contact view page

You also need to update the index.jsp files to invoke the initial action for each mode.

Look at some of the key portions of the JSP files, the one for rendering the main view and then an example of the rendered content for this JSP. A session-scoped bean that contains the list of contacts is required for this JSP. You define several standard Struts tag libraries to use within the JSP. When the user selects a contact in the rendered list, you want processing to continue with the detailview action, passing the selected contact oid as a parameter with the request form data.

Listing 8. Main View JSP

<%@ taglib uri="/tags/struts-bean" prefix="bean" %>
<%@ taglib uri="/tags/struts-html" prefix="html" %>
<%@ taglib uri="/tags/struts-logic" prefix="logic" %>
<%@ taglib uri="/tags/struts-portal-html" prefix="portalhtml" %>
<%@ taglib uri="/WEB-INF/tld/portlet.tld" prefix="portletAPI" %>

<jsp:useBean id="contactList" class="java.util.List" scope="session" />

<html:html locale="true">
<table border="0" width="95%" align="center" cellspacing="0" cellpadding="0" >

  <thead>
  <tr>
    <td class="wpsTableHead"><bean:message key="contact.name"/></td>
    <td class="wpsTableHead"><bean:message key="contact.company"/></td>
    <td class="wpsTableHead"><bean:message key="contact.email"/></td>
    <td class="wpsTableHead"><bean:message key="contact.mobile"/></td>
  </tr>
  </thead>

  <html:form method="post" action="/detailview" >
  <html:hidden property="selectedContact"/>

    <logic:iterate
        name="contactList" id="aContact"
        type="com.ibm.sample.contacts.struts.beans.Contact">
  <tr>
    <td class="wpsPortletSmText" valign="top" align="left">
        <a   style="text-decoration: none; color:black;"
          onMouseOver="this.style.color='red'"
          onMouseOut="this.style.color='#000000'"
          href="javascript:document.<portletAPI:encodeNamespace
              value='ContactForm'/>.selectedContact.value=
              '<bean:write name="aContact" property="oid"/>';
              document.<portletAPI:encodeNamespace value=
              'ContactForm'/>.submit()">
        <bean:write name="aContact" property="firstName"/>
        <bean:write name="aContact" property="lastName"/>
        </a>
    </td>
    <td class="wpsPortletSmText" valign="top" align="left">
      <bean:write name="aContact" property="company"/>
    </td>
    <td class="wpsPortletSmText" valign="top" align="left">
      <bean:write name="aContact" property="email"/>
    </td>
    <td class="wpsPortletSmText" valign="top" align="left">
      <bean:write name="aContact" property="mobilePhone"/>
    </td>
  </tr>

  </logic:iterate>

  </html:form>
</table>
</html:html>
Figure 4. Main List View
Main List View

Setting up the database

You can create this application with a couple of options for data persistence: one with a database broker implementation, and the other with an in-memory implementation. For quick development and testing purposes, you may want to just work with the in-memory version because it eliminates the need for the following database set-up steps. Of course, the data is lost whenever the application server is restarted. The code for both of these broker types is available from the download (along with the rest of this sample application), and the broker type is selected through a configuration parameter set in the Web deployment descriptor file web.xml.

However, if you choose to persist the application data to a database, then you first need to create the database table to hold the contact list data. To create the table, run the following SQL command from a command line, substituting the schema name with your choice, but keep the table name as indicated. You can either create a new database (dataspace) for this table or add it to an existing one. If using DB2&reg;, in the Command Center, establish a connection to the appropriate database, copy the following command, and then run it.

CREATE TABLE "DB2ADMIN"."CONTACTS" ("OWNER" VARCHAR(64) NOT NULL,
"OID" VARCHAR(64) NOT NULL PRIMARY KEY, "FIRST_NAME" VARCHAR(32) NOT NULL,
"LAST_NAME" VARCHAR(32) NOT NULL , "EMAIL" VARCHAR(128) NOT NULL,
"TITLE" VARCHAR(64), "COMPANY" VARCHAR(64), "BUS_PHONE" VARCHAR(32),
"MOBILE_PHONE" VARCHAR(32), "FAX_PHONE" VARCHAR(32), "WEB" VARCHAR(128),
"ADDRESS1" VARCHAR(32), "ADDRESS2" VARCHAR(32), "CITY" VARCHAR(32),
"STATE" VARCHAR(2), "ZIP" VARCHAR(10), "COUNTRY" VARCHAR(32) )

Now, you can add an entry into the contacts list table so that you can test your portlet more easily as you are developing it. You can execute the following SQL command to create a new entry. If you want to log in to the portal using a userID other than wpsadmin, for testing purposes, then substitute this userID for wpsadmin in the SQL statement below).

insert into contacts (owner, oid, first_name, last_name, email, company,
mobile_phone) values ('wpsadmin', '01', 'Tim', 'Hanis', 'hanistt@us.ibm.com',
'IBM', '919-254-9072')

Next, create a data source in WebSphere Application Server for the database in which you created the contacts table. This portlet is packaged as a J2EE 1.3 application so you need to specify an Application Server V5 data source. J2EE Level 1.3 includes a Servlet Specification level of 2.3 and a JSP Specification level of 1.2.

  1. In Application Server use the DB2 Legacy CLI-based Type 2 JDBC Driver JDBC driver and specify a Data Source, instead of the Data Source (Version 4).
  2. In the Application Server Administrative Console, select Resources =>JDBC Providers.
  3. If you already have the DB2 Legacy CLI-based Type 2 JDBC Driver installed, then select it. Otherwise, add it by selecting New and completing the next page.
  4. On the DB2 Legacy CLI-based Type 2 JDBC Driver page, ensure that the classpath is set to the correct location of the db2java.zip file.
  5. Select Data Sources, and then select New to create a new V5 data source. Specify a data source name. For the JNDI name, prefix the name with a jdbc/ sub context.
    For example, if you want a data source jndi name of aim, enter jdbc/aim. The portlet will prepend the jdbc/ sub context when attempting to a JNDI name lookup.
  6. If your database requires a userid and password for authentication, specify an authentication alias in Component-managed Authentication Alias.
  7. If you do not already have an alias defined, you can create one in Security => JAAS Configuration => J2C Authentication Data.
  8. Be sure to save the configuration changes and test the data source connection.
Figure 5. WebSphere Application Server Administrative Console
WebSphere Application Server Administrative Console

Finally, create a datasource in Application Developer for this same database. This is needed if you intend to use Application Developer to test and debug this Struts application. From the Application Developer Server perspective edit the WebSphere Portal Server V5 Test Environment definition, select the datasources tab on the edit pane, add a DB2 Legacy CLI-based Type 2 JDBC Driver driver and then add a V5 datasource. Be sure to use jdbc/ as the subcontext prefix for your JNDI name. As in Application Server you may need to add a security authentication alias if you do not already have one created.

Figure 6. Defining the data source
Defining the data source

Implementation class summary

The following tables list the Java classes implemented for the example application implementation available to download and a brief description of each class.

Table 1. ContactsListPortlet classes
Package Class Description
com.ibm.sample.contacts.struts.beansContactHelperHelper class to invoke persistence management functions for contacts
ContactStruts form object that represents a Contact
com.ibm.sample.contacts.struts.actionAddContactActionHandles requests to add a contact
DeleteContactActionHandles requests to delete a contact
DetailViewActionHandles requests to show the detail contact view
ModifyActionHandles requests to modify a contact
ModifyContactActionHandles requests to save the edited contact changes
MainEditActionHandles requests to show the main edit view
MainViewActionHandles requests to display the main contact list view
AbstractActionAbstract class for actions
com.ibm.sample.contacts.struts.persistenceContactListBrokerPersistence class interface
AbstractDBBrokerAbstract class for common function for a database broker
ContactListDBBrokerA database broker implementation extends DbAbstractBroker and implements ContactListBroker
ContactListMemBrokerMemory broker implementation implements ContactListBroker
com.ibm.sample.contacts.struts.utilitiesAIMExceptionBase exception class
AIMWrapperExceptionAn AIMException that wraps another (`underlying') exception
AIMMessageExceptionException to terminate processing and issue a user message
ConstantsInterface that defines application constants
com.ibm.sample.contacts.struts.nlsApplicationResources.propertiesDefault resource bundle contains printable strings
ApplicationResources_en.propertiesLocale specific resource bundle that contains printable strings

Conclusion

This article discussed the implementation of an example application using the Struts Portlet Framework. You saw how to use the Struts modules and how to use them in conjunction with the WebSphere Struts Portlet Framework to support multiple portal modes. The complete implementation for this sample is provided in the download below.


Download

DescriptionNameSize
Code samplecontactslistportlet_struts.zip  ( HTTP | FTP )1311KB

Resources

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into WebSphere on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=WebSphere
ArticleID=13862
ArticleTitle=Developing and deploying a Struts application as a WebSphere Portal V5 Portlet
publish-date=01062004