Leveraging WebSphere Portal V6 programming model: Part 2. Advanced URL generation in themes and portlets

This article describes and shows how to use the latest additions to the URL generation capabilities for themes, skins, and portlets in IBM® WebSphere® Portal Version 6. With these new services, portlets can generate links to other portlets and pages for a broader variety of use cases than in previous versions. Portal and portlet developers learn when and how to use these new features.

Share:

Stefan Behl (stefan.behl@de.ibm.com), Software Engineer, IBM

Stefan Behl photoStefan Behl is a Software Engineer in the IBM Development Laboratory in Boeblingen, Germany. He joined the Workplace and Portal Foundation Development in 2003 and works in the Portal Engine team. His main areas of focus are navigational state handling and page aggregation. Stefan studied Software Engineering at the University of Stuttgart, Germany, and holds a diploma in Computer Science.



Stefan Hepper, WebSphere Portal Programming Model Architect, IBM

Stefan HepperStefan 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.


developerWorks Professional author
        level

Stefan Koch (stefkoch@de.ibm.com), Software Engineer, IBM

Stefan Koch photoStefan Koch is a Software Engineer in the IBM Development Laboratory in Boeblingen, Germany. As part of the WebSphere Portal Engine Team he is responsible for portal tags and URL generation issues. Stefan studied Information Engineering at the University of Applied Sciences Osnabrueck, Germany.



Daphna Steinbach (DAPHNA@de.ibm.com), Software Engineer, IBM

Daphna Steinbach photoDaphna Steinbach is a Software Engineer in the IBM Development Laboratory in Boeblingen, Germany. She joined the Workplace and Portal Foundation Development in 2005 and works in the Portal Engine team. Her main areas of focus are Search (crawlability enablement in Portal), navigational state handling, WCM, and JCR (teamspaces and test coverage). Daphna received a Diploma of Computer Science from the Fachhochschule of Karlsruhe.



27 December 2006

Introduction

In our article Exploiting the WebSphere Portal 5.1.0.1 programming model, Part 2: Advanced URL generation (listed in Resources) we provided an overview of how URL generation works in WebSphere Portal, and how you can programmatically create URLs in your themes and skins using the Navigational State SPI. The information in that paper is still accurate for WebSphere Portal V6.0 (see WebSphere Portal V6 product documentation). In this article, you learn about the additional features that have been added in WebSphere Portal V6.0 for generating URLs.

Why should you care about how to create URLs? Developers who create a monolithic Web application are in full control of every aspect of their application; they can create URLs in any fashion they prefer. In a portal environment, URL generation is different. The portal application developer provides a component that is combined with other components into a larger portal application. Therefore, the URLs can not be generated in an isolated manner; instead, developers need to create them using the mechanisms that are provided by the portal framework.

WebSphere Portal provides different APIs that you can use to create URLs that suit your particular use case. Some best practices for determining the appropriate URL generation API include the following.

  • If you want to communicate data to other portlets, consider first using the portlet coordination feature that enables sending events to other portlets using the Property Broker infrastructure (see Developing JSR 168 compliant cooperative portlets). This choice is preferable because it lets you build loosely coupled portlets that do not depend on each other or on specific pages.
  • If you want to create URLs pointing “back” to your portlet and to trigger an action or to set new render parameters, use PortletURL from the Java Portlet API (see the JSR 168 Portlet Specification).
  • If you want to create render URLs to other portlets or pages, or to set the portal into SOLO state, use the URL Generation API described below in the section Creating URLs in portlets the easy way: Using the URL Generation API. An example in this section illustrates how portlets can share information using portlet render parameters.
  • If you have more advanced use cases that cannot be addressed with the URL Generation API, use the Navigational State SPI which is described below in the section Implementing sophisticated use cases with the Navigational State SPI. This section is the focus of this article, and provides several useful code samples for a variety of use cases. For additional information about the Navigational State SPI, see the Navigational State SPI section in the WebSphere Portal V6 product documentation.

Navigational state represents the view of the portal that is associated with a particular client. The client can request (query) different views by interacting with the Web page (for example, by navigating to a new page). This interaction does not change the state on the server; instead, it requests a new view from the server. It is, therefore, a “safe” operation in terms of HTTP 1.1 (see Hypertext Transfer Protocol – HTTP/1.1). The client can navigate back and forward through recent views, can bookmark views, and can return to them by invoking a browser bookmark. This behavior is achieved by encoding the navigational state into the URL. Different navigational states result in different URLs.

Let’s start with the lightweight, easy URL Generation API, so that you can see how much you can do with this service.


Creating URLs in portlets the easy way, using the URL Generation API

WebSphere Portal V6 provides a very convenient and elegant API that JSR 168-compliant portlets can use to create URLs for the most common use cases. In particular, you can create render URLs to other portlets and pages. You can also create URLs that set the portal into SOLO state.

Solo state simulates a modal dialogue with the currently selected portlet. The user can only interact with that portlet; he or she cannot navigate to other pages or interact with other portlets. You can use SOLO state when you want the user to confirm a specific action, commit data with side effects on other portlets, or handle severe error situations.

Important: URL Generation API only lets you create render URLs because render URLs are safe; you can even send them around in email. Action URLs require security control; therefore, you can only create them using the Navigational State SPI discussed below. Render URLs should be associated with HTTP GET requests, whereas action URLs should be associated with HTTP POST requests. The URL Generatrion API is not meant for WSRP-based remote portlets.

Accessing the URL Generation API

The entry point to the URL Generation API is a portlet service called PortalURLGenerationService, which can be accessed using JNDI. For performance reasons, perform the JNDI lookup of the portlet service within the init() method of your portlet and cache the retrieved service in a portlet instance variable.

Listing 1. Accessing the API using PortalURLGenerationService
import com.ibm.portal.portlet.service.PortletServiceHome;
import com.ibm.portal.portlet.service.url.PortalURLGenerationService;
...
private static final String JNDI_NAME =
  "portletservice/com.ibm.portal.portlet.service.url.PortalURLGenerationService";
/**
 * Init method that looks up the PortalURLGenerationService using JNDI.
 * @see javax.portlet.GenericPortlet#init()
 */
public void init() throws PortletException {
    super.init();
    try {
        final javax.naming.Context ctx = new javax.naming.InitialContext();
        final PortletServiceHome psh = 
            (PortletServiceHome) ctx.lookup(JNDI_NAME);
        portalURLGenerationService = (PortalURLGenerationService) psh
            .getPortletService(PortalURLGenerationService.class);
    } catch (NamingException e) {
        throw new PortletException(e);
    }
}

From the service you can retrieve a PortalURLWriter by providing the portlet request and response. Typically, you use the PortalURLWriter in the doView() method of your portlet:

import com.ibm.portal.portlet.service.url.PortalURLWriter;
final PortalURLWriter portalUrlWriter = 
   ((PortalURLGenerationService)pshPortalURLGenerationService).
   getPortalURLWriter(request, response);

As implied by its name, PortalURLWriter writes portal URLs directly to any given output writer, which is very efficient and avoids unnecessary string object creations. Because there can be many URLs per portal page, this efficiency can save memory consumption and CPU processing time.

PortalURLWriter has two different kinds of methods:

  • Those that generate URLs to other content nodes, such as portal pages
  • Those that create URLs to portlets, either the current one or a different portlet

If you have use cases that cannot be addressed with PortalURLWriter methods, (for example, creating action URLs to other portlets), then you can use the more powerful Navigational State SPI (see Implementing sophisticated use cases with the Navigational State SPI).

Now let’s look at an example that shows the PortalURLGenerationService at work. This example assigns new render parameters to a specific portlet on another page.

Creating links from a portlet to a portlet on a different page

For some use cases you might need to Create a URL to a specific portlet on a specific page, and to pass some parameters to this portlet. When a user clicks on such a link, the portal displays the portal page that contains the target portlet and renders that portlet using the render parameters that have been set for the portlet.

In the following example, the News Selection portlet uses the PortalURLWriter to render a quicklink that triggers the display of the latest sports news in the News Display portlet. To implement this function, we set a render parameter which is understood by the News Display portlet, using a parameter name that is exposed through the interface description of the News Display portlet. The News Display portlet is addressed using the unique name of the portlet window which you can set through the XML interface of portal. The News page is also addressed using a page unique name.

For the sake of simplicity the following code omits the News Selection portlet init() method which is responsible for looking up the PortalURLGenerationService using JNDI (described in Accessing the URL Generation API).

Listing 2. Using a render parameter to pass information to a second portlet
import com.ibm.portal.portlet.service.url.PortalURLGenerationService;
import com.ibm.portal.portlet.service.url.PortalURLWriter;
...

/**
 * Simplified version of a News Selection portlet.
 */
public class NewsSelectionPortlet extends GenericPortlet {

    /**
     * Name of the render parameter that is understood by the News Display
     * portlet to display a certain news article.
     */
    private static final String DISPLAY_ARTICLE_PARAM = "da";

    /**
     * ID of the news article that summarizes the latest sports news.
     */
    private static final String LATEST_SPORTS_NEWS_ID = "124";

    /**
     * The portlet service that provides access to the PortalURLWriter.
     * Variable is initialized in the init() method of the portlet.
     */
    protected PortalURLGenerationService portalURLGenerationService;
    
    /**
     * Creates a render URL that points to the News Display portlet on the
     * News page. Sets a render parameter of the News Display portlet that
     * specifies a specific news article using an ID. Note that the render
     * parameters of the News Display portlet are completely replaced with
     * the parameter map that is passed to the writePortletRenderURL 
     * method. The render parameters of all other portlets and the portal 
     * state is preserved.
     */
    public void doView(RenderRequest request, RenderResponse response) 
        throws PortletException, IOException {
        // set the content type
        response.setContentType(request.getResponseContentType());
        // get the response writer
        final PrintWriter writer = response.getWriter();
        // get the PortalURLWriter
        final PortalURLWriter portalURLWriter = 
            portalURLGenerationService.getPortalURLWriter(
                request, response);
        // set the ID of the news article as a render parameter of the News
        // Display portlet
        final Map params = new HashMap();
        params.put(DISPLAY_ARTICLE_PARAM,
                   new String[] { LATEST_SPORTS_NEWS_ID });
        // write the render URL to the markup
        writer.print("<a href=\"");
        portalURLWriter.writePortletRenderURL(
                writer, 
                "ibm.portal.Home.NewsPage",
                "ibm.portal.Home.NewsPage.NewsDisplayPortlet",
                params);
        writer.print("\">");
        writer.print("Show latest sports news");
        writer.print("</a>");
    }

The following code sample shows how the News Display portlet referenced in our example above can read the render parameter that was set by the News Selection portlet. Using the Java Portlet API (see the JSR 168 Portlet Specification) the display portlet can easily access the render parameter using the render request. Render parameters do not need to be namespaced, they should always be as short as possible.

Listing 3. Display portlet accessing the render parameter through the Portlet API
/**
 * Simplified version of a News Display portlet. Portlet does not require
 * the PortalURLWriter but is based on the Portlet API only.
 */
public class NewsDisplayPortlet extends GenericPortlet {

    /**
     * Name of the render parameter that specifies the ID of the
     * news article that should be displayed.
     */
    private static final String ARTICLE_PARAMETER = "da";

    /**
     * Simplified doView which just displays the article ID of the selected
     * news article.
     */
    public void doView(RenderRequest request, RenderResponse response)
        throws PortletException, IOException {
        // set the content type
        response.setContentType(request.getResponseContentType());
        // get the response writer
        final PrintWriter writer = response.getWriter();
        // get the render parameter specifying the article ID 
        final String[] article = 
            request.getParameterValues(ARTICLE_PARAMETER);
        // display the ID if the render parameter is available
        if (article != null && article.length > 0) {
            final String articleID = article[0];
            writer.print("<div>ID of the selected news article: ");
            writer.print(articleID);
            writer.print("</div>");
        } else {
            writer.println("<div>No news article selected!</div>");
        }
    }

Implementing sophisticated use cases with the Navigational State SPI

For advanced use cases that cannot be implemented using the URLGeneration API, you can use the Navigational State SPI. Initial support for the Navigational State SPI, focusing on the creation of URLs in themes and skins, was introduced with WebSphere Portal 5.1 (see the article Exploiting the WebSphere Portal 5.1.0.1 programming model, Part 2: Advanced URL generation). WebSphere Portal 6.0 extends the SPI with an additional service that can be used in portlets.

Now the SPI offers two services to create URLs:

  • The Portal State Manager Service is a portal service which lets you create URLs within portal artifacts such as themes and skins (for example in custom JSP tags); it extends the capabilities of the tag API. However, it is not meant to be used by portlets.
  • The Portlet State Manager Service is the counterpart service supporting JSR 168 portlets. You use the Portlet State Manager Service to create URLs within portlets that cannot be created using the Java Portlet API (see the JSR 168 Portlet Specification) or the URL Generation API described in the previous section.
    Important: Portlets using Portlet State Manager Service cannot be provided as remote portlets using WSRP.

In WebSphere Portal V6.0, both services are derived from a common service interface and are based on the same object models; that is, you can write your code in a way that lets you run that code in both environments (themes /skins and portlets). Only the concrete service lookup is different.

The com.ibm.portal.state package is the main package of the Navigational State SPI holding the service interfaces, as well as the interfaces making up the navigational state object model. For more information about all SPI interfaces, download the Javadoc for WebSphere Portal V6 APIs and SPIs.

Object model

This section describes the main object models used in the Navigational State SPI. The object models are common to both URL generation services.

Navigational state

Navigational state is modeled as a DOM-like document containing untyped state information represented as strings. The string-based representation enables efficient serialization of navigational states into URLs because it avoids processing time and CPU consuming object-to-string conversions during the serialization process.

You can access and manipulate the navigational state by implementing these interfaces:

com.ibm.portal.state.StateHolder

The StateHolder interface provides read-only access to navigational state information by exposing a getModel() method, which returns an interface to the untyped document model, that is modeled through the DocumentModel interface. The DocumentModel interface provides a set of methods to inspect both the hierarchical structure of the state document and single nodes in the document.

com.ibm.portal.state.StateHolderController

The StateHolderController interface lets you modify state information using a getController() method, which delivers a controller interface to the untyped state document called DocumentController. The DocumentController interface lets you modify the hierarchical structure of the state document, which includes creating document nodes, inserting them into the node hierarchy, and removing nodes.

Engine URLs

URLs are modeled using the com.ibm.portal.state.EngineURL interface, which represents an URL that contains navigational state. You need to specify the initial StateHolder that is referenced by an EngineURL, when requesting a new EngineURL instance from the appropriate URL factory (see Creating URLs using generation services). Typically it is a copy of the request-specific base state.

The crucial method of the EngineURL interface is the getState() method, which returns a controller interface (the StateHolderController interface) to the navigational state referenced by this particular EngineURL instance. Therefore, the state that is associated with the created URL can be easily modified using the Accessor SPI.

In addition, the EngineURL provides several write methods which you can use to directly stream the URL to the HTTP response.

Resource URLs

URLs that address generic resources such as files, icons, and voice grammars cannot contain navigational state, and are modeled using a separate interface called com.ibm.portal.state.DisposableURL. The EngineURL interface, discussed above, extends the DisposableURL interface to add the getState() method.

You can also create resource URLs using the URL generation services that are offered along with the Navigational State SPI. See Creating URLs using generation services.

Accessor SPI

The Accessor SPI is a convenience SPI that provides typed access to the state document model. You can use the SPI to easily query and modify navigational state information. The Accessor SPI is part of the package com.ibm.portal.state.accessors.*.

The Accessor SPI is an abstraction layer that hides the access to particular nodes in the hierarchical document model. For each state aspect (such as page selection, expansion states, or portlet states), the SPI offers a corresponding accessor factory that provides read and write accessors. Internally, the accessors read from or write to the respective positions in the state document model and perform the required type conversions.

The Navigational State SPI offers the accessor factories listed in the following table. Each accessor factory is responsible for a certain state aspect. As specified in the last two table columns, some accessor factories are restricted to the Portal State Manager Service; that is they cannot be used in a portlet. Requesting these factories via the Portlet State Manager Service will result in an exception.

Table 1. Accessory factories provided by the Navigational State SPI
Accessor FactoryDescriptionPortal ServicePortlet Service
SelectionAccessorFactoryProvides accessors to read and write portal page selection information. Use the SelectionAccessorController to create a URL to another portal page.yesyes
PortletAccessorFactoryProvides accessors to read and write portlet-related navigational state information, which includes portlet mode, window state, and render parameters. Use the PortletAccessorController to create URLs that change the navigational state of a portlet (for example, the portlet mode).yesyes
PortletTargetAccessorFactoryProvides accessors to write portlet action-related information. Use the PortletAccessorController to declare a portlet as the target of an action.yesyes
SoloAccessorFactoryProvides accessors to read and write the so-called Solo state. If the portal is in Solo state, it renders only one particular portlet of the current portal page; all navigation controls and tool bars are hidden. Use the SoloAccessorController to create URLs that activate/deactivate the Solo state for a particular portlet.yesyes
ThemeTemplateAccessorFactoryProvides accessors to read and write theme template information. Use the ThemeTemplateAccessorController to create URLs that change the theme template.yesyes
LocaleAccessorFactoryProvides accessors to read and write locale information. Use the LocaleAccessorController to create URLs that change the locale. Note that a locale retrieved from such a URL takes precedence over user preferred locales or locales defined on the user’s browser.yesyes
ExpansionStatesAccessorFactoryProvides accessors to read and write expansion states (specify whether a certain navigation node is expanded or collapsed). Use the ExpansionStatesAccessorController to generate URLs that toggle the expansion state of a navigation node.yesno
ShowToolsAccessorFactoryProvides accessors to read and write tool-related information. Use the ShowToolsAccessorController to create a URL that blends in the tool icons for portlet windows (offering functions such as moving/deleting the respective portlet).yesno
StatePartitionAccessorFactoryProvides accessors to read and write state partition identifiers. Use the StatePartitionAccessorController to include a state partition identifier into a URL. State partition identifiers should be included into URLs that open new browser windows or iFrames.yesno
EngineActionAccessorFactoryProvides accessors to write portal actions. Use the EngineActionAccessorController to create URLs that trigger certain portal actions. The controller also allows to set action parameters.yesno

Creating URLS using generation services

This section describes the services you can use to create URLs in the Navigational State SPI. The Navigational State SPI offers the following two services that are both accessible through the Java Naming and Directory Interface (JNDI):

  • The com.ibm.portal.state.service.PortalStateManagerService is a portal service which you can use in non-portlet artifacts such as themes and skins. You can access this portal service using the JNDI name “portal:service/state/PortalStateManager”. The JNDI lookup returns a PortalStateManagerServiceHome interface which is valid for the lifetime of the portal. Store it in an instance variable or static variable.
  • The com.ibm.portal.portlet.service.PortletStateManagerService is a portlet service which can be used in JSR168 compliant portlets. You can access this portlet service using the JNDI name "portletservice/com.ibm.portal.state.service.PortletStateManagerService". The JNDI lookup returns a generic PortletServiceHome interface which offers a getPortletService(Class) method to get the PortletStateManagerService. The retrieved PortletStateManagerService instance is valid for the lifetime of the portal. Perform the service retrieval in the init() method of the portlet and store the obtained service instance in a portlet instance variable.

Both services use a common interface called com.ibm.portal.state.service.StateManagerService which provides the functions that are common to both services. You can reuse URL generation code that was originally written for themes and skins in portlets if it is based on that common interface.

The PortalStateManagerServiceHome interface

The PortalStateManagerServiceHome interface provides the following two getters to retrieve the service:

PortalStateManagerService getPortalStateManagerService(HttpServletRequest request, HttpServletResponse response)

Returns a service for creating URLs in themes, skins, and custom JSP tags. All server-related information that is needed to generate the URLs (such as hostname, protocol, server port, context path, and so on) is derived from the given servlet request. It also uses configuration data from ConfigService.properties that enables a user to override server related settings (see the Portal configuration services section in the WebSphere Portal product documentation).

PortalStateManagerService getPortalStateManagerService(ServerContext ctx, Locale locale, Profile profile,boolean isProtected, boolean isSecure)

Returns a service that you can use for “offline” use cases such as creating URLs in environments where the servlet request is not available (for example, in an Enterprise JavaBean). The artifact that uses this kind of service must run in the same JVM as the portal that provides the service. Using this method requires that you explicitly pass in the needed contextual information, which you can normally retrieve from the current request.

Use the ServerContext argument to provide the needed server-related information such as hostname, protocol, server port, and context path. Use the isProtected argument to indicate the protected portal area, and isSecure to indicate a secure context. Furthermore, you must provide the current locale and client profile in order to enable the service to create URLs that address locale-specific resources such as language-dependent icons.

The lifetime of a PortalStateManagerService object depends on whether you requested a request-specific service or an offline service. The request-specific service has request scope; that is, it can only be used for the duration of one servlet request. For all subsequent servlet requests you need to request a new service instance from the home interface. The lifetime of the offline PortalStateManagerService corresponds with the lifetime of the ServerContext object.

The PortletStateManagerService interface

You can use the portlet-specific service within the render method of your portlet (or in the helper methods serving the mandatory portlet modes such as doView, doEdit, and doHelp) to include URLs into the markup. You can also use this service in the processAction method to send a redirect to a certain URL.

The PortletStateManagerService interface exposes the following two methods:

PortletStateManager getPortletStateManager(PortletRequest request, PortletResponse response)

Returns a PortletStateManager object. You can use this object during action processing and rendering (for example in the processAction method, doView method, doEdit method, and so on.). The PortletStateManager interface adds additional methods to extend the generic StateManagerService interface, which, for example, lets you directly read the current request-specific navigational state of the portlet (portlet mode, window state, and render parameters).

PortletStateManagerController getPortletStateManagerController(ActionRequest request, Action response)

Returns a PortletStateManagerController that extends the PortletStateManager interface. PortletStateManagerController provides additional methods that enable modifying the current navigational state of the portlet and is, therefore, only accessible during action processing (for example, in the processAction method of the portlet).

Both the PortletStateManager and the PortletStateManagerController have request scope, which means that you must not store references to them across requests. Instead, you must retrieve the service from the PortletServiceHome object. To indicate that the retrieved PortletStateManager or PortletStateManagerController instance is no longer accessed in the scope of a request, invoke the dispose method inherited from the Disposable interface on it.

The base interface StateManagerService

Beginning with WebSphere Portal V6.0, the PortletStateManager and the PortalStateManagerService are derived from the com.ibm.portal.state.service.StateManagerService interface which offers functionality that is common to both URL generation services. The use cases that are common to both services refer to the creation of EngineURLs that carry navigational state and resource URLs. Therefore, the StateManagerServiceinterface should be sufficient to implement most of the use cases. The interface provides the following two methods:

URLFactory getURLFactory()

Returns a com.ibm.portal.state.URLFactory object with several methods to create a variety of URLs (EngineURLs as well as resource URLs). Refer to the Javadoc for WebSphere Portal V6 APIs and SPIs for details about the offered methods.

AccessorFactory getAccessorFactory(Class factoryType)

Provides access to the various accessor factories. This method requires one argument that specifies the type of the needed accessor factory.When you use the portlet service, the set of accessor factories is restricted (see Table 1).

A typical usage pattern involves these steps:

  1. Get a new EngineURL object from the URLFactory using one of the newURL methods.
  2. Modify the navigational state of the URL using the Accessor SPI. Use the getAccessorFactory method to get the needed accessor factories for the state aspects that should be modified.
  3. Write the created URL to the markup using either the writeDispose (preferred) or writeCopy method in the EngineURL interface.

Code samples

This section provides ten code samples that illustrate how to achieve the most common use cases. Samples 1 and 2 show how you can retrieve the two URL generation services described in the previous sections whereas all other URL generation code samples abstract from the concrete service so that you can reuse the code in the environment (portal or portlet) of your choice.

The code samples typically use com.ibm.portal.ObjectIDs to identify resources. However, you can also use unique names to address these resources. The respective accessor controllers provide additional methods that accept strings representing unique names as well.

The code samples do not contain any Java import statements. These Java imports cover all the portal-related classes and interfaces that are used:

import com.ibm.portal.ObjectID;
import com.ibm.portal.portlet.service.PortletServiceHome;
import com.ibm.portal.state.EngineURL;
import com.ibm.portal.state.PortletStateManager;
import com.ibm.portal.state.URLFactory;
import com.ibm.portal.state.accessors.locale.LocaleAccessorController;
import com.ibm.portal.state.accessors.locale.LocaleAccessorFactory;
import com.ibm.portal.state.accessors.portlet.PortletAccessorController;
import com.ibm.portal.state.accessors.portlet.PortletAccessorFactory;
import com.ibm.portal.state.accessors.portlet.PortletTargetAccessorController;
import com.ibm.portal.state.accessors.portlet.PortletTargetAccessorFactory;
import com.ibm.portal.state.accessors.selection.SelectionAccessorController;
import com.ibm.portal.state.accessors.selection.SelectionAccessorFactory;
import com.ibm.portal.state.accessors.statepartition.StatePartitionAccessorController;
import com.ibm.portal.state.accessors.statepartition.StatePartitionAccessorFactory;
import com.ibm.portal.state.accessors.themetemplate.ThemeTemplateAccessorController;
import com.ibm.portal.state.accessors.themetemplate.ThemeTemplateAccessorFactory;
import com.ibm.portal.state.accessors.url.ServerContext;
import com.ibm.portal.state.exceptions.StateException;
import com.ibm.portal.state.service.PortalStateManagerServiceHome;
import com.ibm.portal.state.service.PortletStateManagerService;
import com.ibm.portal.state.service.StateManagerService;

Sample 1: Access PortalStateManagerService

This sample shows how you can get access to the PortalStateManagerService to create URLs in themes, skins, custom JSP tags, or other portal-level Java artifacts. For performance reasons, perform the JNDI lookup only once; however, the service must be requested for each servlet request.

/** the JNDI name to retrieve the PortalStateManagerServiceHome object */
private static final String JNDI_NAME = 
  "portal:service/state/PortalStateManager";

/** PortalStateManagerServiceHome object to retrieve the service from */
private static PortalStateManagerServiceHome serviceHome;

public void useRequestService(final HttpServletRequest request,
            final HttpServletResponse response) {
  try {
    // get the request-specific service from our home interface
    final StateManagerService service
      = getServiceHome().getPortalStateManagerService(request, response);
    // use the service
    // ...
    // indicate that we do not need it any longer
    service.dispose();
  } catch (Exception e) {
    // error handling
  }
}

public void useOfflineService(final ServerContext ctx,
             final Locale locale,
             final Profile profile,
             final boolean prot,
	         final boolean secure) {
  try {
    // get the offline service from our home interface
    final StateManagerService service = getServiceHome()
        .getPortalStateManagerService(ctx, locale, profile, prot, secure);
    // use the service
    // ...
    // indicate that we do not need it any longer
    service.dispose();
  } catch (Exception e) {
    // error handling
  }
}

/**
 * Looks up the PortalStateManagerServiceHome being valid
 * for the lifetime of the portal.
 */
private synchronized static PortalStateManagerServiceHome getServiceHome() {
  if (serviceHome == null) {
    try {
      final Context ctx = new InitialContext();
      serviceHome =
        (PortalStateManagerServiceHome) ctx.lookup(JNDI_NAME);
    } catch (Exception e) {
      // error handling
    }
  }
  return serviceHome;
}

Sample 2: Access PortletStateManagerService

This sample shows how you can get access to the PortletStateManagerService to create EngineURLs in JSR 168 compliant portlets. For performance reasons, you should perform the JNDI lookup in the init() method of your portlet. The PortletStateManager must, however, be requested for each RenderRequest.

public class MyPortlet extends GenericPortlet {

  /** The JNDI name which is needed to lookup the service */
  private static final String JNDI_NAME =
"portletservice/com.ibm.portal.state.service.PortletStateManagerService";

  /** portlet state manager service */
  protected PortletStateManagerService service;

  /**
   * @see javax.portlet.GenericPortlet#init()
   */
  public void init() throws PortletException {
    super.init();
    try {
      // lookup the portlet state manager service
      final Context ctx = new InitialContext();
      final PortletServiceHome serviceHome = (PortletServiceHome) 
        ctx.lookup(JNDI_NAME);
      service = (PortletStateManagerService) 
        serviceHome.getPortletService(PortletStateManagerService.class);
    } catch (NamingException e) {
      throw new PortletException(e);
    }
  }

 
  /**
   * @see javax.portlet.GenericPortlet#doView(RenderRequest,RenderResponse)
   */
  protected void doView(
    final RenderRequest request, final RenderResponse response)
    throws PortletException, IOException {
    response.setContentType(request.getResponseContentType());
    final PrintWriter writer = response.getWriter();
    try {
      // get the request-specific portlet state manager
      final PortletStateManager mgr = service.
        getPortletStateManager(request, response);
      // do something (create URLs etc.)
      // ...
      // indicate that we do not need the portlet state manager any longer
      mgr.dispose();
    } catch (StateException e) {
      throw new PortletException(e);
    }
  }

Sample 3: Create a URL that navigates to a portal page

This code sample shows how you can create an EngineURL that points to a certain portal page. The page is identified by an ObjectID that is encoded into the navigational state using the SelectionAccessorController.

This code is based on the generic StateManagerService interface which can be used in both the portal and portlet environments.

public EngineURL createPageURL(final StateManagerService service,
    final ObjectID pageID) throws StateException {
    // get the needed factories
    final URLFactory urlFactory = service.getURLFactory();
    final SelectionAccessorFactory selFct = (SelectionAccessorFactory) 
        service.getAccessorFactory(SelectionAccessorFactory.class);
    try {
        // get a new URL from the URL factory
        final EngineURL url = urlFactory.newURL(null);
        // get a selection controller which operates on the
        // URL-specific state
        final SelectionAccessorController selCtrl = 
            selFct.getSelectionAccessorController(url.getState());
        try {
            // change the selection
            selCtrl.setSelection(pageID);
            // return the URL
            return url;
        } finally {
            selCtrl.dispose();
        }
    } finally {
        urlFactory.dispose();
    }
}

Sample 4: Create a URL that navigates to a portal page and displays that page in a specified language

This code sample shows how you can create an EngineURL that points to a certain portal page and displays that page using a certain java.util.Locale. The page is identified by an ObjectID that is encoded into the navigational state using the SelectionAccessorController. The locale is encoded using the LocaleAccessorController.

This code is based on the generic StateManagerService interface which can be used in the both the portal and portlet environments.

public EngineURL createLocalizedPageURL(final StateManagerService service, 
    final ObjectID pageID, final Locale locale) throws StateException {
    // get the needed factories
    final URLFactory urlFactory = service.getURLFactory();
    final SelectionAccessorFactory selFct = (SelectionAccessorFactory) 
        service.getAccessorFactory(SelectionAccessorFactory.class);
    final LocaleAccessorFactory locFct = (LocaleAccessorFactory) 
        service.getAccessorFactory(LocaleAccessorFactory.class);
    try {
        // get a new URL from the URL factory
        final EngineURL url = urlFactory.newURL(null);
        // get a selection controller which operates on the
        // URL-specific state
        final SelectionAccessorController selCtrl = 
            selFct.getSelectionAccessorController(url.getState());
        // get a locale controller which operates on the URL-specific state
        final LocaleAccessorController locCtrl = 
            locFct.getLocaleAccessorController(url.getState());
        try {
            // change the selection
            selCtrl.setSelection(pageID);
            // change the locale
            locCtrl.setLocale(locale);
            // return the URL
            return url;
        } finally {
            selCtrl.dispose();
            locCtrl.dispose();
        }
    } finally {
        urlFactory.dispose();
    }
}

Sample 5: Create a URL that changes the language

This code sample shows how you can create an EngineURL that changes the locale in use, without regard to the selected portal page. The locale is encoded using the LocaleAccessorController.

This code is based on the generic StateManagerService interface which can be used in the both the portal and portlet environments.

public EngineURL createLocaleURL(final StateManagerService service,
    final Locale locale) throws StateException {
    // get the needed factories
    final URLFactory urlFactory = service.getURLFactory();
    final LocaleAccessorFactory locFct = (LocaleAccessorFactory) 
        service.getAccessorFactory(LocaleAccessorFactory.class);
    try {
        // get a new URL from the URL factory
        final EngineURL url = urlFactory.newURL(null);
        // get a locale controller which operates on the URL-specific state
        final LocaleAccessorController locCtrl = 
            locFct.getLocaleAccessorController(url.getState());
        try {
            // change the locale
            locCtrl.setLocale(locale);
            // return the URL
            return url;
        } finally {
            locCtrl.dispose();
        }
    } finally {
        urlFactory.dispose();
    }
}

Sample 6: Create a URL that navigates to a portal page and displays that page using a special theme template

This code sample shows how you can create an EngineURL that points to a certain portal page and displays that page using a special theme template. A theme template corresponds to the JSP of a theme that the portal uses when it renders a portal page.

The page is identified by an ObjectID that is encoded into the navigational state using the SelectionAccessorController. The theme template that should be used for rendering is encoded using the ThemeTemplateAccessorController.

This code is based on the generic StateManagerService interface which can be used in the both the portal and portlet environments.

public EngineURL createThemeTemplateURL(
    final StateManagerService service,
    final ObjectID pageID,
    final String themeTemplate) throws StateException {
    // get the needed factories
    final URLFactory urlFactory = service.getURLFactory();
    final ThemeTemplateAccessorFactory tAccFct = 
        (ThemeTemplateAccessorFactory) service                   
            .getAccessorFactory(ThemeTemplateAccessorFactory.class);
    final SelectionAccessorFactory sAccFct = 
        (SelectionAccessorFactory) service                          
            .getAccessorFactory(SelectionAccessorFactory.class);
    try {
        // get a new URL from the URL factory
        final EngineURL url = urlFactory.newURL(null);
        // get a theme template controller which operates on the
        // URL-specific state
        final ThemeTemplateAccessorController tCtrl =
            tAccFct.getThemeTemplateAccessorController(url.getState());
        // get a selection controller which operates on the
        // URL-specific state
        final SelectionAccessorController sCtrl =                         
            sAccFct.getSelectionAccessorController(url.getState());
        try {
            // change the theme template
            tCtrl.setThemeTemplate(themeTemplate);
            // change the page selection
            sCtrl.setSelection(pageID);
            // return the URL
            return url;
        } finally {
            tCtrl.dispose();
            sCtrl.dispose();
        }
    } finally {
        urlFactory.dispose();
    }
}

Sample 7: Create a URL that changes the render parameters of a portlet on a specified portal page

The following code sample shows how you can create an EngineURL that navigates to a certain portal page and changes one or more render parameters of a particular portlet window on that page. To create such a URL, you must encode the ID of your target page using the SelectionAccessorController. T set the render parameters, you can use the PortletAccessorController.

This code is based on the generic StateManagerService interface which can be used in the both the portal and portlet environments.

public EngineURL createRenderParametersURL(
    final StateManagerService service, final ObjectID pageID,
    final ObjectID portletWindowID, final Map renderParams)
    throws StateException {        
    // get the needed factories
    final URLFactory urlFactory = service.getURLFactory();
    final PortletAccessorFactory pAccFct =
        (PortletAccessorFactory) service
            .getAccessorFactory(PortletAccessorFactory.class);
    final SelectionAccessorFactory sAccFct =
        (SelectionAccessorFactory) service
            .getAccessorFactory(SelectionAccessorFactory.class);
    try {
        // get a new URL from the URL factory
        // null indicates that the current state should be copied
        final EngineURL url = urlFactory.newURL(null);
        // get a selection controller which operates on the
        // URL-specific state
        final SelectionAccessorController sCtrl = 
            selFct.getSelectionAccessorController(url.getState());
        // get a portlet controller that operates on the URL-specific state
        final PortletAccessorController pCtrl = pAccFct
            .getPortletAccessorController(portletWindowID, url.getState());
        try {
            // set the page selection
            sCtrl.setSelection(pageID);
            // set the render parameters for the portlet window
            pCtrl.getParameters().putAll(renderParams);
        } finally {
            pCtrl.dispose();
            sCtrl.dispose();
        }
        return url;
    } finally {
        urlFactory.dispose();
    }
}

Sample 8: Create a URL that triggers a portlet action

The following code sample shows how you can create an EngineURL that triggers the action phase of a portlet window. To create such an URL, you must first declare the portlet window as an action target using the PortletTargetAccessorController and then set the action parameters using the PortletAccessorController.

This code is based on the generic StateManagerService interface which can be used in the both the portal and portlet environments.

public EngineURL createPortletActionURL(
    final StateManagerService service, final ObjectID portletWindowID,
    final Map actionParams) throws StateException {
    // get the needed factories
    final URLFactory urlFactory = service.getURLFactory();
    final PortletTargetAccessorFactory tAccFct = 
        (PortletTargetAccessorFactory) service
           .getAccessorFactory(PortletTargetAccessorFactory.class);
    final PortletAccessorFactory pAccFct =
        (PortletAccessorFactory) service
            .getAccessorFactory(PortletAccessorFactory.class);
    try {
        // get a new URL from the URL factory
        // null indicates that the current state should be copied
        final EngineURL url = urlFactory.newURL(null);
        // get a target controller that operates on the URL-specific state
        final PortletTargetAccessorController tCtrl = tAccFct
            .getPortletTargetAccessorController(url.getState());
        // get a portlet controller that operates on the URL-specific state
        final PortletAccessorController pCtrl = 
           pAccFct.getPortletAccessorController(
               portletWindowID, url.getState());
            try {
                // set the action target
                tCtrl.setActionTarget(portletWindowID);
                // set the action parameters
                pCtrl.getParameters().putAll(actionParams);
            } finally {
                tCtrl.dispose();
                pCtrl.dispose();
            }
            return url;
        } finally {
            urlFactory.dispose();
        }
    }

Sample 9: Create a URL that changes the portlet mode or window state of a portlet

The following code sample shows how you can create an EngineURL that changes the portlet mode or window state of a portlet window. To create such a URL, you can use the PortletAccessorController which lets you change the portlet mode and window state.

This code is based on the generic StateManagerService interface which can be used in the both the portal and portlet environments.

public EngineURL createPortletModeWindowStateURL(
    final StateManagerService service, final ObjectID portletWindowID,
    final WindowState windowState, final PortletMode portletMode)
    throws StateException {
    // get the needed factories
    final URLFactory urlFactory = service.getURLFactory();
    final PortletAccessorFactory pAccFct =
        (PortletAccessorFactory) service
            .getAccessorFactory(PortletAccessorFactory.class);
    try {
        // get a new URL from the URL factory
        // null indicates that the current state should be copied
        final EngineURL url = urlFactory.newURL(null);
        // get a portlet controller that operates on the URL-specific state
        final PortletAccessorController pCtrl = pAccFct
            .getPortletAccessorController(portletWindowID, url.getState());
        try {
            // set the portlet mode
            if (portletMode != null) {
                pCtrl.setPortletMode(portletMode);
            }
            // set the window state
            if (windowState != null) {
                pCtrl.setWindowState(windowState);
            }
        } finally {
            pCtrl.dispose();
        }
        return url;
    } finally {
        urlFactory.dispose();
    }
}

Sample 10: Create a URL that opens a new state partition for popup windows or iframes

This code sample shows how you can create an EngineURL that creates a new state parititon. You can use state partition URLs to open browser popup windows (for example, in combination with JavaScript) or for iframes. To create such a URL, you must include a state partition ID into the navigational state using the StatePartitionAccessorController.

The StatePartitionAccessorFactory is only available through the PortalStateManagerService; that is, you cannot use it within portlets.

public EngineURL createStatePartitionURL(
    final StateManagerService service, final ObjectID pageID)
    throws StateException {
    // get the needed factories
    final URLFactory urlFactory = service.getURLFactory();
    final StatePartitionAccessorFactory partFct = 
        (StatePartitionAccessorFactory) service
            .getAccessorFactory(StatePartitionAccessorFactory.class);
    final SelectionAccessorFactory selFct =
        (SelectionAccessorFactory) service
            .getAccessorFactory(SelectionAccessorFactory.class);
    try {
        // get a new URL from the URL factory
        // null indicates that the current state should be copied
        final EngineURL url = urlFactory.newURL(null);
        // get a state partition controller that operates on the
        // URL-specific state
        final StatePartitionAccessorController partCtrl = partFct
            .getStatePartitionAccessorController(url.getState());
        // get a selection controller that operates on the
        // URL-specific state
        final SelectionAccessorController selCtrl = 
            selFct.getSelectionAccessorController(url.getState());
        try {
            // include a state partition
            partCtrl.includeStatePartitionId();
            // set the new page selection
            selCtrl.setSelection(pageID);
        } finally {
            partCtrl.dispose();
            selCtrl.dispose();
        }
        return url;
    } finally {
       urlFactory.dispose();
    }
}

Conclusion

This article discussed and showed you how to use the new additions to the URL generation APIs in WebSphere Portal V6.0. WebSphere Portal V6.0 provides a simplified API, the URLGeneration API, that lets you easily create URLs pointing to other portlets and pages. You saw that extensions to the state SPI now enable you to create links to change the current language or to select a specific theme. Finally, you saw that in WebSphere Portal V6.0 you can now leverage these URL generation APIs and SPIs not only from themes and skins, but also from within portlets.

Resources

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

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

 


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

All information submitted is secure.

Choose your display name



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

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

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

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

 


All information submitted is secure.

Dig deeper into WebSphere on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=WebSphere
ArticleID=185910
ArticleTitle=Leveraging WebSphere Portal V6 programming model: Part 2. Advanced URL generation in themes and portlets
publish-date=12272006