Portals organize and structure enterprise content. They provide a common entry point and navigation to lead users to the information they require. However, users often want to simply jump directly to specific content.
For example, a company sends out an e-mail to all employees requiring them to read a policy update; the e-mail includes a direct link to a specific page in the guidelines document using a document viewer. Then the e-mail instructs employees to confirm they have read the guideline by clicking on a direct link to a specific page in another application.
The key to addressing content in Web applications is, of course, URLs. Beginning with Version 5.1 of WebSphere Portal, you could provide your users with rich and bookmarkable URLs to jump directly to any portal state that a user had previously selected and bookmarked. However, because of their richness, these URLs use an opaque encoding format and can only be constructed programmatically from code that is running inside WebSphere Portal. Also, the resulting URLs are not portable across portal installations, such as between a test and a production system.
You can also use administrator-defined URL mappings to link to individual portal pages with fixed, human-readable URLs. URL mappings, however, do not let you use the full range of state information that can be addressed in the portal. They can only select a page; they cannot control portlets on that page or the selected language, for example.
Version 6.0.1 of WebSphere Portal fills the gap between these two URL formats by providing a new pluggable URI decoding framework: the portal URI resolution service. The idea behind this service is to convert the identification (URI) of a piece of content into WebSphere Portal state (for example, a selected page), which is represented by a portal URL. During this resolution step the service lets you address the full range of possible portal states with URI formats that are well-defined and can be constructed outside of the portal. You can customize the URI formats for your application using pluggable formats and decoders.
This article covers advanced programming interfaces that help you tailor more complex portal setups to your needs. Readers of this article should have a basic understanding of navigational state and URL representations in WebSphere Portal; these concepts are described in detail in Exploiting the WebSphere Portal 5.1.0.1 programming model: Part 2: Advanced URL generation.We also refer to administrative concepts like portal page structures and unique names, see the Resources section of the article for more information. Some programming experience with advanced portal SPIs like the model SPI is helpful but not required.
The URI resolution service architecture
The purpose of the URI resolution framework is to provide a mechanism to translate a resource identification (that is, a piece of content or POC) in the scope of WebSphere Portal to a portal view of that resource. As a simple example, consider a document that can be identified by a unique identifier such as "author42-2007-08-04x39".
To view this document in the scope of WebSphere Portal the user must navigate to the corresponding document viewer portlet, look for the document, and then select it for display. This process requires a good deal of detailed knowledge by the end user such as the page that hosts the document viewer portlet, the correct library, and so on.
With the URI resolution framework you can directly address a viewer for the document with a unique identifier (a URI) for the resource; your users don't need to know which portal page displays the resource and how it needs to be configured. The framework translates the global resource identity which is represented by the URI into WebSphere Portal view state, that is, a portal page with one or more preconfigured portlets. This means you define a URI format (an article: scheme) for document identifiers leadingk to URIs such as:
article:author42-2007-08-04x39 |
The framework can then translate this information into an actual portal URL such as:
http://acme.com/wps/myportal/!ut/p/c1/04_SB8K8xLLM9MSSz... |
In this article we refer to the resource identity as the URI of a "piece of content", also called POC URI or simply URI. The URI only specifies the identity, not the location of the view of the document. The location of the view to the document is called the portal URL, or simply URL and the information contained in this URL is the navigational state, or simply state.
The following figures illustrate two use cases. In Figure 1 a user addresses a resource (POC) directly from outside the portal, that is, without existing navigational state. He does so by sending an HTTP GET request to a POC servlet that is part of the WebSphere Portal; the URI of the POC is passed as a query parameter to this servlet. For the example POC URI given above, the requested URL would be:
http://acme.com/wps/poc?uri= article:author42-2007-08-04x39 |
The servlet calls a pluggable resolver that translates the URI into navigational state, constructs a URL out of this navigational state, and redirects to this URL.
http://acme.com/wps/myportal/!ut/p/c1/04_SB8K8xLLM9MSSz... |
As a result the user is represented with a view to the addressed resource. The details about what this view displays are implementation details of the associated resolver.
Figure 1. Addressing a POC from outside the portal
This is the use case which we demonstrate in the example scenario for this article.
In another use case, as shown in Figure 2, the user leverages the URI resolution framework during a conversation with WebSphere Portal. A typical example is the interaction with a search result portlet that displays links to arbitrary search results without being aware of the viewer portlets or pages that would display those results. The search result portlet can do this by providing an "indirect link": a portal URL that has a POC URI as the target instead of an explicit portal page. Because the indirect link is still a portal URL, it can retain navigational state. When the user clicks such a link, WebSphere Portal detects that a POC URI is selected, invokes the resolution framework, and displays the result of the resolution process, typically after an additional redirect that is needed to re-initialize portal request processing.
Unlike the first use case, the navigational state that defines the current conversation of the user with the portal is preserved, and only the part that is required to display the POC is modified
Figure 2. Addressing a POC within a portal page
From the perspective of the resolver component that translates from a URI into navigational state, both use cases are equivalent. The resolver receives a URI and modifies the current navigational state accordingly. The current navigational state may be empty (for the first use case) or pre-populated (second use case).
The resolution process couples resolvers to URIs; that is, when a resolution is requested, the framework looks for a resolver that can handle the URI. You declare this coupling in an XML file based on the URI scheme. Resolvers are extensions that plug into the Eclipse extension framework (see Resources). The Eclipse extension points to which the resolvers plug into are defined by a sub-component of the portal URI resolution framework, called the Content Operations Registry (COR). Figure 3 depicts the flow of operation during a resolution process.
Because resolvers are implemented as Eclipse extensions, they can be packed and deployed so that they are known to the portal class loader. You can either bundle them as JAR files deployed into the portal class path, or you can include them in portlets or servlet WAR files that are deployed dynamically at run time. The latter approach facilitates development work because it does not require server restarts to update a resolver; you can simply redeploy the WAR file.
Accessing the resolver servlet
For the first use case of addressing a piece of content from outside the portal, the user issues a request to the POC servlet of WebSphere Portal. This URL has the following format:
http://<server>:<port>/wps/[my]poc?uri=<uri> |
The "uri" parameter carries the identity of the resource and its value must be a valid POC URI. The URI is passed as a query parameter and may need to be encoded accordingly.
The POC servlet has two servlet mappings that can be accessed: /poc and /mypoc. These are similar to the /portal and /myportal mappings for portal access. The /mypoc mapping enforces a valid authentication. The /poc mapping does not require authentication but will recognize if valid authentication or single-signon information is present. If that case, the resolver framework will redirect to /myportal; otherwise, it redirects to /portal.
In many usage scenarios it is appropriate to address the /poc mapping, because it will detect potential authentication information automatically.
In any case, access control to the actual page and portlets to which the POC URI resolves is enforced by the portal.
Now, let's look at how you can use the URI resolution framework in combination with other portal APIs to implement a custom URI scheme mapping to portal pages. In the example that follows, you see how to set up a URI resolver with a demo configuration as well as a simple administration tool for URL mappings.
The example URI resolver lets you manage your URI mapping definitions to address portal pages and to control the state and content of portlets on the pages. This sort of URI mapping gives you significantly more control and flexibility than the built-in URL mapping feature that comes with WebSphere Portal. We call it a "deep link" URI resolver, because it enables stable links to arbitrary content within the portal installation (see http://en.wikipedia.org/wiki/Deep_link).
The incoming URL in our example consists of a keyword that is looked up in an internal XML-based configuration dictionary. For each keyword, the dictionary stores the target page and information about how portlets on that page should be displayed. We use the portal "custom unique names" feature (see Resources) to reference pages and portlets from this configuration.
For example, suppose the incoming URL is:
http://acme.com/wps/poc?uri=deeplink:link1 |
The code would look up the keyword link1 in the dictionary, which could point to the following entry:
<deeplink linkname="link1" page-uniquename="hr.news"> <portlet uniquename="company.news" state="maximized"> <parameter name="article">2007-08-04x39</parameter> </portlet> </deeplink> |
So, accessing the URL leads to the
hr.news page, maximizes the company.news portlet on that page and passes an article parameter to the portlet, presumably directing it to display a specific news item. In terms of the URI resolution framework, this service converts the URI deeplink:link1 into WebSphere Portal state.
Installing the sample scenario
The components of the sample scenario described above are packaged together in the uri-resolver-sample.zip file in the Download section of this article. To install and run this scenario, you need Version 6.0.1 or higher of WebSphere Portal.
Extract the zip file into the portal installation directory. You see the following components:
-
/installableApps/DeeplinkProvider.war - The deeplink URI mapper packaged together with a simple administration portlet.
-
/installableApps/testportlet.war - A simple test portlet that prints out render parameters.
-
/shared/app/deplinkConfig.xsd - A sample configuration file for the deeplink URI mapper
-
/URIResolver-Setup.xml - The xmlaccess script to install the portlets and create sample pages
After extracting the zip file, you can install the scenario by running the following command (on a single line) from the bin directory of your portal installation:
> xmlaccess -user adminid -password adminpwd -url portalserver_and_port
-in ..\URIResolver-Setup.xml
|
For a default development installation, the correct parameters would be
>xmlaccess -user wpsadmin -password wpsadmin -url localhost:10038
-in ..\URIResolver-Setup.xml |
Running the script installs the two portlets mentioned above, as well as the pbjsrshipexample.war portlet application that is included in the examples shipped with WebSphere Portal. We use portlets from this property broker shipping example application in our setup, because they provide an example of JSR 168-compliant portlets that can be controlled externally by well-defined parameters.
In addition to installing the portlet WAR files, the setup script does the following:
- Creates two example pages:"Deeplink Page 1" which contains the test portlet"Deeplink Page 2" which contains the test portlet along with the "Standard Order Detail" and "Standard Customer Detail" portlets from the property broker shipping scenario.
- Assigns custom unique names to the pages and the portlets contained on the pages so that they can be referenced from the deep link configuration.
- Assigns access control so that "Deeplink Page 1" is visible to all users and "Deeplink Page 2" and the target portlets are visible even to anonymous users.
- Creates a new administration page "Deep Links" with the deep link administration portlet in the "Portal User Interface" section of the portal administration pages.
After the installation, you might need to restart your portal server so that the URI resolvers are initialized correctly.
Download deeplinkResolver-source.zip to get the source code and eclipse project files for the deeplink URI mapper.
After installing the scenario, view the configuration portlet, which lists all the currently configured links:
Figure 3. The DeepLink administration portlet
For simplicity, the configuration is kept in an XML file portal/shared/app/deeplinkConfig.xml. You can edit this file manually and reload it by clicking the Reload configuration file button in the administration portlet. For a production-ready implementation, you would need to change the implementation to use a database instead, to enable clustering, and that would also require better tools for configuration updates.
You can click on the defined links in the admin portlet to test them directly; you see that they are accessing the POC servlet with the format that we introduced before:
http://server:port/wps/poc?uri=deeplink:linkname
|
The point of using the URI resolution service is that you can use that stable portal URI in any static HTML content to refer to the target page with its configuration. For example, you can copy the following URI into the address bar.
http://server:port/wps/poc?uri=deeplink:link6 |
It will take you to the second sample page, minimize the first two portlets, and show the data for the customer ID 8288473 as defined in the corresponding XML configuration:
<deeplink linkname="link6" page-uniquename="testpage2">
<portlet uniquename="testportlet" state="minimized"/>
<portlet uniquename="orderDetail" state="minimized"/>
<portlet uniquename="customerDetail" state="normal">
<parameter name="customerId">8288473</parameter>
</portlet>
</deeplink>
|
The resulting page looks like this:
Figure 4. Customized page view using URI resolution
The URL in the address bar is not the content URI that you entered in the address bar; the portal redirects you to a "standard" portal URL here. However, this is just an implementation detail of the current version of WebSphere Portal that you don’t need to care about when writing your own resolution service.
Defining and implementing a URI resolver
Now let's look at a concrete example of plugging your own URI resolver into the infrastructure. In this section, we investigate the "deeplink" URI resolution service.
The service handles a custom URI "deeplink" scheme with a very simple structure. The scheme-specific part of the URI is used as a key to look up mapping information in a custom mapping repository. So the
deeplink:link1 URI can be seen as a custom mapping from link1 to the information (page, and so on) stored in the repository for the key link1. In URI resolution service terms, deeplink:link1 is regarded as a resource that has an associated portal view defined in the configuration file. The resolution process translates from the URI into the navigational state.
Defining a resolution service handler
The central interface in the creation of a custom URI handling code is com.ibm.portal.resolver.ResolutionService. This interface enables custom implementations to analyze an incoming URI and to modify the current state of the portal with information that was decoded from the URI.
We will examine the implementation of the deeplink resolver in a later section. For the moment, let us take a look at the interface and the data that it accesses.
public interface ResolutionService {
… some constants …
boolean resolve(Resolved res, URI uri,
String verb, Map params, Set acceptedBindings, Context ctx)
throws ResolutionException, StateException;
|
The processing of the incoming URI is done in the
resolve method, which takes a number of arguments:
-
The
Resolvedobject is used by an implementation to pass on the outputs of the URI resolution process. In particular, it provides access to a StateHolderController that can be used to manipulate portal state such as the selected page. -
The URI is the incoming URI that should be decoded.
-
The verb is used to distinguish between different ways of accessing the same resource specified by the same URI.The
viewverb indicates that the handler does not provide actual data for the response; instead, the framework should dispatch to normal portal rendering after all applicable handlers have completed their processing. The result of the request is a normal portal page, which will typically show a viewer portlet for the resource specified by the URI. Thedownloadverb, on the other hand, indicates that the URI should not resolve to a portal page, rather, it should resolve to the content of the resource specified by the POC URI. Theviewverb is the default for the POC servlet.For example, accessing the following results in a
viewresolution.http://acme.com/wps/poc?uri=foo:bar
Accessing the following results in a
downloadresolution.http://acme.com/wps/poc?uri=foo:bar&verb=download
-
The parameter map contains additional parameters for the resolution; in a POC URL, these can be specified as query or POST parameters on the request.
-
The accepted bindings specify which sort of bindings are applicable to the resolution process, and will normally contain a binding to the HTTP method (GET or POST) that was used to invoke the resolution. This is needed for advanced use cases only.
-
The context is an opaque container that is used to transport internal information through the resolution process, which is needed to look up some portal API services, as you will see in the example.
The general pattern for a resolution service is as follows:
- Extract information from the URI and the parameters
- Update the
Resolvedobject to communicate the result of the resolution process
This design enables the combination of several resolution services in a "chain of responsibility" pattern. Each of the services is responsible for evaluating one aspect of the input and setting some properties of the output before passing control on to the next service. A typical example of such a chain is a separation between one resolver that selects the right target page -- typically containing a viewer for the requested resource -- and another resolver that configures a portlet on the page, the viewer, to display the desired content.
The URI resolution framework does not explicitly expose a request object; there are, in fact, use cases where the resolution process can be used outside the context of a servlet request (in an EJB, for example), but we do not consider those here.
Resolvers need to be registered with the Content Operations Registry (COR) component of the portal to become active. Registering a resolver involves two steps:
- First, associate a
ContentLocationwith an URI scheme by providing an appropriate factory. This content location typically does not implement any logic; it is only used as the glue between the URI and associated services. In this article, we only deal with resolution into portal navigational state; however, in general, there exists a 1:n relationship between the content location and services to handle it. Therefore, the content location decouples the services from the actual URI format. - In a second step, you associate one or more "services" with the
ContentLocationand, therefore, implicitly also with the URI. In the example in this article, we only deal with one such service, thecom.ibm.portal.resolver.ResolutionService.
In the example, the code for the necessary artifacts is located in the
DeeplinkContentLocationFactory class:
public class DeeplinkContentLocationFactory extends SingleContentLocationFactory
{
public static String SCHEME = "deeplink";
public static String LOCATION_TYPE_ID = "deeplink.LocationType";
public DeeplinkContentLocationFactory()
{
super(SCHEME, new ContentLocation());
}
private static class ContentLocation extends DefaultContentLocation {
public ContentLocation() {
super(LOCATION_TYPE_ID, SCHEME);
}
}
} |
You can see that there is not much to code because WebSphere Portal helper classes do most of the job. The only things that we need to define are the name of the URL scheme and an arbitrary internal ID for the content location type.
A content location factory locates a content location for a given URI. The SingleContentLocationFactory helper class handles the simple case of a content location factory that is just checks for a single URI scheme and returns a singleton content location if the URI matches the scheme. We create our own subclass, passing the scheme and the content location in the constructor.
The content location is the COR artifact that handles a URI. When you implement your own scheme in WebSphere Portal, though, you will not normally define any handler code here, because the COR interfaces do not give you easy access to portal-specific information. Instead, we define a local subclass of WebSphere Portal’s DefaultContentLocation with the scheme, and we delegate the handling of the URI to a portal ResolutionService.
You can regard the implementation of the content location factory as some boilerplate glue code that is necessary to plug into the URI resolution framework and does not normally need any application-specific logic. We do not reference our resolution service explicitly in the code; the link between a DefaultContentLocation and its associated resolution service is made declaratively.
The artifact that finally plugs the different pieces together is a plugin.xml descriptor for the WAS-integrated Eclipse plugin registry. The deeplink resolver is packages as a Web application WAR file to allow dynamic installation of the service; in addition, this makes it possible to bundle the resolver service with an administrative portlet. The plugin.xml can be found in deeplink.war/WEB-INF/plugin.xml:
<?xml version="1.0" encoding="UTF-8"?>
<plugin provider-name="IBM" version="1.0.0" name="DeeplinkPlugin" id="deeplink">
<extension point="com.ibm.content.operations.registry.locationTypeContribution">
<contentLocationType title="DeeplinkLocationType"
match.uri.scheme="deeplink"
class="deeplink.resolver.DeeplinkContentLocationFactory"
id="deeplink.LocationType"/>
</extension>
<extension point="com.ibm.content.operations.registry.locationServiceHandler">
<serviceHandler
class="deeplink.resolver.DeeplinkResolutionService"
locationTypeId="deeplink.LocationType"
id="com.ibm.portal.resolver.ResolutionService"/>
</extension>
</plugin>
|
The plugin descriptor defines a location type for the deeplink URL scheme and the resolution service for the URL format. The link between them is the location type ID. The different bits that need to match for a successful resolution process are as follows.
In the location type:
-
classis the class name of the content location factory that you have defined in your code. -
match.uri.schemeis the URI scheme and must match the scheme that is used in the super() call of the factory’s constructor (if it derives fromSingleContentLocationFactory). -
idis an arbitrary, but unique, ID.
In the resolution service:
-
locationTypeIdmust match the id attribute of the location type and in the super() call of the content location’s constructor (if it derives fromDefaultContentLocation). -
classis the class name of the resolution service that you have defined in your code. -
idmust have the constant valuecom.ibm.portal.resolver.ResolutionService, indicating that this service is handled by the portal resolution framework.
If you find that your resolution service does not get invoked as expected, the first thing to check is that the identifiers in the code and the plugin.xml line up correctly.
Let us quickly summarize the steps that take place when the framework tries to resolve an incoming URL, as shown in the Figure 5:
Figure 5. Resolving a URL
- The default entry point is the POC servlet with a URL in the format:
http://server/wps/poc?uri=pocURI&verb=view/download¶meters
The POC servlet extracts the POC URI and the parameters and passes it on to the COR component to find the appropriate service.
-
The COR searches all content location factories defined in the Eclipse registry to see if they can provide a matching content location for the POC URI, typically based on the URI scheme.
-
The matching content locations are invoked to handle the URI. These are typically subclasses of
DefaultContentLocation. -
DefaultContentLocationsearches the Eclipse registry forResolutionServiceswith a matching location type id and invokes them to handle the URL. -
Each resolution service checks whether it can handle the URI.
-
If it cannot, then the service returns
falseso that the next matching resolution service will be invoked instead. -
Otherwise, the service updates the
Resolvedobject with information extracted from the URI or the parameters. In the course of its processing, it might also dispatch (through the framework) to lower-level resolution services that process specific parts of the URI information.After a successful resolution the service returnstrue.
-
Displaying portal pages: Implementing the deep link service
You have now seen how the URL resolution framework works and how you can plug your own resolution service into the framework. Next, we investigate the deeplink resolution service to see an example of how a resolution service could look.
The implementation of the ResolutionService interface can be found in the
DeeplinkResolutionService class. Most URL resolvers that are running inside WebSphere Portal need access to one or more portal-specific services. The com.ibm.portal.resolver.helper.Services interface provides a convenience access to those portal services that are typically needed for a resolver implementation using Services.SINGLETON.
The resolve method of the deep link service is straightforward; it treats the whole scheme specific part of the content URI as a link name that is looked up in the configuration store.
linkname = uri.getSchemeSpecificPart(); link = DeeplinkConfigurationAccess.getConfiguration().getDeeplink(linkname); |
If a valid configuration for the link name is found, a DeeplinkResolver object is instantiated with the arguments to the resolve() call, and that object is then called to resolve the link.
We do not go into details on the implementation of the configuration store, because it is not related to the topic of this article. For our sample implementation, we use a simple file-based XML store that is read using the XML DOM API and kept completely in memory. The configuration deeplinkConfig.xml file is loaded using the classpath and located in the portal/shared/app directory for easy access. It can be refreshed by an explicit call which is triggered by the admin portlet.
For a production implementation, you would probably use a database instead so you have a single repository in a clustered environment. You could then efficiently load requested links and keep them in a cache instead of having to load the whole configuration at once. The configuration store is accessed through an interface layer (DeeplinkConfiguraion, DeeplinkData and PortletData) so that it can easily be replaced with a different implementation.
The configuration of a single link is described with a DeeplinkData instance for page data, which points to several PortletData objects that describe the desired state for portlets on the page. Within WebSphere Portal, all resources, such as pages and portlets, are identified by portal object IDs. Within the deeplink configuration XML, we cannot use these IDs to identify pages and portlets, because they are not human readable and are different for each installation; so we rely on unique names instead, as described in the introduction of the example scenario.
The DeeplinkResolver class is now responsible for mapping the link configuration to the portal resources. We need to extract information about portal data structures which are available in the public model APIs of WebSphere Portal. See the Resources section for links to detailed information about the portal model APIs.
In the resolveDeeplink method we need to check if the page described by the given page unique name does, in fact, exist and is visible to the current user. So, we use the portal ContentModel, which can be retrieved from the Services object described in the last section:
ContentModel contentModel = services.getContentModel(context); ContentNode page = (ContentNode) contentModel.getLocator().findByUniqueName(link.getPageUniqueName()); |
If successful, this code will give us a ContentNode object representing the target page defined by the unique name. (Actually, the target does not need to be a page; it could just be a label, such as Home in a default portal installation).
Next, we check to see if we need to set up state for portlets on the page. If that is the case, we must make sure that the ContentNode is a page and not just a label, and we need two more model APIs:
-
LayoutModel, which describes the layout of the page. -
PortletModel, (introduced with WebSphere Portal V6.0.1), which gives access to portlets that are placed on pages.
With the layout model, the
collectPortletsOnPage method can recursively traverse the layout elements of the page, collecting all the LayoutControl elements that represent portlets placed on the page. The, the portlet model drills down further to find out the portlet configuration and the unique names for the portlets.
We will not go deeper into the use of WebSphere Portal model APIs here, but you can find more details in the commented source code of the DeeplinkResolver class. For this discussion, you just need to know that you can correlate portlet unique names, which are referenced in the deep link configuration, to portlet window IDs, which are required to set the navigational state for a portlet that is displayed on a page.
We have now collected and validated all necessary information about the "piece of content" (the link) and its mapping to a portal view, that is, a page and portlets. The final step in writing a URL resolver is to use this information to set up the navigational state that should be displayed in the resulting portal page. For this, not surprisingly, you use the portal state API. For more information on the portal state API, see the Resources section.
Manipulating portal state starts with two basic interfaces:
- A
StateHolderControllerobject, which represents a given state that can be manipulated. For a URL resolution service, theStateHolderControlleris part of theResolvedparameter that the resolver uses to propagate its output. - A
StateManagerobject that gives access to accessors, which modify theStateHolderController. TheStateManagercan be retrieved from theServicesobject, similar to the portal models in the last section.
The pattern for using these interfaces is as follows.
- From the state manager, retrieve an accessor factory for a specific aspect of portal state (for example, the selected page).
- From the accessor factory, retrieve an accessor, passing in a
StateHolderController. - Use type-specific methods on the accessor to update the state.
- Finally, dispose of the accessor, making it available for re-use.
For example, the following code fragment from the DeeplinkResolver sets the target page for the deep link:
StateManagerService stateManager = services.getStateManager(context);
selAccFac = (SelectionAccessorFactory) stateManager.
getAccessorFactory(SelectionAccessorFactory.class);
…
SelectionAccessorController sac = selAccFac.getSelectionAccessorController(state);
sac.setSelection(page.getObjectID());
sac.dispose(); |
Setting navigational state for target portlets
If the requested deep link has associated configuration for portlets, we follow the same pattern to modify the display state for these portlets with a PortletAccessorController. This accessor lets you set the window state (normal, minimized, maximized) for the portlet. You can also use it to set render parameters that the portlet can use to determine its internal view state. If and how a portlet actually uses these render parameters is up to the programmer. For example, a document viewer portlet could use a currentDoc render parameter to determine the document and a page render parameter to determine the page that it displays.
Render parameters are only available in the Java portlet API (JSR 168). Portlets using the IBM Portlet API need to use the portlet session to track their internal state. Session data is not accessible outside the portlet and cannot, therefore, be configured by a URI resolver.
When you set render parameters for a portlet directly from the deep link resolver, as described in the last section, the parameter names and values must be included in the deeplink configuration.
For example:
<deeplink linkname="link1" page-uniquename="hr.news">
<portlet uniquename="company.news" state="maximized">
<parameter name="article">2007-08-04x39</parameter>
</portlet>
</deeplink> |
This implies that you know that the company news portlet uses a parameter named
article with a specific format; this information has therefore essentially been promoted from an implementation detail to an external API of the portlet. Portlets can declare this API in a deployment descriptor:
- Beginning with Version 5.1, WebSphere Portal supports collaborative portlets that declare their possible external inputs in a WSDL descriptor. The property broker shipping example used in our demonstration setup uses this method.
- The upcoming Version 2.0 of the Java Portlet Specification (JSR 286) extends the portlet deployment descriptor with public render parameters that are explicitly declared as external inputs.
Of course, the programmer must hard-code the use of certain render parameters, so that their format cannot be changed in further releases of the portlet. To get around this restriction, the portlet developer could add one more indirection and specify (of course) a URI format for addressing specific views or states of the portlet. This would be a typical use case for the "chaining" of URI resolvers that we mentioned earlier. The deeplink resolver selects the page and optionally maximizes the portlet, while another URI resolver provided by the portlet sets up the internal state of the portlet. Because this is an advanced use case, we do not go into details about the implementation of such a setup in this article.
We have finished our review of the deep link resolver implementation. We have so far only looked at the "good case". Of course there are a lot of ways that things could go wrong during the resolution. The provided URI could be invalid in a variety of ways. For example, the specified target page might not exist or it is not visible to the current user due to insufficient permissions.
The typical web behavior for an invalid URL is to send an HTTP error response to the browser, such as
HTTP error 404 "page not found". In a URI resolver, an error is provided by simply throwing a ResolutionException, which will cause the POC servlet to respond with an HTTP error. The default error code is 400 "bad request", but you can change it by overriding the getStatus() method of the ResolutionException.
In some environments, displaying an HTTP error in the browser may be considered too crude. An alternative that looks friendlier to users, is to have the URI resolver dispatch to a well-known error-handling page that contains an error display portlet. That way, users don’t feel like they have left your portal or that they have done something wrong. Within the error display portlet, you could, for example, provide Back and Home links that lead the user to a normal portal page.
This concludes our tour of the portal URI resolution infrastructure. You have seen how you can use the new APIs provided by WebSphere Portal 6.0.1 to implement your own well-defined URL format for linking to portal content from external sources. You saw a detailed coding example for implementing such a URI resolver.
The URI resolution infrastructure gives you a flexible and customizable framework to map from a stable URL to page and portlet state within the portal. Customizing the URI resolution lets you evolve URL formats and balance requirements for new features with stability of existing bookmarks. You can decide if additional mapping data (such as the deep link configuration) is required to resolve URIs into portal views, and you can control how that mapping data is stored and administered. Of course, this flexibility comes at the cost of extra development effort, so you should stick with the built-in portal URL format or the built-in URL mapping service if that gives you all the functionality that you require. But if you want full control of all behavior from URL resolution to error handling, the portal URI resolution framework gives you a powerful infrastructure to tailor everything to your needs.
| Description | Name | Size | Download method |
|---|---|---|---|
| Sample resolution service source code | deeplinkResolver-source.zip | 46 KB | HTTP |
| Sample configuration and installable portlets | uri-resolver-sample.zip | 26 KB | HTTP |
Information about download methods
- Participate in the discussion forum.
-
State handling concepts
-
Exploiting the WebSphere Portal 5.1.0.1 programming model: Part 2: Advanced URL generation
-
Custom unique names
-
The portal model APIs
-
Exploiting the WebSphere Portal 5.1.0.1 programming model: Part 4: Making your portal dynamic and context sensitive
-
The Eclipse extension registry in WebSphere Portal
-
Leveraging the WebSphere Portal V6 programming model article series
-
WebSphere Portal zone
-
WebSphere Portal product documentation

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.

Dr. Carsten Leue works as an architect within the WebSphere Portal team in the IBM Development Laboratory in Boeblingen, Germany. He has 6 years of experience in the software development field and holds a PhD in physics from the University of Heidelberg, Germany. He joined IBM in 2000 to apply his scientific background on image processing to address recognition for a postal solution. He moved to WebSphere Portal to work on the WSRP OASIS standard as one of the specification editors. After gaining deep insight into WebSphere Portal as a Chief Programmer, Carsten now focuses on the architecture of the Foundation in the areas of state handling and configuration management.




