# Portlet Integration

## Introduction

This chapter shows how to use Web Flow in a Portlet environment. Web Flow has full support for JSR-168 portlets. The `booking-portlet-mvc` sample application is a good reference for using Web Flow within a portlet. This application is a simplified travel site that allows users to search for and book hotel rooms.

## Configuring web.xml and portlet.xml

The configuration for a portlet depends on the portlet container used. The sample applications, included with Web Flow, are both configured to use Apache Pluto, the JSR-168 reference implementation.

In general, the configuration requires adding a servlet mapping in the `web.xml` file to dispatch request to the portlet container.

    <servlet>
        <servlet-name>swf-booking-mvc</servlet-name>
        <servlet-class>org.apache.pluto.core.PortletServlet</servlet-class>
        <init-param>
            <param-name>portlet-name</param-name>
            <param-value>swf-booking-mvc</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>swf-booking-mvc</servlet-name>
        <url-pattern>/PlutoInvoker/swf-booking-mvc</url-pattern>
    </servlet-mapping>

The `portlet.xml` configuration is a standard portlet configuration. The `portlet-class` needs to be set along with a pair of `init-param`s. Setting the `expiration-cache` to `0` is recommended to force Web Flow to always render a fresh view.

    <portlet>
        ...
        <portlet-class>org.springframework.web.portlet.DispatcherPortlet</portlet-class>
        <init-param>
            <name>contextConfigLocation</name>
            <value>/WEB-INF/web-application-config.xml</value>
        </init-param>
        <init-param>
            <name>viewRendererUrl</name>
            <value>/WEB-INF/servlet/view</value>
        </init-param>
        <expiration-cache>0</expiration-cache>
        ...
    </portlet>

## Configuring Spring

### Flow Handlers

The only supported mechanism for bridging a portlet request to Web Flow is a `FlowHandler`. The `PortletFlowController` used in Web Flow 1.0 is no longer supported.

The flow handler, similar to the servlet flow handler, provides hooks that can:

- select the flow to execute

- pass input parameters to the flow on initialization

- handle the flow execution outcome

- handle exceptions

The `AbstractFlowHandler` class is an implementation of `FlowHandler` that provides default implementations for these hooks.

In a portlet environment the targeted flow id can not be inferred from the URL and must be defined explicitly in the handler.

    public class ViewFlowHandler extends AbstractFlowHandler {
        public String getFlowId() {
            return "view";
        }
    }

### Handler Mappings

Spring Portlet MVC provides a rich set of methods to map portlet requests. Complete documentation is available in the Spring Reference Documentation.

The `booking-portlet-mvc` sample application uses a `PortletModeHandlerMapping` to map portlet requests. The sample application only supports `view` mode, but support for other portlet modes is available. Other modes can be added and point to the same flow as `view` mode, or any other flow.

    <bean id="portletModeHandlerMapping"
          class="org.springframework.web.portlet.handler.PortletModeHandlerMapping">
        <property name="portletModeMap">
            <map>
                <entry key="view">
                    <bean class="org.springframework.webflow.samples.booking.ViewFlowHandler" />
                </entry>
            </map>
        </property>
    </bean>

### Flow Handler Adapter

A `FlowHandlerAdapter` converts the handler mappings to the flow handlers. The flow executor is required as a constructor argument.

    <bean id="flowHandlerAdapter"
          class="org.springframework.webflow.mvc.portlet.FlowHandlerAdapter">
        <constructor-arg ref="flowExecutor" />
    </bean>

## Portlet Views

In order to facilitate view rendering, a `ViewRendererServlet` must be added to the `web.xml` file. This servlet is not invoked directly, but it used by Web Flow to render views in a portlet environment.

    <servlet>
        <servlet-name>ViewRendererServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.ViewRendererServlet</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>ViewRendererServlet</servlet-name>
        <url-pattern>/WEB-INF/servlet/view</url-pattern>
    </servlet-mapping>

## Portlet Modes and Window States

### Window State

The Portlet API defined three window states: normal, minimized and maximized. The portlet implementation must decide what to render for each of these window states. Web Flow exposes the string value of the window state under `portletWindowState` via the request map on the external context.

    requestContext.getExternalContext().getRequestMap().get("portletWindowState");

    externalContext.requestMap.portletWindowState

### Portlet Mode

The Portlet API defined three portlet modes: view, edit and help. The portlet implementation must decide what to render for each of these modes. Web Flow exposes the string value of the portlet mode under `portletMode` via the request map on the external context.

    requestContext.getExternalContext().getRequestMap().get("portletMode");

    externalContext.requestMap.portletMode

## Issues in a Portlet Environment

### Redirects

The Portlet API only allows redirects to be requested from an action request. Because views are rendered on the render request, views and `view-state`s cannot trigger a redirect.

The `externalRedirect:` view prefix is a convenience for Servlet based flows. An `IllegalStateException` is thrown if a redirect is requested from a render request.

`end-state` redirects can be achieved by implementing `FlowHandler.handleExecutionOutcome`. This callback provides the `ActionResponse` object which supports redirects.

### Switching Portlet Modes

The portlet container passes the execution key from the previous flow when switching to a new mode. Even if the mode is mapped to a different `FlowHandler` the flow execution will resume the previous execution. You may switch the mode programatically in your FlowHandler after ending a flow in an ActionRequest.

One way to start a new flow is to create a URL targeting the mode without the execution key.

### Portlets and JSF

Web Flow supports JSF as the view technology for a portlet. However, a jsf-portlet bridge (JSR-301) must be provided. At the time of this writing, no feature complete jsf-portlet bridge exists. Some of the existing bridge implementations may appear to work, however, side effect may occur.

JSF portlets are considered experimental at this time.