The Java Portlet Specification 1.0 (JSR 168) defines the portlet preferences API as the preferred way for portlets to store persistent configuration. In addition to supporting JSR 168, WebSphere Portal V6 provides a multi-level preference store. This article shows portlet programmers how to write standard-compliant portlets that can run on any portal, while also exploiting the configuration handling features of WebSphere Portal. You see different ways of storing persistent configuration in portlets, reasons for and against using the portlet preferences API, and an example portlet which illustrates many of the capabilities of the API.
In many implementation scenarios, you can apply portlet configuration to different scopes; associated user roles can configure a portlet for each scope. For example, end users should be able to modify their personal view of a portlet; however, portal administrators might configure portlet properties for everyone. WebSphere Portal supports configuration scope capabilities through a hierarchical preference store. Later in this article, you see an example of a simple document viewer portlet, which demonstrates how portlet modes are used in the programming model to access and modify configuration scopes, and lets you experiment with overlaying configuration changes in different scope.
A portlet programmer is not always in a position to anticipate which aspects of a portlet should be modifiable, in which scope, for a portal deployment. The WebSphere Portal 6 configuration and programming model lets your programmers develop portlets that use meta-configuration to control behavior. Then, you can enable the deployer or administrator to decide the customizable aspects of the portlet, as you will see in the example.
This article also provides a quick look at the portlet cloning feature that lets you manage multiple configurations for the same portlet code. Finally, you see various constraints to the validity of a configuration, and how you can avoid or manage configuration inconsistencies.
Portlet configuration and file-based stores
Most portlets need some sort of persistent configuration to operate. Configuration enables administrators to adapt the behavior of portlets to their operating environment, and it lets end users personalize the portlet appearance.
In stand-alone Java programs, this sort of configuration is often stored in the file system, typically using properties files, XML files, or the
java.prefs API introduced with Java 1.4. For Web applications, particularly portlets, this sort of configuration storage has two major drawbacks:
- File access is expensive; therefore, configuration is typically only read during application startup. Consequently, changes in the configuration require a restart of the application to become effective.
- Well-written Web applications need to support load distribution in a cluster of application servers. Therefore, the same application is running on multiple machines, and identical configuration files need to be maintained on all servers, which causes extra administrative overhead.
Both of these considerations do not apply to âstaticâ configuration, such as that determined only by the programmer (not by administrators). For example, configuration files that describe the configuration for application frameworks, such as Struts or Spring, could be considered to be part of the code (but written in a âconfiguration-languageâ, rather than in Java). For this type of configuration, using properties or XML files is quite appropriate. You can package those configuration files inside the portlet WAR, and then load them using PortletContext.getResourceAsStream. An update to the configuration effectively constitutes an update of your application version, and requires re-deploying the WAR file. The deployment mechanism ensures that the update is distributed to all cluster nodes.
Storing portlet configuration in a database
Portlet configuration that is not âstaticâ should not be stored in files. Usually, a central database is a more appropriate way to keep configuration that is updated during runtime for these reasons:
- Information can be retrieved more efficiently; therefore, there is no need to preload all configuration during application startup.
- The database is shared among all machines in a cluster so that administration becomes easier.
The Java Portlet Specification provides the PortletPreferences API to enable portlet programmers to take advantage of a high-performance, centralized configuration repository for name-value pairs. In WebSphere Portal, the implementation of this API uses the central portal configuration database, giving you all the benefits of a database configuration store, without the need to deal with database access or setup. Using portlet preferences to store your configuration gives you additional benefits, because they are integrated with the portal data model.
- Access to portlet preferences is governed by portal access control (PAC). Users or administrators can only modify portlet preferences if they have been given the necessary authority.
- Portlet preferences can be partially configured from a portal-provided, generic administration UI, so that you don't always have to code specific configuration views.
- Portlet preferences are recognized by portal configuration tools such as the XML configuration interface and portal scripting. You can use these tools to access and change configuration values from command-line scripts and to transfer configuration between portal installations (for example, between a test and a production system).
- Portlet preferences are available in the WebSphere managed client environment, and can be synchronized between the server and the managed client.
Before looking deeper into using the PortletPreferences API, consider the following cases in which using this API for dynamic configuration might not be appropriate.
- Portlet preferences are only available in portlets (obviously). If you need to share configuration settings with other web applications or even client-side applications, a custom database or an LDAP directory might be a better way to store your data.
- Portlet preferences are not appropriate for storing sensitive information, unless you take explicit care to protect them. For example, if you use portlet preferences to store user passwords without encrypting them, an operator can see all the passwords in an XML configuration export. In WebSphere Portal, you should use the credential vault portlet service to store passwords or other credentials.
- Portlet preferences are treated as strings or string arrays. They don't allow you to express complex data schemes, and they are not intended as a substitute for databases. You can encode complex information as Strings (for example, using Java beans XML serialization), but if you need advanced data management features (such as referential integrity), you need to implement your own database schema, using technologies such as entity EJBs or direct JDBC.
- Finally, because of the underlying database schema and the way that WebSphere Portal loads information from the database, the implementation of portlet preferences in WebSphere Portal works best if you store no more than a few dozen preference values per portlet in each scope (for example, per user, more on scopes later) and if preference values do not exceed 4K (in UTF-8 representation). Therefore, keeping base-64 encoded JPEG pictures in portlet preferences could be problematic. If you need to manage large amounts of data, performance concerns may dictate that you again use other technologies for database access.
That said, portlet preferences offer an easy-to-use and reasonably efficient method to store any sort of portlet configuration that you will encounter in most practical situations. In particular, they are well suited to common use cases such as storing back-end connection data (except for passwords, see above) and per-user display preferences for portlets.
To illustrate how you can use configuration features of the PortletPreferences API, we will use a simple document viewer portlet as an example. Since we are only interested in configuration management, the example portlet does not use a real content or document management system for the documents; it simply displays some static HTML files that are packaged into the portlet WAR file below the /data directory. Also, it does not illustrate how you would manage issues such as caching, which a real-world document viewer would need to deal with.
From a configuration standpoint, the portlet offers the following values that can be customized, without changing the portlet code:
| Configuration value | Preference key | Preference value |
|---|---|---|
|
category of the document |
|
Subdirectory inside the WAR file |
|
name of the document |
|
File name inside the subdirectory |
|
number of lines to be displayed |
|
Integer value between 5 and 20 |
Portlet preferences can have multiple values; that is, you could store a list of strings in a preference. Initially, the example portlet only needs single-value preferences. We will use multiple values later.
You specify portlet preferences and assign initial values in the portlet.xml deployment descriptor. For example:
<portlet-preferences>
<preference>
<name>folder</name>
<value>folder1</value>
</preference>
...
</portlet-preferences>
|
Of course, during runtime, portlets can also store and read preference keys that were not declared in the deployment descriptor.
The Java Portlet Specification states that language-specific names and descriptions for preferences can be declared in the portlet resource bundle. WebSphere Portal does not currently use these values, but it is, nevertheless, good practice to include a brief description of the meaning of each preference in the resource bundle.
portlet.xml: <resource-bundle>PortletResources</resource-bundle>
PortletResources.properties:
javax.portlet.preference.description.document=The file name of the \ selected document inside the selcted folder javax.portlet.preference.description.display-lines=... |
Now that you have provided default values for all the preferences in the deployment descriptor, you can code the normal display mode for the sample portlet (see the doView method in PrefSamplePortlet and view.jsp).
The PortletPreferences object is obtained from the portlet request, which gives the portlet container the necessary context information (such as page or user) for selecting the correct configuration:
PortletPreferences preferences = request.getPreferences(); |
To make accessing the preferences from the JSP a little easier, wrap all the configuration information in Java beans (as the ConfigurationData bean in the example); then, preference values can be accessed as Java bean properties, such as the number of display lines:
public String getDisplayLines() {
return preferences.getValue("display-lines", "10");
}
|
When you write out preference values, you need to specify a default value, (â10â, in this example) which is used if the requested preference has not been defined or if the preference store is unavailable. That default can also be null. As you see, using preferences in your code instead of hard-coded values is really easy.
Tip: Good practice is to escape HTML special characters (using the JSTL <c:out> tag) whenever you write out data from editable persistent configuration (such as portlet preferences) to an HTML page. See the information on âcross-site-scriptingâ in Resources for details.
Of course, the main point in storing values in preferences, rather than hard-coding them, is that you can easily update them during portal runtime. To do so, you usually need a configuration screen in your portlet, which is normally implemented in the portlet EDIT mode. You can write preference values in any portlet action, whether the portlet is in EDIT mode or not; however, using the EDIT mode indicates to the portal what you are doing, which gives you a few advantages:
- You don't need to code an explicit link that takes you to the configuration screen; if your portlet supports EDIT mode, the portal will automatically present UI controls to enter the mode. WebSphere Portal does this as part of the context menu in the portlet skin.
- The portal can also display a link to the EDIT mode from other contexts where it makes sense to configure the portlet; for example, in the Manage Pages portlet of WebSphere Portal.
- Writing preferences is protected by portal access control; not all users that can view a portlet can also personalize it. EDIT mode is only accessible to users that are allowed to store preferences. Storing preferences in the VIEW mode instead can lead to a
ReadOnlyExceptionif this code is executed for users that do not have the necessary permission. - Using EDIT mode leads to a consistent overall portal experience in which users can change configuration data by always clicking on the same EDIT mode button.
Our sample portlet handles preference configuration in the doEdit method, which dispatches to doConfiguration. The associated JSP configuration.jsp displays a HTML FORM for the different configuration values. Submitting the form triggers a portlet action; the action dispatches to the FormEvaluator class where the form parameters are extracted from the request and set as new preference values. To keep to code simple, we use the same keys for form parameters as for preferences, so we can use the same code for all applicable keys:
private void processSetting(String key, String defaultValue)
throws ReadOnlyException {
String value = request.getParameter(key);
if (value != null && !value.equals(prefs.getValue(key, defaultValue))) {
prefs.setValue(key, value);
changed = true;
}
...
}
|
We only set a new value if the submitted parameter is actually different from the current preference value; that is, if the default entry in the form field was changed. Doing so will become particularly important when we later explore advanced preference handling capabilities. For the current functionality, it would not hurt too much if we overwrite a preference with an identical value, except that it could create unnecessary database entries.
When all changes have been processed, the new preferences must be stored to persist the changes to the database; otherwise, the changes would be lost with the next request.
if (changed) {
prefs.store();
}
|
WebSphere Portal preferences store structure
When you run the code above, the question is, who gets to see the changes? The Java Portlet Specification âassumes that preference attributes are user specificâ but gives portal implementors the freedom to add preference sharing concepts, without further defining how such sharing is controlled. In WebSphere Portal V6, if a portlet that supports only EDIT and VIEW modes reads and writes preferences, the changes are only visible to a single user; if you log in with a different account, you see the preference values that were defined in the deployment descriptor. WebSphere Portal indicates this by showing only a âpersonaolizeâ option in the context menu to enter EDIT mode.
[This behavior changed from WebSphere Portal V5.x to V6.0 be more consistent with the spirit of the specification. In WebSphere Portal V5.x, the visibility of preference changes is taken from the behavior of the IBM Portlet API and depends on the access control permissions of the user making the change.]
In practice, there are a many use cases where preference sharing is desired. A simple example is an administrator setting up a read-only page (for example, a portal home page) for all users. If the administrator places a document viewer on that page, he or she wants to specify the displayed document for all users, not just for the administrator. In the portal structure of WebSphere Portal, there are (at least) two scopes for which configuration sharing makes sense:
- Set shared preferences for all users w view a particular occurrence of a portlet on a page, as in the example.
- Set shared preferences for all users that view a particular portlet on any page, or that place the portlet on their own private pages.
Instead of introducing new APIs to treat configuration in these shared scopes, you can use the PortletPreferences API from the standard, by giving the underlying preference store a hierarchical structure with fall-back. You can store preferences for each portlet on 3 layers: personalized, page, and global. Whenever a preference value is âlooked upâ during runtime using PortletPreferences.getValue, the given key is searched first on the personalized layer. If the value for that key has not been personalized by the current user, the look-up defaults to the page layer, and finally, to the global portlet layer.
This fall-back takes place separately for each preference key, so the combined PortletPreferences object that is built for a particular portlet request usually contains a combination of values that come from different levels of the preference store. Some of the preference keys can be personalized for the current portlet window while others are acquired from the shared default levels.
Therefore, for reading preferences, you can share values on multiple layers without requiring any API extensions. But how do you write to the shared preference levels? This is controlled by the portlet mode.By selecting a particular mode for configuration processing, the portlet can indicate to the portal runtime that it is changing configuration on a shared layer. The Java Portlet Specification defines two optional custom portlet modes, EDIT_DEFAULTS and CONFIG, which WebSphere Portal uses for this purpose. The following table shows how portlet modes relate to preference layers.
| Portlet mode | Operates on preference layer | Preference values written in this mode apply to: |
|---|---|---|
|
CONFIG |
global |
All occurrences of the portlet on all pages, but can be overridden by page or user specific values if the portlet code supports this. |
|
EDIT_DEFAULTS |
page |
A particular occurrence of the portlet on a page; they override values that were set in CONFIG mode. |
|
VIEW, EDIT, HELP |
personalized (window) |
The current portlet window; that is, they are specific to the current user and a particular occurrence of the portlet on a page. These preferences override all values that were set on a higher level. |
Of course, when you read a preference value in EDIT_DEFAULTS mode, the returned value does not take into account any user-specific setting from EDIT mode. Therefore, the configuration screen in EDIT_DEFAULTS mode only shows the values that apply to all users viewing the portlet on the current page (that is, those values that you are actually modifying). Analogous behavior applies to CONFIG mode.
Using the hierarchical preference store
To use the configuration hierarchy in the sample portlet, you need to support the additional two portlet modes. First, you need to declare them as custom modes in portlet.xml, and also apply the modes to the portlet (mode names are not case sensitive).
<portlet>
...
<supports>
<mime-type>text/html</mime-type>
<portlet-mode>VIEW</portlet-mode>
<portlet-mode>EDIT</portlet-mode>
<portlet-mode>EDIT_DEFAULTS</portlet-mode>
<portlet-mode>CONFIG</portlet-mode>
</supports>
...
</portlet>
<custom-portlet-mode>
<portlet-mode>CONFIG</portlet-mode>
</custom-portlet-mode>
<custom-portlet-mode>
<portlet-mode>EDIT_DEFAULTS</portlet-mode>
</custom-portlet-mode>
|
Next, you need to extend the portlet code to deal with those modes. The standard portlet API provides the convenience base class
GenericPortlet, which detects the current portlet mode (VIEW, EDIT, or HELP) and dispatches to a specific handler method (doView and so on). We extend this approach with an ExtendedGenericPortlet subclass that adds the support for CONFIG and EDIT_DEFAULTS mode and dispatches to the doConfig and doEditDefaults methods.
public class ExtendedGenericPortlet extends GenericPortlet {
public static final PortletMode CONFIG_MODE =
new PortletMode("config");
public static final PortletMode EDIT_DEFAULTS_MODE
= new PortletMode("edit_defaults");
protected void doDispatch(RenderRequest request,
RenderResponse response)
throws PortletException, java.io.IOException {
WindowState state = request.getWindowState();
if (!state.equals(WindowState.MINIMIZED)) {
PortletMode mode = request.getPortletMode();
if (mode.equals ... // handling for VIEW, EDIT, HELP
} else if (mode.equals(CONFIG_MODE)) {
doConfig(request, response);
} else if (mode.equals(EDIT_DEFAULTS_MODE)) {
doEditDefaults(request, response);
} else {
throw new PortletException("unknown
portlet mode: " + mode);
}
} |
[In the sample, we use the tools package for generic utility classes such as this one, which might be useful for arbitrary portlet projects. Code that is specific to the sample is placed in the prefsample package.]
You do not have to code specific behavior for the two new modes in the sample portlet; you can just dispatch all configuration modes EDIT, EDIT_DEFAULTS, and CONFIG to the same doConfigure method, so we display a common screen for all configuration levels.
An additional feature you might want to consider for a portlet which supports multiple configuration levels, is the ability to âforgetâ personalized configuration values and to revert to the default. You use the PortletPreferences.reset method to enable this functionality. In the multi-layer preference model of WebSphere Portal, resetting a preference means that its value is deleted from the current layer, so that the default from the next higher layer becomes visible again. To enable it in the example, the configuration JSP contains a âresetâ checkbox for each resettable setting, with a form parameter reset.
key. You can handle the reset behavior with the following code in the processSetting method.
private void processSetting(String key, String defaultValue)
throws ReadOnlyException {
String value = request.getParameter(key);
if (value != null && !value.equals(
prefs.getValue(key, defaultValue))) {
prefs.setValue(key, value);
changed = true;
}
if (request.getParameter("reset."+key) != null) {
prefs.reset(key);
changed = true;
}
} |
Resetting a preference has no effect in CONFIG mode (global preference layer) in WebSphere Portal V6 because there is no default value to which it can fall back.
Usually, preference values are set and stored in a portlet action that is triggered by an OK button in the configuration screen, and this button also returns the portlet to VIEW mode. It is essential to perform this action in the configuration mode so that storing the preferences writes to the correct layer.
Use this call inside the action handling code so that the portlet reverts to VIEW mode after the action processes:
actionResponse.setPortletMode(PortletMode.VIEW); |
If you, instead, used the portlet URL for the OK button to return to VIEW mode, the action would have already executed in VIEW mode and any configuration change would go to the personalized layer.
To try out the behavior of the WebSphere Portal preference hierarchy, you can run the sample portlet, which supports all three configuration modes. For example, try this scenario:
- If you have not already done so, download the sample portlet.
- Log in as portal administrator. Deploy the WAR file. In the Manage Portlets administrative, locate the Preferences Sample Portlet. To provide access to the portlet for all users, click the Assign access button, and add the All authenticated users special group to the Editor role on the portlet.
- Now, place two instances of the portlet on a page. Using the Assign page permissions option on the page context menu, add the All authenticated users special group to the Editor role on the page.
- To enter CONFIG mode, right-click on first portlet on the page, and select Configure from the portlet context menu. Change the document setting to
doc2, and apply the change. Observe that the displayed document has changed in both portlets on the page. - To enter EDIT mode right-click on first portlet on the page, and select Personalize from the portlet context menu. Change the Display lines property to
20and apply the change. Observe that this modification does not affect the second portlet on the page. - Log in with a different user account. You see the changed document, but not the modified Display lines property in the first portlet. Now, right-click the portlet, and select Edit shared settings to enter EDIT_DEFAULTS mode. Change the document setting to
doc3and the Display lines property to5. Again, the modification does not affect the second portlet on the page. - Log out, and switch to the first user account. Observe that the first portlet still uses 20 display lines, because the personalized value still takes precedence over the 5 that was set at the shared level. However, the displayed portlet is now doc3; the shared value that was changed with the other account âshines throughâ, even though the portlet has been personalized by the current user, because that particular key has not been personalized.
Figure 1 shows a tree structure that illustrates the preference store so far. The upper three levels represent the data stored in the portal preference store. The lowest layer depicts the combined preference data that will be retrieved by a portlet with the
request.getPreferencescall.
Figure 1. Peference store structure
- Go into EDIT mode again, and reset the Display lines setting. After applying the change, the value (20) is deleted from the personalized preference level, and the lookup of this setting defaults to the next higher preference layer on which a value has been set. So, the portlet uses the setting 5 from the shared level that you specified in step 6. Observe that the setting does not revert to the default value that it had before it was personalized; instead, it is the current, most-specific applicable value.
Now you can see why it is important that the FormEvaluator code that handles configuration changes does not write all input parameters to the portlet preferences. Code that always sets the complete configuration before storing it effectively prevents later changes on a higher level from âshining throughâ. Therefore, the processing code must check if the input fields in the form have been modified from the existing preference values before setting the preferences.
Otherwise, the "personalize" action in step 5 stores the full configuration, so it effectively "freezes" the personal configuration of the portal administrator at that point. Eventually, the portlet in step 7 would still display document doc2 (because the value was unnecessarily stored at the personal level), even though the value was adapted to doc3 by another user (a page-level editor) in step 6.
Restricting configuration access
Write access to portlet configuration is protected by portal access control, which works by assigning roles on resources to users or user groups. In the case of portlet configurations, the relevant resources are the portlet and the page on which the portlet is placed. The roles that the current user has on these resources determine which configuration levels can be modified.
- A person with USER role on the page and the portlet can view the portlet; the portlet code can read configuration, but cannot write to it. If the portlet code does try to set preferences, that will lead to a
ReadOnlyException. - A person with PRIVILEGED USER role on both the portlet and the page can enter EDIT mode and can personalize portlet settings in VIEW or EDIT mode.
- A person with EDITOR role on both the portlet and the page, can enter EDIT_DEFAULTS mode and set shared preferences on the page level.
- A person with MANAGER or ADMINISTRATOR role on the portlet, can enter CONFIG mode and set shared preferences on the portlet level. Because this configuration level is not associated with a particular page, no specific privilege on a page is required. (The portlet must be visible on an arbitrary page to access the configuration screen.)
- The ADMINISTRATOR role implies all other permissions. In contrast, the EDITOR or MANAGER roles do not imply PRIVILEGED_USER permissions; therefore, these roles, by themselves, do not allow a user to enter EDIT mode or to personalize portlet settings. In particular, the EDITOR role on a portlet has no effect at all unless the portlet supports the EDIT_DEFAULTS mode.
WebSphere Portal access control lets you control in which scope a user may modify the configuration; for example, a page editor can customize the portlet for all users, but only on a specific page.
However, to make role-based access control really useful for portlet configuration, you need to control in which scope and by which role a setting can be modified. A desirable example setup for access control in the document viewer sample might look like this:
- The portal administrator defines, on the global layer, which content categories exist and are selectable.
- Page editors place the document viewer on multiple pages, and select the content category and the document inside the category that the portlet displays on that page. They can also configure a default viewing experience.
- Users cannot change the displayed document, but they can personalize the viewing experience (that is, set the number of displayed lines).
Supporting this functionality requires that we restrict the preference keys that can be modified in a particular mode; for example, in EDIT mode, only the display-lines preference should be editable. The configuration screens for the different configuration modes should offer distinct editing capabilities instead of a single configuration view that enables editing of all preferences. Then, portal administrators can use access role assignments to control which users can enter a specific configuration mode, and thereby implicitly control which configuration values they can modify.
To enable the desired access control setup, you need to introduce "selectable" categories. You need another preference, allowed-folders, that contains the list of permitted values for the folder preference. Preferences can have multiple values for a single key, so you can store the list of selectable folders in a single preference.
Programming configuration views using meta-configuration data
To easiest way to differentiate the configuration screens could be to write a separate JSP for each mode ; for example, the EDIT mode JSP would only show the display-lines preference. The problem with this approach is that the programmer decides which settings are modified on which level, even though the decision should probably be made by an administrator. If you write a portlet for a specific use case and have a precise specification of the desired behavior, that might work out well. However, it is quite problematic when you write a more generic portlet (such as a document viewer) that could be used in multiple organizational environments and by multiple portals.
In fact, the IBM Portlet API has different API constructs for separate configuration layers. Customer feedback has shown that this is annoying in cases where configuration that should be editable by end users is only available on administrator configuration screens, and vice versa.
To avoid this problem, rather than fixing at programming time which preferences are changed in which mode, defer that decision to deployment time so that it can be changed without requiring code modifications. In other words, to make the configuration handling configurable, we introduce âmeta-configurationâ that tells the portlet how the configuration should be handled.
That sounds a bit tricky at first, but the Java Portlet Specification already gives us a good start. It lets you mark preferences as read-only so that they cannot be modified by end users in the standard portlet modes (VIEW, EDIT, HELP). It also proposes that the CONFIG mode can be used to administer such read-only preferences. So, by declaring, (for example) the allowed-folders preference as read-only in the deployment descriptor you configure this preference key to be editable in CONFIG mode only.
<preference>
<name>allowed-folders</name>
<value>folder1</value>
<value>folder2</value>
<read-only>true</read-only>
</preference>
|
There is no API to declare a preference as read-only; it can only be declared in the deployment descriptor and changing that state requires a re-deployment of the WAR file.
The specification does not provide other means of declaring âmeta-configurationâ data, so we needed to invent our own technique to specify preferences that are restricted to EDIT_DEFAULTS mode. As a convention, we used a special non-editable preference locked-pref-keys to hold a list of preference keys that are locked for end users, and should only be administered in EDIT_DEFAULTS mode.
<preference>
<name>locked-pref-keys</name>
<value>folder</value>
<value>document</value>
<read-only>true</read-only>
</preference>
|
Now, with these declarations you can still use a single configuration JSP, you just need to build a little more logic into that JSP to check the meta information and only allow the user to modify the preferences that are appropriate for the current mode. The other configuration options are locked and can either be hidden completely or shown as disabled. Showing them as disabled may be convenient, especially if there is a relationship between the locked settings and those that are editable; the document viewer sample uses this style.
Alternatively, some parts of the configuration can be separated out into their own configuration screens, which can only be accessed if the edited preference is writable in the current portlet mode. The sample uses this technique to control the configuration of allowed document categories (folders) with the allowed-folders preference.
The sample portlet contains some generic utility code in the ConfigurationHelper bean to simplify writing adaptable configuration JSP files. This bean is initialized with the current preferences and uses the following method to determine if a given preference key is writable in the current mode.
public boolean isWritable(String prefName) {
PortletMode mode = request.getPortletMode();
if (mode.equals(ExtendedGenericPortlet.CONFIG_MODE))
return true;
PortletPreferences prefs = request.getPreferences();
if (prefs.isReadOnly(prefName))
return false;
if (! mode.equals(ExtendedGenericPortlet.EDIT_DEFAULTS_MODE)) {
String[] lockedKeys = prefs.getValues("locked-pref-keys", null);
if (lockedKeys != null && Arrays.asList(lockedKeys).contains(prefName))
return false;
}
return true;
}
|
You can now pass a helper bean to the configuration JSP.
<jsp:useBean id="chelper" type="tools.ConfigurationHelper" scope="request"/> |
You can use scriptlets, such as the following, to display JSP sections conditionally, depending on the meta-configuration for preferences.
<% if(chelper.isWritable("document")) { %> |
With the JSP 2.0 specification, a JSP programming style using JSTL tags and the JSP expression language is favored over the use of scriptlets. The JSPs in the sample portlet also use this newer programming style. Java function calls cannot be easily integrated into JSP expressions; therefore, the ConfigurationHelper bean makes the isWritable method accessible with the writable property that has a Map interface. Maps can be accessed elegantly in JSP expressions, as in this example:
<c:if test='${chelper.writable["document"]}'> |
Important: Using JSP 2.0 features such as JSP expressions requires that you declare version 2.4 of the servlet specification in the web.xml deployment descriptor.
We can make it even easier to declare HTML FORM inputs that are enabled or disabled based on the meta information. The helper bean provides a second pseudo map that provides the HTML attribute text disabled='disabled' for non-writable preference keys (or the empty string, otherwise), so you can write HTML declarations like the following.
<SELECT NAME="document" ${chelper.disabled["document"]} ...> |
Using this style of programming, you can adapt the sample portlet to a different organizational environment with just a few changes in the deployment descriptor. For example, assume that end users should now be able to select the displayed document, while changing to a different folder is still restricted to page editors. All you have to do is to is to edit the portlet.xml, remove the document property from the values of locked-pref-keys, re-package the WAR file and update to the new WAR version using the Manage web modules administration portlet. Now, the document selection is available for end users in EDIT mode. The update procedure can even preserve all the existing configuration values.
Finally, there is one important difference in the type of meta information that we use to mark the portlet mode and scope for a particular preference key. Read-only preferences that cannot be modified by end-users and an optional CONFIG mode for editing these settings are defined by the Java Portlet Specification. Consequently, the portlet container runtime in WebSphere Portal enforces that such preferences can only be modified in CONFIG mode, and setting their value in another mode throws a ReadOnlyException.
On the other hand, using the locked-pref-keys preference to designate additional settings that can only be modified in EDIT_DEFAULTS mode is just our convention, and is not enforced by the portal runtime. Therefore, even if these settings are disabled in the EDIT mode configuration screen, users could still circumvent this restriction by hand-crafting an action URL with additional parameters for setting preferences that are not accessible in the configuration screen. To rule out this possibility, you must explicitly repeat the isWritable check in the action handler before preference values are written.
private void processSetting(String key, String defaultValue)
throws ReadOnlyException {
String value = request.getParameter(key);
if (value != null && !value.equals(prefs.getValue(key, defaultValue))) {
if (! chelper.isWritable(key))
throw new ReadOnlyException("Cannot update "+key);
prefs.setValue(key, value);
changed = true;
}
...
|
Portlet cloning and the portal administration UI
You have seen that the global preference layer is initialized from the preference values specified in the deployment descriptor and that this is the only layer that can be configured without association to a specific page. Actually, for many types of generic portlets (for example, viewers for different data formats), it is useful to have a single WAR file and installed code, with multiple, associated global configurations. In the case of the document viewer sample, the administrator can set up multiple versions of the portlet for different content categories, and make each one available to a particular user group (by giving them the PRIVILEGED USER role on the portlet) so they can place the portlet on their private pages and select the displayed document.
Instead of requiring that multiple versions of the same portlet be deployed to serve this use case, WebSphere Portal lets you create clones of the portlet, that is, different versions that use the same code and WAR file, which differ only in the global preference layer and the portlet title. Portal documentation sometimes refers to this level of configuration (title, administrator-level preferences and associated access roles) as a âportlet definitionâ.
The portlet cloning feature has the following advantages:
- Clones require fewer system resources than multiple deployed WAR files. Consider the effect on application server startup time if you need 50 separate enterprise applications to support viewing the 50 separate content categories in your back-end information system.
- Clones are just configuration, so they can be managed entirely with portal administration facilities. For example, clone configurations can be transferred between systems simply using the portal configuration tooling; there is no need to work with WAR files and the configuration of the underlying application server to create or transfer portlet clones.
WebSphere Portal provides its own administration UI to manage the preferences of portlets or portlet clones at the global layers. To see an example:
- In the Manage portlets administration portlet, select the sample portlet, and click the Copy portlet button to create a new clone with a different title.
- Select the new cloned portlet, and click Configure portlet. You see a configuration view that lets you edit the preferences on the global layer as raw key-value pairs.
- Change the
documentsetting on the cloned portlet. - Place the cloned portlet on a page, and enter CONFIG mode. You see the changed configuration, because the Manage portlets administration portlet manages the same level of configuration as the CONFIG mode form within the portlet.
Dealing with configuration consistency
So far, you have seen a sample portlet with multiple configuration options and flexible configuration screens, but we have ignored an important problem with the flexible configuration management. How do we insure that configuration values remain semantically valid when they are edited? A valid configuration for the document viewer sample must fulfill different types of constraints, such as:
- Constraints on values for individual preference. We stated that the value of
display-linesshould be an integer between 5 and 20, but so far we have taken no measures to enforce this constraint. - Constraints on the combination of multiple preference values. The selected category (value of
folder) should be contained in the list of allowed categories (values ofallowed-folders). - Constraints on preferences that depend on external factors. The selected document must be available as a file in the portlet WAR.
A common approach to dealing with these constraints is to restrict the configuration views so that the user can only select valid settings. This is certainly desirable from a usability perspective. However, unfortunately, it is not correct to assume that your portlet will always encounter only valid configurations, just because your configuration screens do not allow users to enter invalid data!
That last statement might sound a little puzzling at first, so let us examine a number of reasons why such situations may happen.
Dependency on external factors
Let's start with constraints of type 3. External factors often change over time. In our sample portlet, all documents are hard-packaged into the WAR file, but in a realistic situation, a document store back-end will change over time. A document that was available while the portlet was configured may have been removed at a later time when the portlet is actually rendered. For these types of constraints, usually the only sound approach to deal with inconsistencies is to code your portlet in a fault-tolerant way, and to display an error message if you encounter an invalid configuration during rendering. Sometimes you can use a reasonable fall-back instead. The sample portlet treats the case of an unavailable document with a special error screen.
ConfigurationData cdata =
new ConfigurationData(preferences, getPortletContext());
try {
BufferedReader input
= new BufferedReader(new FileReader(cdata.getFilePath()));
request.setAttribute("documentContent", input);
} catch(FileNotFoundException ex) {
viewJsp = "errorView";
}
|
You might also encounter an invalid configuration during rendering if the portlet configuration can be changed by means other than the portlet configuration screen. We have already talked about portal configuration user interfaces and scripting tools that bypass the portlet configuration screens and operate directly on the underlying configuration store. The Java Portlet Specification has anticipated this possibility and has defined a programming construct that helps to deal with this situation: the PreferenceValidator.
Rather than encoding configuration validity checks directly in the action handler that processes configuration screen input, portlet programmers are encouraged to move this validation logic into a special checker class, which implements the PreferenceValidator interface, and to declare this validator in the deployment descriptor.
<portlet-preferences>
...
<preferences-validator>
prefsample.PreferencesValidator
</preferences-validator>
</portlet-preferences>
|
Then, the validation logic can be performed by tools as well as interfaces that edit the configuration directly and which bypass the rest of the portlet code. To make this work in development environments or stand-alone configuration management tools, a preference validator should be self-contained; that is, it should not depend on any of the following:
- Libraries that are not packaged in the portlet WAR file
- Runtime environments such as EJBs, or other applications in a running application server
- External infrastructure such as remote servers that are accessed over the network
The requirement that it be self-contained usually rules out the use of a preference validator for type 3 constraints on preference values. It is, however, quite useful for enforcing type 1 or 2 constraints, and the sample portlet includes a validator which does exactly that.
public void validate(PortletPreferences prefs) throws ValidatorException {
String lines = prefs.getValue("display-lines", null);
if (lines != null) {
try {
int n = Integer.parseInt(lines);
if (n < MIN_LINES || n > MAX_LINES)
throw new ValidatorException("Display lines must be between "
+MIN_LINES+" and "+MAX_LINES,
Collections.singleton("display-lines"));
} catch(NumberFormatException ex) {
throw new ValidatorException(...);
}
}
String[] allowedFolders = prefs.getValues("allowed-folders", null);
if (allowedFolders != null) {
String folder = prefs.getValue("folder", null);
if (! Arrays.asList(allowedFolders).contains(folder))
throw new ValidatorException(...);
}
}
|
Any call to PortletPreferences.store in the portlet action will trigger the validator and fail with a ValidatorException, if the validator throws one. The restriction for the display-lines value is not yet enforced in the UI. We could enforce it using a drop-down control with all integers from 5 to 20, but option lists of that size are sometimes considered harder to use than simple text inputs. So, we deal with the possible exception by aborting the action. We omit changing to VIEW mode, and present the validator error message in the configuration JSP. (In a production-quality portlet, you would need exceptions with multi-language messages.) You can test that the preference modification in the administration UI triggers the same validator code, so that storing the portlet settings fails (for example) after setting the display lines to 30 or a non-integer string.
Consistency problems in a multi-layer preference setup
For type 2 constraints (multiple preference values), inconsistent configurations can arise as a result of editing preferences independently on multiple layers. You might have seen this situation when trying out the document viewer, where the allowed-folders constraint can cause such a situation. Consider the following scenario.
- The portlet is deployed with
allowed-foldersset tofolder1, folder2andfolderset tofolder1from the deployment descriptor; then, it is placed on a page. - In EDIT_DEFAULTS mode, the folder is set to
folder2. Thendoc4is selected as thedocument. This is a valid configuration that is accepted by the validator, so those two preference values are stored at the page layer. The configuration seen on the page is shown in this table.allowed-folders folder1,folder2 from global layer folder folder2 from page layer document doc4 from page layer
- The administrator decides that category
folder2should not be accessible any more and removes this value fromallowed-folders. On the global level,folderanddocumentare still set to the initial values, so the configuration that is stored at the global level is as follows.allowed-folders folder1 folder folder1 document doc1
- Now, what happens if you leave CONFIG mode and return to view the portlet? The effective configuration is the following.
allowed-folders folder1 from global layer folder folder2 from page layer document doc4 from page layer
However, this configuration is clearly invalid, and would not pass the preference validator test!
You can also run into a similar problem when you first use EDIT_DEFAULTS mode to change the
document only and later change the selected folder in CONFIG mode.
In an abstract view, the problem arises because there are constraints between multiple preference keys that are defined on different layers, and a change on a higher layer (global) can render the values on a lower layer invalid. There are several ways you can deal with these effects.
First, because such problems occur when there are constraints between preferences stored on multiple layers, you can get around the problem by always explicitly setting all the constrained values together, so they are always consistently taken from the same preference layer. Alternatively, you can encode the related values into a single preference string. This solution works well for related values such as city/zip code or x/y coordinates.
Applied to the sample portlet, step 2 sets not only the folder and document preferences, but also explicitly sets the allowed-folders preference to its current value.
| allowed-folders | folder1,folder2 | from page layer |
| folder | folder2 | from page layer |
| document | doc4 | from page layer |
The resulting configuration cannot be invalidated by any changes in CONFIG mode on the global layer. However, this approach undermines the requirement that only a user with administrator role can define which folders are accessible. Even though
folder has been removed from the global configuration, it is still present in the page-layer configuration. You could use the same approach to always tie the selected folder and the selected document. We did not chose to do so because that would not allow us to have a meta-configuration where folder and document are managed on different levels. We cannot tie related preferences together when access control requirements dictate that they are configured on separate layers.
What other options are there? You can employ the two techniques that we have already seen.
- You can detect invalid configurations during rendering and show an error message. In the sample, unavailable documents are treated in this manner. That code will also handle the case where document and folder do not match.
- Sometimes, you can use a preference validator to avoid inconsistent configurations. WebSphere Portal calls the validator when preferences are stored as well as whenever the portal detects that changes on a higher layer might have invalidated the currently visible configuration.
The validator we defined for the sample portlet tests whether the selected folder is contained in the list of allowed folders. So, in the scenario above, the validator runs when we return to VIEW mode in step 4, and the inconsistent configuration is detected.
How does the portal react in this case? Throwing the ValidatorException at the portlet does not make sense, because the portlet is only reading the preferences and has not attempted to create an invalid configuration. On the other hand, if the portlet has defined a validator, it can expect to receive only preference values that pass the validator test. The portal has no way of knowing how specific preference values could be âfixedâ, so the only way it can resolve the inconsistency is by discarding entire preference layers, until the resulting configuration is valid. See the WebSphere Portal InfoCenter for a detailed description of the exact algorithm for resolving invalid configurations.
In the example scenario, the page layer configuration will be hidden, and the next higher layer (the global layer) is eventually a valid configuration that gets returned to the portlet.
| allowed-folders | folder1 | from global layer |
| folder | folder1 | from global layer |
| document | doc1 | from global layer |
| [folder] | [folder2] | page layer is not visible to the portlet |
| [document] | [doc4] | page layer is not visible to the portlet |
The hidden configuration layers are not deleted from the database at this point; they only become invisible to the portlet. Other portal APIs and configuration management tools can still access the âinvalidâ data. Only when the portlet stores new values on the hidden layer, is the invalid data permanently discarded.
In the example, when you return to CONFIG mode and re-allow
folder2, the page layer configuration with doc4 selected becomes visible again. That behavior might seem a bit disruptive to users, but that is how a portal with hierarchical access control is supposed to work. If somebody with higher access privileges makes a decision that affects others, this decision is enforced and takes precedence, even if it might be disruptive to the affected users.
If you have defined a preference validator that can have this effect of hiding invalid configuration, you can indicate to users that there is a problem to reduce the confusion. WebSphere Portal reports this situation to portlets by setting a special property on the portlet request with the constant key:
com.ibm.portal.portlet.constants.REQUEST_HAS_INVISIBLE_PREFERENCES |
If you want to avoid coding a hard dependency on WebSphere Portal interfaces into your portlet, test against the string value of this constant, as our sample does in the ConfigurationHelper class.
public boolean isPartiallyInvisible() {
return request.getProperty
("com.ibm.portal.portlet.request-has-invisible-preferences") != null;
}
|
The sample uses this indication to display a small information message in the configuration JSP.
<c:if test='${chelper.partiallyInvisible}'>
<P class="portlet-msg-alert">
Parts of your portlet configuration are currently invisible
because there is a conflict between your customizations and
administrator settings.
</P>
</c:if>
|
In summary, we recommend you use preference validation when you have configuration data that leverages the preference hierarchy of WebSphere Portal to avoid presenting the user invalid data.
This completes our tour through the usage and features of the portlet preferences API in WebSphere Portal. In the coding examples, we did not use any APIs or programming constructs that are not part of the standard Java Portlet API. Therefore, you could run the sample portlet on any JSR 168 compliant portal. Still, we are also exploiting features that are specific to the behavior of WebSphere Portal; these might not be supported in other implementations.
EDIT mode and the support for personalized configuration through preferences are required by the standard. Support for EDIT_DEFAULTS mode and CONFIG mode is defined in the standard but optional for implementations. The specification also does not mention in which scope preferences set in these modes are visible, although these preferences clearly are intended to affect more than a single user. Finally, implementations are not required to provide a hierarchical preference store with multiple layers shadowing each other.
We believe that the preference store implementation of WebSphere Portal is a useful and compatible extension of the concepts defined by the standard and that it enables programmers to write portlets that can flexibly adapt to a large number of possible use cases and organizational environments. Hopefully, this article has demonstrated how to achieve this flexibility, and has presented a useful set of tools and techniques that you can apply in your own portlet projects.
To assist you in getting started, the source code of the sample portlet has separated the classes that are specific to our example use case (in the prefsample package) from generic code fragments that are independent of the sort of portlet you might be writing (in the tools package). You can the latter code for other portlet projects so it can help you further improve the flexibility and usability of your portlets.
| Description | Name | Size | Download method |
|---|---|---|---|
| Sample portlet | PreferencesSample.war | 29 KB | HTTP |
Information about download methods
- Participate in the discussion forum.
-
What's new in WebSphere Portal Version 6?
-
WebSphere Portal 5.1.0.1 Programming Model series
-
Java Portlet Specification 1.0 (JSR 168)
-
Best practices: Developing portlets using JSR 168 and WebSphere Portal
-
CERT advisory concerning cross-site scripting attacks is relevant for preference handling because you often construct HTML output for user A that includes data (preference values) set by another user B (for example, the page editor). In that case, user B could potentially inject scripting code into the data that tries to steal credentials from the active user A. To avoid this, special characters in the data need to be escaped.
-
WebSphere Portal product documentation provides access to the all InfoCenters, release notes, readmes, and so on for all releases of the WebSphere Portal family.
-
WebSphere Portal zone provides a wide variety of technical resources to help you build and extend your own portals.

Oliver Köth is the lead developer for the portlet container in WebSphere Portal and is responsible for the implementation of the Java Portlet Specification 2.0 in the product. He holds a Diplome of Computer Science from the University of Erlangen, Germany and has been working on IBM’s portal mission since 2001 in the IBM Böblingen Development Laboratory.

Stefan Hepper is the responsible architect for the WebSphere Portal and Workplace programming model and public APIs. He co-led the Java Portlet Specification V1.0 (JSR 168) and is now leading the V2.0 (JSR 286) effort. Stefan received a Diploma of Computer Science from the University of Karlsruhe, Germany, and in 1998 he joined the IBM Böblingen Development Laboratory.
Comments (Undergoing maintenance)





