Files
spring-webflow/spring-webflow-reference/src/portlet.xml
2010-10-28 13:44:57 +00:00

270 lines
11 KiB
XML

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<chapter id="portlet">
<title>Portlet Integration</title>
<sect1 id="portlet-introduction">
<title>Introduction</title>
<para>
This chapter shows how to use Web Flow in a Portlet environment.
Spring Web Flow requires Portlet API 2.0 to run with.
The <code>booking-portlet-mvc</code> 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.
</para>
</sect1>
<sect1 id="portlet-config-core">
<title>Configuring web.xml and portlet.xml</title>
<para>
The configuration for a portlet depends on the portlet container used.
The sample applications, included with Web Flow, are both configured to use <ulink url="http://portals.apache.org/pluto/">Apache Pluto</ulink>.
</para>
<para>
In general, the configuration requires adding a servlet mapping in the <code>web.xml</code> file to dispatch request to the portlet container.
</para>
<programlisting language="xml"><![CDATA[
<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>
]]></programlisting>
<para>
The <code>portlet.xml</code> configuration is a standard portlet configuration.
The <code>portlet-class</code> needs to be set along with a pair of <code>init-param</code>s.
Setting the <code>expiration-cache</code> to <code>0</code> is recommended to force Web Flow to always render a fresh view.
</para>
<programlisting language="xml"><![CDATA[
<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>
]]></programlisting>
</sect1>
<sect1 id="portlet-config-spring">
<title>Configuring Spring</title>
<sect2 id="portlet-config-spring-handler">
<title>Flow Handlers</title>
<para>
The only supported mechanism for bridging a portlet request to Web Flow is a <code>FlowHandler</code>.
The <code>PortletFlowController</code> used in Web Flow 1.0 is no longer supported.
</para>
<para>
The flow handler, similar to the servlet flow handler, provides hooks that can:
<itemizedlist>
<listitem>
<para>select the flow to execute</para>
</listitem>
<listitem>
<para>pass input parameters to the flow on initialization</para>
</listitem>
<listitem>
<para>handle the flow execution outcome</para>
</listitem>
<listitem>
<para>handle exceptions</para>
</listitem>
</itemizedlist>
</para>
<para>
The <code>AbstractFlowHandler</code> class is an implementation of <code>FlowHandler</code> that provides default implementations for these hooks.
</para>
<para>
In a portlet environment the targeted flow id can not be inferred from the URL and must be defined explicitly in the handler.
</para>
<programlisting language="java"><![CDATA[
public class ViewFlowHandler extends AbstractFlowHandler {
public String getFlowId() {
return "view";
}
}
]]></programlisting>
</sect2>
<sect2 id="portlet-config-spring-mappings">
<title>Handler Mappings</title>
<para>
Spring Portlet MVC provides a rich set of methods to map portlet requests.
Complete documentation is available in the <ulink url="http://static.springframework.org/spring/docs/2.5.x/reference/portlet.html#portlet-handlermapping">Spring Reference Documentation</ulink>.
</para>
<para>
The <code>booking-portlet-mvc</code> sample application uses a <code>PortletModeHandlerMapping</code> to map portlet requests.
The sample application only supports <code>view</code> mode, but support for other portlet modes is available.
Other modes can be added and point to the same flow as <code>view</code> mode, or any other flow.
</para>
<programlisting language="xml"><![CDATA[
<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>
]]></programlisting>
</sect2>
<sect2 id="portlet-config-spring-handler-adapter">
<title>Flow Handler Adapter</title>
<para>
A <code>FlowHandlerAdapter</code> converts the handler mappings to the flow handlers.
The flow executor is required as a constructor argument.
</para>
<programlisting language="xml"><![CDATA[
<bean id="flowHandlerAdapter"
class="org.springframework.webflow.mvc.portlet.FlowHandlerAdapter">
<constructor-arg ref="flowExecutor" />
</bean>
]]></programlisting>
</sect2>
</sect1>
<sect1 id="portlet-views">
<title>Portlet Views</title>
<para>
In order to facilitate view rendering, a <code>ViewRendererServlet</code> must be added to the <code>web.xml</code> file.
This servlet is not invoked directly, but it used by Web Flow to render views in a portlet environment.
</para>
<programlisting language="xml"><![CDATA[
<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>
]]></programlisting>
</sect1>
<sect1 id="portlet-modes">
<title>Portlet Modes and Window States</title>
<sect2 id="portlet-modes-state">
<title>Window State</title>
<para>
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 <code>portletWindowState</code> via the request map on the external context.
</para>
<programlisting language="java"><![CDATA[
requestContext.getExternalContext().getRequestMap().get("portletWindowState");
]]></programlisting>
<programlisting><![CDATA[
externalContext.requestMap.portletWindowState
]]></programlisting>
</sect2>
<sect2 id="portlet-modes-mode">
<title>Portlet Mode</title>
<para>
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 <code>portletMode</code> via the request map on the external context.
</para>
<programlisting language="java"><![CDATA[
requestContext.getExternalContext().getRequestMap().get("portletMode");
]]></programlisting>
<programlisting><![CDATA[
externalContext.requestMap.portletMode
]]></programlisting>
</sect2>
</sect1>
<sect1 id="portlet-jsf">
<title>Using Portlets with JSF</title>
<sect2 id="portlet-jsf-using-portlet-jsf">
<para>
Prior to version 2.1 of Spring Web Flow, support for JSF Portlets was considered
experimental and relied on a Portlet Bridge for JSF implementation.
Furthermore JSR-329 (the latest specification in this area), which targets
Portlet API 2.0 and JSF 1.2 environments at the time of writing is not yet
final causing portlet bridge implementations to also remain incomplete.
</para>
<para>
A closer comparison of Spring Web Flow and a Portlet Bridge for JSF shows
the two have significant overlap. They both drive the
JSF lifecycle and they both shield JSF from knowledge about Portlet action
and render requests.
</para>
<para>
Considering all of the above, starting with version 2.2, Spring Web Flow
provides support for JSF Portlets using its own internal Portlet integration rather
than a Portlet Bridge for JSF. We believe this will provide value for Web Flow users
by reducing the number of dependencies in what is already a fairly complex
combination of technologies with specifications lagging behind.
</para>
<para>
What this practically means is the configuration required for JSF Portlets is
very similar to what is alread documented in the rest of this chapter
with the exception of <xref linkend="portlet-views"/>, which is not necessary
with JSF.
</para>
<para>
Review the <code>swf-booking-portlet-faces</code> sample in the Web Flow distribution
for a working JSF Portlets example with complete configuration details. The main thing
you'll need to notice in addition to what has already been described in this
chapter is the <code>faces-config.xml</code> configuration:
</para>
<programlisting language="java"><![CDATA[<?xml version="1.0"?>
<!DOCTYPE faces-config PUBLIC
"-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.0//EN"
"http://java.sun.com/dtd/web-facesconfig_1_0.dtd">
<faces-config>
<application>
<view-handler>
org.springframework.faces.webflow.application.portlet.PortletFaceletViewHandler
</view-handler>
</application>
</faces-config>
]]></programlisting>
</sect2>
</sect1>
<sect1 id="portlet-issues">
<title>Issues in a Portlet Environment</title>
<sect2 id="portlet-issues-redirects">
<title>Redirects</title>
<para>
The Portlet API only allows redirects to be requested from an action request.
Because views are rendered on the render request, views and <code>view-state</code>s cannot trigger a redirect.
</para>
<para>
The <code>externalRedirect:</code> view prefix is a convenience for Servlet based flows.
An <code>IllegalStateException</code> is thrown if a redirect is requested from a render request.
</para>
<para>
<code>end-state</code> redirects can be achieved by implementing <code>FlowHandler.handleExecutionOutcome</code>.
This callback provides the <code>ActionResponse</code> object which supports redirects.
</para>
</sect2>
<sect2 id="portlet-issues-modes">
<title>Switching Portlet Modes</title>
<para>
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 <code>FlowHandler</code> the flow execution will resume the previous execution.
You may switch the mode programatically in your FlowHandler after ending a flow in an ActionRequest.
</para>
<para>
One way to start a new flow is to create a URL targeting the mode without the execution key.
</para>
</sect2>
</sect1>
</chapter>