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
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
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.
- 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 thePortalStrutsBlank.warfile 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. ThePortalStrutsBlank.warfile 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
- 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.
- 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.
- 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\tlddirectory and select Ok. - Highlight (do not click the checkbox) of tld; then, in
the right pane click the checkbox for
portlet.tldand click Finish.
- 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.
- Change the
<portlet-app-name>and the<portlet-name>to meaningful names. - Change the
<portlet-app uid>to a unique identifier. - Change the href to the relative servlet id that was specified as the
web.xml <servlet id>value. For example, if you specifiedContactsListPortletfor the servlet id in theweb.xml, then you would specifyWEB-INF/web.xml#ContactsListPortletfor the portlet href inportlet.xml. - Specify edit and help modes support for html devices as well as view mode.
- For the concrete portlet app, specify a unique uid and meaningful
names for both the
portlet-app-nameand theportlet-name. - Change the concrete-portlet href value to match the portlet app
<portlet id>value. - 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> |
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.
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;
} |
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.
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:
- Adding a contact
- Editing a contact
- Main view page
- Main edit page
- 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
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®, 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.
- 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).
- In the Application Server Administrative Console, select Resources =>JDBC Providers.
- 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.
- On the DB2 Legacy CLI-based Type 2 JDBC Driver page, ensure
that the classpath is set to the correct location of the
db2java.zipfile. - 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 ofaim, enterjdbc/aim. The portlet will prepend thejdbc/sub context when attempting to a JNDI name lookup. - If your database requires a userid and password for authentication, specify an authentication alias in Component-managed Authentication Alias.
- If you do not already have an alias defined, you can create one in Security => JAAS Configuration => J2C Authentication Data.
- Be sure to save the configuration changes and test the data source
connection.
Figure 5. 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
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.beans | ContactHelper | Helper class to invoke persistence management functions for contacts |
| Contact | Struts form object that represents a Contact | |
| com.ibm.sample.contacts.struts.action | AddContactAction | Handles requests to add a contact |
| DeleteContactAction | Handles requests to delete a contact | |
| DetailViewAction | Handles requests to show the detail contact view | |
| ModifyAction | Handles requests to modify a contact | |
| ModifyContactAction | Handles requests to save the edited contact changes | |
| MainEditAction | Handles requests to show the main edit view | |
| MainViewAction | Handles requests to display the main contact list view | |
| AbstractAction | Abstract class for actions | |
| com.ibm.sample.contacts.struts.persistence | ContactListBroker | Persistence class interface |
| AbstractDBBroker | Abstract class for common function for a database broker | |
| ContactListDBBroker | A database broker implementation extends DbAbstractBroker and implements ContactListBroker | |
| ContactListMemBroker | Memory broker implementation implements ContactListBroker | |
| com.ibm.sample.contacts.struts.utilities | AIMException | Base exception class |
| AIMWrapperException | An AIMException that wraps another (`underlying') exception | |
| AIMMessageException | Exception to terminate processing and issue a user message | |
| Constants | Interface that defines application constants | |
| com.ibm.sample.contacts.struts.nls | ApplicationResources.properties | Default resource bundle contains printable strings |
| ApplicationResources_en.properties | Locale specific resource bundle that contains printable strings |
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.
| Name | Size | Download method |
|---|---|---|
| contactslistportlet_struts.zip | 1311KB | FTP |
Information about download methods
Tim 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.





