+

Search Tips   |   Advanced Search


URL generation services

Learn about the services used to create URLs in the navigational state SPI.

The Navigational State SPI offers the following two services that create URLs that are accessible via the Java Naming and Directory Interface (JNDI):

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 in themes, skins, and portlets if they use the common interface.

For example, you can use a custom JSP tag, which was originally designed for use in themes, in a portlet-specific JSP because only the service lookup is different.


Getting access to the PortalStateManagerService

Access the PortalStateManagerService through a JNDI lookup using the lookup name “portal:service/state/PortalStateManager”.

The lookup returns a com.ibm.portal.state.service.PortalStateManagerServiceHome interface that provides the following two get commands that retrieve com.ibm.portal.state.service.PortalStateManagerService:

PortalStateManagerService getPortalStateManagerService( HttpServletRequest request, HttpServletResponse response)

The returned service is suitable for creating URLs in themes, skins, and custom JSP tags. All server-related information that is needed to generate the URLs (such as host name, protocol, server port, context path etc.) is retrieved from the given servlet request.

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

This method returns a PortalStateManagerService used for “offline” use cases such as creating URLs in environments where the servlet request is not available (for example, in an Enterprise JavaBean). Using this method requires that the server-related information (such as host name, protocol, server port, context path etc.) is provided in the ServerContext argument. The com.ibm.portal.state.accessors.url.ServerContext interface is the needed abstraction for this kind of information. To create URLs that point to a certain virtual portal, the provided ServerContext object needs to address that virtual portal either via the context path or by host name. The two boolean arguments specify whether the URLs should point to the protected area by default (isProtected) and whether the created URLs should be served using a secure connection by default (isSecure). To support creating resource URLs, the arguments locale and profile have to be provided. The locale argument is needed to create URLs that address locale-specific resources such as language-dependent icons. The profile argument represents the client profile and in particular allows for checking the capabilities of the client.

The lifetime of a PortalStateManagerService object depends on whether you requested a “request-specific” service or “offline” service. The request-specific service has request scope; it is only used for the duration of one servlet request. For all subsequent servlet requests, request a new service instance from the home interface.

The lifetime of the offline PortalStateManagerService corresponds with the lifetime of the ServerContext object.

The obtained PortalStateManagerServiceHome object is valid for the lifetime of the portal. You should perform the JNDI lookup only once and store the retrieved home object accordingly, for example in an instance or static variable.

The following example shows how to get access to the service:

/** 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 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;}

The PortalStateManagerService interface is derived from the generic StateManagerService interface.

As the StateManagerService extends the com.ibm.portal.Disposable interface, you should indicate when you do not need to access the service any longer by invoking the offered dispose() method on it. Dispose the request-specific PortalStateManagerService by the end of the processed servlet request.


Getting access to the PortletStateManagerService

Access the PortletStateManagerService via JNDI using the lookup name "portletservice/com.ibm.portal.state.service.PortletStateManagerService".

The lookup returns a generic com.ibm.portal.portlet.service.PortletServiceHome interface which offers a getPortletService(Class) method to get the PortletStateManagerService.

The retrieved PortletStateManagerService instance is valid for the lifetime of the portal. You should perform the service retrieval in the init method of the portlet and store it in a portlet instance variable. The following example shows the required lookup code:

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 (NameNotFoundException e) {
      throw new PortletException(e);
    } catch (NamingException e) {
      throw new PortletException(e);
    }
  }

You can use the actual service within the render method of the portlet (or in the helper methods serving the mandatory portlet modes such as doView, doEdit, and doHelp) to include URLs into the markup or in the processAction method in order to send a redirect to a certain URL. The PortletStateManagerService interface exposes the following two methods:

PortletStateManager getPortletStateManager( PortletRequest request, PortletResponse response)

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

PortletStateManagerController getPortletStateManagerController( ActionRequest request, Action response)

This method returns a PortletStateManagerController that extends the PortletStateManager interface.

The PortletStateManagerController provides additional methods that allow for modifying the navigational state of the portlet for the current request 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 not store references to them across requests. Instead 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 following example shows you how to retrieve the service from the PortletServiceHome object:

/**
 * @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);
  }}


The base interface StateManagerService

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 StateManagerService interface should be sufficient to implement most of the use cases. The interface provides the following two methods:

URLFactory getURLFactory()

This method returns a com.ibm.portal.state.URLFactory object that offers several methods to create a variety of URLs. For further details on the URLFactory.

AccessorFactory getAccessorFactory(Class accessorFactoryClass)

This method provides access to the various accessor factories; see Accessor SPI for additional information. Pass the respective accessor factory interface in as a method argument to retrieve a certain accessor factory the Class object.

When using the PortletStateManagerService, the set of accessor factories is restricted to the SelectionAccessorFactory, PortletAccessorFactory, PortletTargetAccessorFactory, SoloAccessorFactory, ThemeTemplateAccessorFactory, LocaleAccessorFactory, StatePartitionAccessorFactory, and ExpansionStatesAccessorFactory.

Typically, you request an EngineURL object from the URLFactory and then modify the navigational state according to the required URL semantics. To perform this step, call the getState() method of the EngineURL to get the StateHolderController object that is required to modify the navigational state via the Accessor SPI. The following code snippet exemplarily illustrates this typical usage pattern. The following example shows how to call the getState() method of the EngineURL:

protected EngineURL createPageLink(final ObjectID pageID)
  throws StateException {
  // get the URL factory from the state manager service
  final URLFactory urlFactory = service.getURLFactory();
  try {
    // get a EngineURL from the factory; maintain navigational state
    final EngineURL url = urlFactory.newURL(null);
    final SelectionAccessorFactory selectionFct = (SelectionAccessorFactory) 
      service.getAccessorFactory(SelectionAccessorFactory.class);
    // get a selection controller that operates on the URL-specific state
    final SelectionAccessorController selectionCtrl = 
      selectionFct.getSelectionAccessorController(url.getState());
    try {
      // modify page selection and return URL
      selectionCtrl.setSelection(pageID);
      return url;
    } finally {
      selectionCtrl.dispose();
    }
  } finally {
    urlFactory.dispose();
  }}

In the above code sample, the newURL(Constants.Clone) method of the URLFactory is used. However, the URLFactory offers several additional convenience methods to create EngineURLs as well as resource URLs. The following list provides a complete description of the URLFactory interface:

EngineURL newURL(Constants.Clone type)

Choose this method to create a EngineURL because it is suitable for all prevalent use cases. The method provides an EngineURL, which refers to the StateHolder representing the navigational state of the current request. The type argument specifies how the request-specific StateHolder should be cloned for the URL to be created. There are the following four pre-defined clone constants:

  • SMART_COPY indicates that a shallow StateHolder copy should be created which records the state modifications applied for this particular EngineURL only, instead of copying all nodes in the document model to construct the clone.

    The SMART_COPY represents the default; therefore passing in null is equivalent to SMART_COPY.

    This clone method also allows the generation of relative “delta” URLs.

  • DEEP_COPY results in a complete copy of the request-specific StateHolder, for example each node and the node hierarchy is cloned. The deep copy prevents the generation of delta URLs.

  • EMPTY_COPY indicates that the contents of the request-specific StateHolder should be cleared, for example the created EngineURL is based on an empty state. Any interaction with such a URL results in the lost the navigational state of previous interactions.

If the URLFactory does not have access to the current request a new empty StateHolder is created internally. In that case the given type argument does not take any effect.

EngineURL newURL(StateHolder state, Constants.Clone type)

This second getURL method requires that the StateHolder the EngineURL is based on is passed in explicitly. Accordingly, the type argument refers to this particular StateHolder. Use this method when the URL to be created should encode a StateHolder that was defined programmatically. Clicking on this URL results in the lost the navigational state of previous interactions with the portal.

EngineURL newURL(URLContext ctx, Constants.Clone type)

This variant allows you to specify whether the created URL should be absolute, server-relative, or relative. This can be achieved by passing in an URLContext interface which has to be implemented accordingly; for example if the URL must be absolute, the isAbsolute() method must return true whereas isRelative() and isServerRelative() must return false. Because this method does not require an explicit StateHolder argument, the EngineURL to be created will encode the StateHolder retrieved from the given request. To reduce markup size it is strongly recommended to pass in an URLContext that allows for relative URLs . If the URLFactory does not have access to the current request, a new empty StateHolder is created internally. In that case the given type argument does not take any effect.

EngineURL newURL(URLContext ctx, Constants.Clone type)

This method is the counterpart of the previous method and takes a StateHolder defined as an explicit argument.

DisposableURL newResourceURL( String name, PortalResources.Type type)

This method creates a URL that points to a generic resource. The filename and resource type identify the resource. The resource lookup takes request-specific information such as the current locale, the client device, and the markup name into account (if the request is available). The com.ibm.portal.state.accessors.url.PortalResources interface provides several useful constants for the resource type representing resource types such as files, sounds, icons, voice grammars, etc.

DisposableURL newResourceURL( String name, PortalResources.Type type, PortalResources.State state)

This method creates a URL that points to a generic resource. The filename and resource type identify the resource. It can also contain a resource state that is used to further distinguish the lookup of the resource. The resource lookup takes additional information from the request into account (if available). This is the current locale, the client device, markup chosen for the client, and the theme name.

An absolute URL is a complete URL containing protocol, host name and port. In the case of a server-relative URL, the browser implies the current protocol, host name and port. In the case of a relative URL, the browser appends the URL to either the current request URL or to the value of the HTML base tag (if any). Server-relative and relative URLs cannot be enforced. In case of a protocol switch from “http” to “https”, for example, the generated URL is absolute in any case.


Change the host name of absolute URLs

You can change the host name of absolute URLs for security reasons.

For example, this can be the case if the DOM of an application, such as a portlet, runs within an iframe and you do not want the JavaScript code within that iframe to be able to access the DOM of the HTML document itself. If all URLs within that iframe are absolute URLs and the host name of these URLs is different from the one from which the document originates, then the iframe has access to itself only. In other words, it can neither manipulate nor access the rest of the document DOM. To ensure this, create all URLs within the portlet by using the Navigational State SPI with absolute URLs. The portlet should then define a virtual host name, which must be rerouted by a proxy to the portal server again. In addition the portlet must ensure that all requests in which the desired absolute URLs are to be generated contain a special request header that tells the portal the name of the virtual host name. The headers are as follows:

com.ibm.lotus.openajax.virtualhost

Use this for setting the host name of every generated absolute URL to the value of this request header.

com.ibm.lotus.openajax.virtualport

Use this for setting the port of every generated absolute URL to the value of this request header.


Parent topic:

Navigational State SPI