This chapter shows how to use Web Flow in a Portlet environment.
Spring Web Flow requires Portlet API 2.0 to run with.
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.
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.
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>
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"; } }
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>
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"> <property name="flowExecutor" ref="flowExecutor" /> </bean>
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>
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
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
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.
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.
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.
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 Section 14.4, “Portlet Views”, which is not necessary with JSF.
Review the swf-booking-portlet-faces
sample in the Web Flow distribution
for a working JSF Portlets example with complete configuration details. The main
things to ensure are that the <faces:resources>
elements is
included as part of your Spring configuration and that your
faces-config.xml
configuration includes a PortletViewHandler
:
<?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.context.portlet.PortletViewHandler </view-handler> </application> </faces-config>
The JSF Portlet support provided with Spring Web Flow requires JSF v2.0 or above. If
you are upgrading from a previous release you should ensure that your faces-config.xml
references org.springframework.faces.webflow.context.portlet.PortletViewHandler
instead
of org.springframework.faces.webflow.application.portlet.PortletFaceletViewHandler
. You
should also ensure that you have added <faces:resources>
to you Spring configuration.
Although JSF v2.0 is a minimum requirement, this has been primarily driven to provide better support in a Servlet environment. Many of the more advanced JSF 2.0 features (for example 'Partial State Saving') are not supported by Spring Web Flow in a Portlet environment.
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.
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.