ShowTable of Contents
Summary
Implementing IBM® Lotus® Web Content Management bookmark portlets is, in
itself, a straightforward process. However, this article describes an implementation whereby
the portlets can be on many pages, which means it is important to design bookmark portlets
that render efficiently.
Contents
1 Introduction
2 Persistence
2.1 Portlet preferences
2.2 Custom database
2.3 Taxonomy element
2.4 Web Content Management site areas
2.5 Property extension database
3 In-memory storage
3.1 Rendering the bookmark portlets
4 Updating bookmarks
4.1 Delete action
4.2 Save action
4.3 Alternative using the PUMA REST API
5 Rendering detailed listing portlets
6 Conclusion
7 Resources
Introduction
This document discusses a design approach for supporting Lotus Web Content Management (hereafter called “Web Content Management”) bookmarks portlets that was derived from customer requirements. To get the most from this article, you should have knowledge of IBM WebSphere® Portal, Web Content Management, and writing custom portlets.
Consider a simplified version of a bookmarks portlet, My Favorites, which provides a list of favorite articles for a specific user (see figure 1). The articles are stored in Web Content Management, and clicking on one of the items navigates the user to the detailed rendering of the article.
Figure 1. Bookmarks portlet
There are other variations of this list, for example, a list of graphics or charts, or a list of categories. In the case of categories, clicking on one of the items navigates the user to a list of recent articles for the category.
In these kinds of portlets, if there are many items in the list, you may also have the option of clicking the View All link to get a detailed list of the bookmarked items. You can also delete items from this list, indicated by the 'X' by each item.
So far this is standard fare. But what if one of the requirements from a customer were that these portlets are to be rendered on most pages? In that case, it would be important to carefully consider rendering performance.
The approach described here was designed to meet the specific requirements of portlets that could be placed on most pages and deliver information specific to the user. We describe not only this customer-derived approach but also some alternatives that can be applicable to other applications with similar requirements.
We'll cover the following design aspects in the remainder of this document:
- Persistence– how to store the bookmark data.
- In-memory storage– how not to always go back to the persistent store.
- Rendering book marks– how to retrieve and render the bookmarks.
- Updating bookmarks– how to add and remove bookmarks.
- Rendering detailed bookmark listings – how to not only retrieve and render the bookmarks, but also to include detailed information about the content.
Persistence
One of the important decisions to make is how to store the bookmarks. This is key because it can impact performance significantly. The information that needs to be stored for each bookmark is as follows:
My Favorites
- Display title
- Web Content Management content name and path (to pass as WCM_GLOBAL_CONTEXT value)
- Web Content Management Document ID (for verification)
- Alternatively, a link to a thumbnail image
A favorite categories portlet would require the category name to be stored rather than content name and path.
Table 1 summarizes the persistence approaches for bookmark data along with their pros and cons, followed by more detailed descriptions of each. The rest of this article assumes the Property extension database approach, listed last.
Table 1. Summary of persistence approache
Persistence approach | Pros | Cons |
Portlet preferences |
- Known paradigm for bookmarks,
- Possible re-use of existing bookmark portlet source
|
- Cannot render the bookmarks on multiple pages
- Custom development
|
Custom database | Flexibility | Custom development |
Web Content Management taxonomy element | Ease of development | May not have the necessary flexibility,depending on requirements |
Web Content Management site areas | Ease of development | Possibly performance |
Property extension database |
- Performance
- Flexibility
- Built-in mechanism to optionally load values into memory on login
| Custom development |
Portlet preferences
Portlet preferences is a natural approach to consider for this portlet. For one thing, there already exists an out-of-the-box bookmark portlet that uses this approach, and at first glance it makes a lot of sense since the preferences can be stored per user.
However, a down side to this approach for our requirements is that the portlet preferences are stored per page, and we want the same list to render on all pages that the portlets are on. There are methods to address this issue.
For example, it is possible to modify the portlet palette (“flyout” on the right in the theme) to render portlets, and to have the palette open by default. Since the palette is an i-frame to a portal page, it looks to the user like it is part of the page. And from a portlet preferences point of view, it is the same portal page no matter where the user navigates in the site with the modified theme.
Since it is the same portal page, the portlet preferences will show the same list to the user on all pages to which they navigate. The steps to modify the portlet palette toachieve this are not currently documented, and it is not a straightforward process since there is a tight coupling between the portlets on the palette and the theme.
Another down side to this approach is that the detailed listing portlets also need to know the bookmarks. There are ways retrieve the preferences from another portlet, but it is not a typically portlet paradigm.
Custom database
A custom database can certainly be used to implement the bookmarks, and it offers the most flexibility; however, the down side is that it probably requires the most development work since a persistence API layer is needed. Also, custom deployment needs to be addressed for creating the tables.
A related approach is to build a custom WebSphere Portal personalization (PZN) resource collection to the database tables. The advantage is that you can use some out-of-the-box capabilities such as applying PZN rules and IBM Rational Application Development for WebSphere Software (RAD) wizard tooling.
Performance would need to be verified, however, because this would be a “highly personalized” use of PZN rules, and not a typical a use of PZN engine. In other words, the information rendered would be specific to the user and rendered on many pages.
In either approach, cleanup of user data would need to be addressed as users are deleted from the system over time.
Taxonomy element
The Web Content Management taxonomy element may apply to the general concept of My Favorite Categories, and this may be a good option for some users for that particular portlet. However, some of the requirements we needed to address for our customer made the application of the taxonomy element not straightforward or comprehensive.
Web Content Management site areas
You can use Web Content Management content items to store the user bookmarks; for example, you could have a site area per user in a special library. The site area could contain content links to the bookmarks articles and charts (though this approach would not work for My Favorite Categories).
The advantage of this approach is that development time should be much faster since you could take advantage of components such as menus and navigators.
The down side is that performance may not be as good as other approaches since the information rendered is specific to each user on many pages. This is a different use case from the typical ones for which Web Content Management and its caching is intended.
As with the custom database option, cleanup of user data would need to be addressed as users are deleted from the system.
Property extension database
The approach assumed in this article is to use the WebSphere Portal property extension database (look-aside). Property extensions allow attributes to be associated with users and groups without the need to modify the LDAP. For further information, refer to the WebSphere Portal Information Center topic, “
Configuring a property extension database on Linux.”
At the beginning of this section we listed the specific data that needs to be stored. You might have thought we could just store the Web Content Management document ID, and retrieve everything else from there.
The concern with that approach, however, is that it would require Web Content Management lookups of information to create the links, a potentially slow step that would be repeated in a similar way for each portlet on each page. This can hinder performance since these bookmarks are specific to the user.
We decided to store the minimum we needed to render the links, as well as storing the document ID for verification purposes.
One of our assumptions is that content is not deleted or expired, so we don't need to be concerned about broken references to content. If that assumption is not for you, then you will need to handle the case of broken references to content. Even with this assumption, though, display titles can still change, so some mechanism must be in place to periodically update the display titles. This can be done, for example, via:
- a custom log-in method, or
- checking/refreshing once during a user session and marking that the check has been completed on a session attribute, or
- a background task
The periodic checks and refreshes may be slow at first, but subsequent renderings should be much faster.
All data types are multi-valued variable-length string properties. The three fields of data for each bookmark (for example, display title, document ID, and path) can be stored as separate properties, but that can make sorting difficult. An alternative is to have one multi-valued field for each bookmark, in which the property value is delimited appropriately.
As users are deleted from the LDAP, the property extension database would also need to be periodically cleaned up.
In memory storage
Since these portlets are rendered on most pages, the associated data will ideally be stored in memory for fast access. Since the WebSphere Portal property extension database has been chosen as the approach, WebSphere Portal User Management can be used for this purpose.
The Puma Store Service contains the configuration settings for Portal User Management. For more information, refer to the Information Center topic, “
Portal configuration services.”
The following property configures both the Portal User Management and the PUMA SPI:
store.puma_default.user.base.attributes =
Defines the attribute subset that portal loads during direct user lookups, for example at Login. Attributes that are not defined in this list are loaded by a separate request to the backend user store.
We used this property to indicate that the properties needed for the custom portlets are loaded at login.
===Rendering the bookmark portlets
The bookmark portlets are built as custom JSR 286 portlets (recall figure 1).The portlets use the PUMA Java SPI to retrieve the bookmark data for the logged-in user. The display title and path/name data are retrieved from the property extension database. Using this data the portlets construct and render the appropriate links to the portal pages that render the Web Content Management content details pages.
For more information on using the PUMA Java SPI, see the WebSphere Portal wiki article,
“
Developing with the PUMA SPI.”
Listing 1 shows a code snippet demonstrating how to retrieve extension properties using the PUMA Java SPI:
Listing 1. Code to retrieve extension properties
try {
com.ibm.portal.um.PumaProfile pp
= pumaHome.getProfile(request);
com.ibm.portal.um.User currentUser = pp.getCurrentUser();
pp.reload(pp.getCurrentUser());
ArrayList userAttrs =
new ArrayList();
userAttrs.add("MyBookmark");
Map userAttrMap = pp.getAttributes(currentUser,
userAttrs);
if (userAttrMap !=
null)
{
java.util.List attrValues
= (
java.util.List) userAttrMap.get("MyBookmark");
if (attrValues !=
null) {
for (
Iterator attrValuesItr = attrValues.iterator();
attrValuesItr.hasNext();)
{
render link....
}
}
...
Updating bookmarks
As mentioned above, the “X”'s next to items in the My Favorites portlets specified in the wire frames indicate a UI element that allows an item to be deleted from the user customization data (recall figure 1).
Adding bookmarks for articles is initiated from different portlets. That is, links on article detail pages will initiate the add bookmark requests from a number of Web Content Management rendering portlets. When a bookmark is added, the side portlets must refresh to show the updated list (see figure 2).
Figure 2. Article detail portlet

Delete action
The delete action can be supported by dynamically submitting a form via JavaScript. This action updates the appropriate property extensions via the PUMA Java SPI, removing the corresponding content location, name, and display information.
The page is then refreshed in a traditional server-side manner; that is, by calling the PUMA Java SPI to render the list view.
Listing 2 shows methods that demonstrate removing property extension values with the PUMA Java API:
Listing 2. Removing property extension values
public void removeValue
(ActionRequest request, String AttributeName, String attributeVal)
throws PortletException, java.io.IOException {
try {
List attrValues = new ArrayList();
attrValues.addAll(getMultivalueStringField
(request, AttributeName));
attrValues.remove(attributeVal);
setMultivalueStringField(request, AttributeName,
attrValues);
} catch (PumaAttributeException e) {
throw new PortletException(e);
} catch (PumaModelException e) {
throw new PortletException(e);
} catch (PumaException e) {
throw new PortletException(e);
}
}
private List getMultivalueStringField
(PortletRequest request, String attrName)
throws PumaAttributeException, PumaModelException, PumaException {
PumaProfile profile = pumaHome.getProfile(request);
List attributes = new ArrayList(1);
attributes.add(attrName);
Map userAttribs
= profile.getAttributes
(profile.getCurrentUser(), attributes);
List list =
(List) userAttribs.get(attrName);
return list;
}
private void setMultivalueStringField
(ActionRequest request, String attrName, List attrValues)
throws PumaAttributeException, PumaModelException, PumaException {
PumaProfile profile = pumaHome.getProfile(request);
PumaController controller = pumaHome.getController(request);
Map