1817 lines
84 KiB
XML
1817 lines
84 KiB
XML
<?xml version="1.0" encoding="UTF-8"?>
|
||
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
|
||
"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
|
||
|
||
<chapter id="portlet" xmlns:xi="http://www.w3.org/2001/XInclude">
|
||
<title>Portlet MVC Framework</title>
|
||
|
||
<section id="portlet-introduction">
|
||
<title>Introduction</title>
|
||
|
||
<sidebar>
|
||
<title>JSR-168 The Java Portlet Specification</title>
|
||
<para>For more general information about portlet development, please
|
||
review a whitepaper from Sun entitled
|
||
<ulink url="http://developers.sun.com/prodtech/portalserver/reference/techart/jsr168/">"Introduction to JSR 168"</ulink>,
|
||
and of course the
|
||
<ulink url="http://jcp.org/aboutJava/communityprocess/final/jsr168/">JSR-168 Specification</ulink> itself.</para>
|
||
</sidebar>
|
||
|
||
<para>In addition to supporting conventional (servlet-based) Web development,
|
||
Spring also supports JSR-168 Portlet development. As much as possible, the
|
||
Portlet MVC framework is a mirror image of the Web MVC framework, and also
|
||
uses the same underlying view abstractions and integration technology. So, be
|
||
sure to review the chapters entitled <xref linkend="mvc"/> and
|
||
<xref linkend="view"/> before continuing with this chapter.</para>
|
||
|
||
<note>
|
||
<para>Bear in mind that while the concepts of Spring MVC are the
|
||
same in Spring Portlet MVC, there are some notable differences
|
||
created by the unique workflow of JSR-168 portlets.</para>
|
||
</note>
|
||
|
||
<para>The main way in which portlet workflow differs from servlet
|
||
workflow is that the request to the portlet can have two distinct
|
||
phases: the action phase and the render phase. The action phase is
|
||
executed only once and is where any 'backend' changes or actions occur,
|
||
such as making changes in a database. The render phase then produces
|
||
what is displayed to the user each time the display is refreshed.
|
||
The critical point here is that for a single overall request, the action
|
||
phase is executed only once, but the render phase may be executed
|
||
multiple times. This provides (and requires) a clean separation between
|
||
the activities that modify the persistent state of your system and the
|
||
activities that generate what is displayed to the user.</para>
|
||
|
||
<!-- insert some content about Spring Web Flow here -->
|
||
<xi:include href="swf-sidebar.xml"/>
|
||
|
||
<para>The dual phases of portlet requests are one of the real strengths
|
||
of the JSR-168 specification. For example, dynamic search results can be
|
||
updated routinely on the display without the user explicitly rerunning
|
||
the search. Most other portlet MVC frameworks attempt to completely
|
||
hide the two phases from the developer and make it look as much like
|
||
traditional servlet development as possible - we think this
|
||
approach removes one of the main benefits of using portlets. So, the
|
||
separation of the two phases is preserved throughout the Spring Portlet
|
||
MVC framework. The primary manifestation of this approach is that where
|
||
the servlet version of the MVC classes will have one method that deals
|
||
with the request, the portlet version of the MVC classes will have two
|
||
methods that deal with the request: one for the action phase and one for
|
||
the render phase. For example, where the servlet version of
|
||
<classname>AbstractController</classname> has the
|
||
<methodname>handleRequestInternal(..)</methodname> method, the portlet
|
||
version of <classname>AbstractController</classname> has
|
||
<methodname>handleActionRequestInternal(..)</methodname> and
|
||
<methodname>handleRenderRequestInternal(..)</methodname> methods.</para>
|
||
|
||
<para>The framework is designed around a
|
||
<classname>DispatcherPortlet</classname> that dispatches requests to
|
||
handlers, with configurable handler mappings and view resolution, just
|
||
as the <classname>DispatcherServlet</classname> in the web framework
|
||
does. File upload is also supported in the same way.</para>
|
||
|
||
<para>Locale resolution and theme resolution are not supported in
|
||
Portlet MVC - these areas are in the purview of the
|
||
portal/portlet container and are not appropriate at the Spring level.
|
||
However, all mechanisms in Spring that depend on the locale (such as
|
||
internationalization of messages) will still function properly because
|
||
<classname>DispatcherPortlet</classname> exposes the current locale in
|
||
the same way as <classname>DispatcherServlet</classname>.</para>
|
||
|
||
<section id="portlet-introduction-controller">
|
||
<title>Controllers - The C in MVC</title>
|
||
<para>The default handler is still a very simple
|
||
<interfacename>Controller</interfacename> interface, offering just two
|
||
methods:</para>
|
||
<itemizedlist>
|
||
<listitem>
|
||
<para><methodname>void handleActionRequest(request,response)</methodname> </para>
|
||
</listitem>
|
||
<listitem>
|
||
<para><methodname>ModelAndView handleRenderRequest(request,response)</methodname> </para>
|
||
</listitem>
|
||
</itemizedlist>
|
||
<para>The framework also includes most of the same controller
|
||
implementation hierarchy, such as <classname>AbstractController</classname>,
|
||
<classname>SimpleFormController</classname>, and so on. Data binding,
|
||
command object usage, model handling, and view resolution are all the
|
||
same as in the servlet framework.</para>
|
||
</section>
|
||
|
||
<section id="portlet-introduction-view">
|
||
<title>Views - The V in MVC</title>
|
||
<para>All the view rendering capabilities of the servlet framework are
|
||
used directly via a special bridge servlet named
|
||
<classname>ViewRendererServlet</classname>. By using this servlet, the
|
||
portlet request is converted into a servlet request and the view can be
|
||
rendered using the entire normal servlet infrastructure. This means all
|
||
the existing renderers, such as JSP, Velocity, etc., can still be used
|
||
within the portlet.</para>
|
||
</section>
|
||
|
||
<section id="portlet-introduction-scope">
|
||
<title>Web-scoped beans</title>
|
||
<para>Spring Portlet MVC supports beans whose lifecycle is scoped to the
|
||
current HTTP request or HTTP <interfacename>Session</interfacename> (both
|
||
normal and global). This is not a specific feature of Spring Portlet MVC
|
||
itself, but rather of the <interfacename>WebApplicationContext</interfacename>
|
||
container(s) that Spring Portlet MVC uses. These bean scopes are described
|
||
in detail in <xref linkend="beans-factory-scopes-other"/></para>
|
||
</section>
|
||
|
||
<!--
|
||
As of Spring 3.0.0.RC1, the PetPortal sample application is not
|
||
included in the Spring distribution. Thus the following note is
|
||
commented out until further notice.
|
||
-->
|
||
<!--
|
||
<note>
|
||
<para>The Spring distribution ships with a complete Spring Portlet MVC
|
||
sample application that demonstrates all of the features and functionality
|
||
of the Spring Portlet MVC framework. This 'petportal' application can be found
|
||
in the <filename class="directory">'samples/petportal'</filename> directory of
|
||
the full Spring distribution.</para>
|
||
</note>
|
||
-->
|
||
</section>
|
||
|
||
<section id="portlet-dispatcher">
|
||
<title>The <classname>DispatcherPortlet</classname></title>
|
||
|
||
<para>Portlet MVC is a request-driven web MVC framework, designed around
|
||
a portlet that dispatches requests to controllers and offers other
|
||
functionality facilitating the development of portlet applications.
|
||
Spring's <classname>DispatcherPortlet</classname> however, does more
|
||
than just that. It is completely integrated with the Spring
|
||
<interfacename>ApplicationContext</interfacename> and allows you to use
|
||
every other feature Spring has.</para>
|
||
|
||
<para>Like ordinary portlets, the
|
||
<classname>DispatcherPortlet</classname> is declared in the
|
||
<literal>portlet.xml</literal> file of your web application:</para>
|
||
|
||
<programlisting language="xml"><![CDATA[<portlet>
|
||
<portlet-name>sample</portlet-name>
|
||
<portlet-class>org.springframework.web.portlet.DispatcherPortlet</portlet-class>
|
||
<supports>
|
||
<mime-type>text/html</mime-type>
|
||
<portlet-mode>view</portlet-mode>
|
||
</supports>
|
||
<portlet-info>
|
||
<title>Sample Portlet</title>
|
||
</portlet-info>
|
||
</portlet>]]></programlisting>
|
||
|
||
<para>The <classname>DispatcherPortlet</classname> now needs to be
|
||
configured.</para>
|
||
|
||
<para>In the Portlet MVC framework, each
|
||
<classname>DispatcherPortlet</classname> has its own
|
||
<interfacename>WebApplicationContext</interfacename>, which inherits all
|
||
the beans already defined in the Root
|
||
<interfacename>WebApplicationContext</interfacename>. These inherited
|
||
beans can be overridden in the portlet-specific scope, and new
|
||
scope-specific beans can be defined local to a given portlet instance.</para>
|
||
|
||
<para>The framework will, on initialization of a
|
||
<classname>DispatcherPortlet</classname>, look for a file named
|
||
<literal>[portlet-name]-portlet.xml</literal> in the <literal>WEB-INF</literal>
|
||
directory of your web application and create the beans defined there
|
||
(overriding the definitions of any beans defined with the same name in
|
||
the global scope).</para>
|
||
|
||
<para>The config location used by the
|
||
<classname>DispatcherPortlet</classname> can be modified through a
|
||
portlet initialization parameter (see below for details).</para>
|
||
|
||
<para>The Spring <classname>DispatcherPortlet</classname> has a few
|
||
special beans it uses, in order to be able to process requests and
|
||
render the appropriate views. These beans are included in the Spring
|
||
framework and can be configured in the
|
||
<interfacename>WebApplicationContext</interfacename>, just as any other
|
||
bean would be configured. Each of those beans is described in more
|
||
detail below. Right now, we'll just mention them, just to let you know
|
||
they exist and to enable us to go on talking about the
|
||
<classname>DispatcherPortlet</classname>. For most of the beans,
|
||
defaults are provided so you don't have to worry about configuring
|
||
them.</para>
|
||
|
||
<table id="portlet-webappctx-special-beans-tbl">
|
||
<title>Special beans in the <interfacename>WebApplicationContext</interfacename></title>
|
||
<tgroup cols="2">
|
||
<colspec colname="c1" colwidth="1*" align="left" />
|
||
<colspec colname="c2" colwidth="3*" />
|
||
<thead>
|
||
<row>
|
||
<entry>Expression</entry>
|
||
<entry>Explanation</entry>
|
||
</row>
|
||
</thead>
|
||
<tbody>
|
||
<row>
|
||
<entry>handler mapping(s)</entry>
|
||
<entry>(<xref linkend="portlet-handlermapping" />) a
|
||
list of pre- and post-processors and controllers that
|
||
will be executed if they match certain criteria (for
|
||
instance a matching portlet mode specified with the
|
||
controller)</entry>
|
||
</row>
|
||
<row>
|
||
<entry>controller(s)</entry>
|
||
<entry>(<xref linkend="portlet-controller" />) the beans
|
||
providing the actual functionality (or at least, access
|
||
to the functionality) as part of the MVC triad</entry>
|
||
</row>
|
||
<row>
|
||
<entry>view resolver</entry>
|
||
<entry>(<xref linkend="portlet-viewresolver" />) capable
|
||
of resolving view names to view definitions</entry>
|
||
</row>
|
||
<row>
|
||
<entry>multipart resolver</entry>
|
||
<entry>(<xref linkend="portlet-multipart" />) offers
|
||
functionality to process file uploads from HTML
|
||
forms</entry>
|
||
</row>
|
||
<row>
|
||
<entry>handler exception resolver</entry>
|
||
<entry>(<xref linkend="portlet-exceptionresolver" />)
|
||
offers functionality to map exceptions to views or
|
||
implement other more complex exception handling
|
||
code</entry>
|
||
</row>
|
||
</tbody>
|
||
</tgroup>
|
||
</table>
|
||
|
||
<para>When a <classname>DispatcherPortlet</classname> is setup for use
|
||
and a request comes in for that specific
|
||
<classname>DispatcherPortlet</classname>, it starts processing the
|
||
request. The list below describes the complete process a request goes
|
||
through if handled by a <classname>DispatcherPortlet</classname>:</para>
|
||
|
||
<orderedlist>
|
||
|
||
<listitem><para>The locale returned by
|
||
<literal>PortletRequest.getLocale()</literal> is bound to the
|
||
request to let elements in the process resolve the locale to use
|
||
when processing the request (rendering the view, preparing data,
|
||
etc.).</para></listitem>
|
||
|
||
<listitem><para>If a multipart resolver is specified and this is an
|
||
<interfacename>ActionRequest</interfacename>, the request is
|
||
inspected for multiparts and if they are found, it is wrapped in a
|
||
<interfacename>MultipartActionRequest</interfacename> for further
|
||
processing by other elements in the process. (See <xref
|
||
linkend="portlet-multipart" /> for further information about
|
||
multipart handling).</para></listitem>
|
||
|
||
<listitem><para>An appropriate handler is searched for. If a handler
|
||
is found, the execution chain associated with the handler
|
||
(pre-processors, post-processors, controllers) will be executed in order
|
||
to prepare a model.</para></listitem>
|
||
|
||
<listitem><para>If a model is returned, the view is rendered, using
|
||
the view resolver that has been configured with the
|
||
<interfacename>WebApplicationContext</interfacename>. If no model is
|
||
returned (which could be due to a pre- or post-processor
|
||
intercepting the request, for example, for security reasons), no
|
||
view is rendered, since the request could already have been
|
||
fulfilled.</para></listitem>
|
||
|
||
</orderedlist>
|
||
|
||
<para>Exceptions that are thrown during processing of the request
|
||
get picked up by any of the handler exception resolvers that are
|
||
declared in the <interfacename>WebApplicationContext</interfacename>.
|
||
Using these exception resolvers you can define custom behavior in case
|
||
such exceptions get thrown.</para>
|
||
|
||
<para>You can customize Spring's <classname>DispatcherPortlet</classname>
|
||
by adding context parameters in the <literal>portlet.xml</literal> file or
|
||
portlet init-parameters. The possibilities are listed below.</para>
|
||
|
||
<table frame="all" id="portlet-dpp-init-params">
|
||
<title><classname>DispatcherPortlet</classname> initialization parameters</title>
|
||
<tgroup cols="2">
|
||
<colspec colname="c1" colwidth="1*" align="left" />
|
||
<colspec colname="c2" colwidth="3*" />
|
||
<thead>
|
||
<row>
|
||
<entry>Parameter</entry>
|
||
<entry>Explanation</entry>
|
||
</row>
|
||
</thead>
|
||
<tbody>
|
||
<row>
|
||
<entry><literal>contextClass</literal></entry>
|
||
<entry>Class that implements
|
||
<interfacename>WebApplicationContext</interfacename>,
|
||
which will be used to instantiate the context used by
|
||
this portlet. If this parameter isn't specified, the
|
||
<classname>XmlPortletApplicationContext</classname> will
|
||
be used.</entry>
|
||
</row>
|
||
<row>
|
||
<entry><literal>contextConfigLocation</literal></entry>
|
||
<entry>String which is passed to the context instance
|
||
(specified by <literal>contextClass</literal>) to
|
||
indicate where context(s) can be found. The String is
|
||
potentially split up into multiple Strings (using a
|
||
comma as a delimiter) to support multiple contexts (in
|
||
case of multiple context locations, for beans that are
|
||
defined twice, the latest takes precedence).</entry>
|
||
</row>
|
||
<row>
|
||
<entry><literal>namespace</literal></entry>
|
||
<entry>The namespace of the
|
||
<interfacename>WebApplicationContext</interfacename>.
|
||
Defaults to <literal>[portlet-name]-portlet</literal>.</entry>
|
||
</row>
|
||
<row>
|
||
<entry><literal>viewRendererUrl</literal></entry>
|
||
<entry>The URL at which
|
||
<classname>DispatcherPortlet</classname> can access an
|
||
instance of <classname>ViewRendererServlet</classname>
|
||
(see <xref linkend="portlet-viewservlet" />).</entry>
|
||
</row>
|
||
</tbody>
|
||
</tgroup>
|
||
</table>
|
||
</section>
|
||
|
||
<section id="portlet-viewservlet">
|
||
<title>The <classname>ViewRendererServlet</classname></title>
|
||
|
||
<para>The rendering process in Portlet MVC is a bit more complex than in
|
||
Web MVC. In order to reuse all the <link linkend="view">view technologies</link>
|
||
from Spring Web MVC, we must convert the
|
||
<interfacename>PortletRequest</interfacename> /
|
||
<interfacename>PortletResponse</interfacename> to
|
||
<interfacename>HttpServletRequest</interfacename> /
|
||
<interfacename>HttpServletResponse</interfacename> and then call the
|
||
<literal>render</literal> method of the
|
||
<interfacename>View</interfacename>. To do this,
|
||
<classname>DispatcherPortlet</classname> uses a special servlet that
|
||
exists for just this purpose: the
|
||
<classname>ViewRendererServlet</classname>.</para>
|
||
|
||
<para>In order for <classname>DispatcherPortlet</classname> rendering to
|
||
work, you must declare an instance of the
|
||
<classname>ViewRendererServlet</classname> in the
|
||
<literal>web.xml</literal> file for your web application as
|
||
follows:</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>
|
||
|
||
<para>To perform the actual rendering, <classname>DispatcherPortlet</classname>
|
||
does the following:</para>
|
||
|
||
<orderedlist>
|
||
<listitem><para>Binds the
|
||
<interfacename>WebApplicationContext</interfacename> to the request
|
||
as an attribute under the same
|
||
<literal>WEB_APPLICATION_CONTEXT_ATTRIBUTE</literal> key that
|
||
<classname>DispatcherServlet</classname> uses.</para></listitem>
|
||
|
||
<listitem><para>Binds the <interfacename>Model</interfacename> and
|
||
<interfacename>View</interfacename> objects to the request to make
|
||
them available to the
|
||
<classname>ViewRendererServlet</classname>.</para></listitem>
|
||
|
||
<listitem><para>Constructs a
|
||
<interfacename>PortletRequestDispatcher</interfacename> and performs
|
||
an <literal>include</literal> using the <literal>/WEB-
|
||
INF/servlet/view</literal> URL that is mapped to the
|
||
<classname>ViewRendererServlet</classname>.</para></listitem>
|
||
</orderedlist>
|
||
|
||
<para>The <classname>ViewRendererServlet</classname> is then able to
|
||
call the <literal>render</literal> method on the
|
||
<interfacename>View</interfacename> with the appropriate
|
||
arguments.</para>
|
||
|
||
<para>The actual URL for the <classname>ViewRendererServlet</classname>
|
||
can be changed using <classname>DispatcherPortlet</classname>’s
|
||
<literal>viewRendererUrl</literal> configuration parameter.</para>
|
||
|
||
</section>
|
||
|
||
<section id="portlet-controller">
|
||
<title>Controllers</title>
|
||
|
||
<para>The controllers in Portlet MVC are very similar to the Web MVC
|
||
Controllers, and porting code from one to the other should be
|
||
simple.</para>
|
||
|
||
<para>The basis for the Portlet MVC controller architecture is the
|
||
<interfacename>org.springframework.web.portlet.mvc.Controller</interfacename>
|
||
interface, which is listed below.</para>
|
||
|
||
<programlisting language="java"><![CDATA[public interface Controller {
|
||
|
||
/**
|
||
* Process the render request and return a ModelAndView object which the
|
||
* DispatcherPortlet will render.
|
||
*/
|
||
ModelAndView handleRenderRequest(RenderRequest request, RenderResponse response)
|
||
throws Exception;
|
||
|
||
/**
|
||
* Process the action request. There is nothing to return.
|
||
*/
|
||
void handleActionRequest(ActionRequest request, ActionResponse response)
|
||
throws Exception;
|
||
}]]></programlisting>
|
||
|
||
<para>As you can see, the Portlet
|
||
<interfacename>Controller</interfacename> interface requires two methods
|
||
that handle the two phases of a portlet request: the action request and
|
||
the render request. The action phase should be capable of handling an
|
||
action request, and the render phase should be capable of handling a
|
||
render request and returning an appropriate model and view. While the
|
||
<interfacename>Controller</interfacename> interface is quite abstract,
|
||
Spring Portlet MVC offers several controllers that already contain a
|
||
lot of the functionality you might need; most of these are very similar
|
||
to controllers from Spring Web MVC. The
|
||
<interfacename>Controller</interfacename> interface just defines the
|
||
most common functionality required of every controller: handling an
|
||
action request, handling a render request, and returning a model and a
|
||
view.</para>
|
||
|
||
<section id="portlet-controller-abstractcontroller">
|
||
<title><classname>AbstractController</classname> and <classname>PortletContentGenerator</classname></title>
|
||
|
||
<para>Of course, just a <interfacename>Controller</interfacename>
|
||
interface isn't enough. To provide a basic infrastructure, all of
|
||
Spring Portlet MVC's <interfacename>Controller</interfacename>s
|
||
inherit from <classname>AbstractController</classname>, a class
|
||
offering access to Spring's
|
||
<interfacename>ApplicationContext</interfacename> and control over
|
||
caching.</para>
|
||
|
||
<table frame="all" id="portlet-ac-features">
|
||
<title>Features offered by the <classname>AbstractController</classname></title>
|
||
<tgroup cols="2">
|
||
<colspec colname="c1" colwidth="1*" align="left" />
|
||
<colspec colname="c2" colwidth="3*" />
|
||
<thead>
|
||
<row>
|
||
<entry>Parameter</entry>
|
||
<entry>Explanation</entry>
|
||
</row>
|
||
</thead>
|
||
<tbody>
|
||
<row>
|
||
<entry><literal>requireSession</literal></entry>
|
||
<entry>Indicates whether or not this
|
||
<interfacename>Controller</interfacename> requires a
|
||
session to do its work. This feature is offered to
|
||
all controllers. If a session is not present when
|
||
such a controller receives a request, the user is
|
||
informed using a
|
||
<classname>SessionRequiredException</classname>.</entry>
|
||
</row>
|
||
<row>
|
||
<entry><literal>synchronizeSession</literal></entry>
|
||
<entry>Use this if you want handling by this
|
||
controller to be synchronized on the user's session.
|
||
To be more specific, the extending controller will
|
||
override the <methodname>handleRenderRequestInternal(..)</methodname> and
|
||
<methodname>handleActionRequestInternal(..)</methodname> methods, which will be
|
||
synchronized on the user’s session if you specify
|
||
this variable.</entry>
|
||
</row>
|
||
<row>
|
||
<entry><literal>renderWhenMinimized</literal></entry>
|
||
<entry>If you want your controller to actually
|
||
render the view when the portlet is in a minimized
|
||
state, set this to true. By default, this is set to
|
||
false so that portlets that are in a minimized state
|
||
don’t display any content.</entry>
|
||
</row>
|
||
<row>
|
||
<entry><literal>cacheSeconds</literal></entry>
|
||
<entry>When you want a controller to override the
|
||
default cache expiration defined for the portlet,
|
||
specify a positive integer here. By default it is
|
||
set to <literal>-1</literal>, which does not change
|
||
the default caching. Setting it to <literal>0</literal>
|
||
will ensure the result is never cached.</entry>
|
||
</row>
|
||
</tbody>
|
||
</tgroup>
|
||
</table>
|
||
|
||
<para>The <literal>requireSession</literal> and
|
||
<literal>cacheSeconds</literal> properties are declared on the
|
||
<classname>PortletContentGenerator</classname> class, which is the
|
||
superclass of <classname>AbstractController</classname>) but are
|
||
included here for completeness.</para>
|
||
|
||
<para>When using the <classname>AbstractController</classname> as a
|
||
baseclass for your controllers (which is not recommended since there
|
||
are a lot of other controllers that might already do the job for
|
||
you) you only have to override either the
|
||
<methodname>handleActionRequestInternal(ActionRequest,
|
||
ActionResponse)</methodname> method or the
|
||
<methodname>handleRenderRequestInternal(RenderRequest,
|
||
RenderResponse)</methodname> method (or both), implement your logic,
|
||
and return a <classname>ModelAndView</classname> object (in the case
|
||
of <literal>handleRenderRequestInternal</literal>).</para>
|
||
|
||
<para>The default implementations of both
|
||
<methodname>handleActionRequestInternal(..)</methodname> and
|
||
<methodname>handleRenderRequestInternal(..)</methodname> throw a
|
||
<classname>PortletException</classname>. This is consistent with
|
||
the behavior of <classname>GenericPortlet</classname> from the JSR-
|
||
168 Specification API. So you only need to override the method that
|
||
your controller is intended to handle.</para>
|
||
|
||
<para>Here is short example consisting of a class and a declaration
|
||
in the web application context.</para>
|
||
|
||
<programlisting language="java"><![CDATA[package samples;
|
||
|
||
import javax.portlet.RenderRequest;
|
||
import javax.portlet.RenderResponse;
|
||
|
||
import org.springframework.web.portlet.mvc.AbstractController;
|
||
import org.springframework.web.portlet.ModelAndView;
|
||
|
||
public class SampleController extends AbstractController {
|
||
|
||
public ModelAndView handleRenderRequestInternal(RenderRequest request, RenderResponse response) {
|
||
ModelAndView mav = new ModelAndView("foo");
|
||
mav.addObject("message", "Hello World!");
|
||
return mav;
|
||
}
|
||
}
|
||
|
||
<bean id="sampleController" class="samples.SampleController">
|
||
<property name="cacheSeconds" value="120"/>
|
||
</bean>]]></programlisting>
|
||
|
||
<para>The class above and the declaration in the web application
|
||
context is all you need besides setting up a handler mapping (see
|
||
<xref linkend="portlet-handlermapping" />) to get this very simple
|
||
controller working.</para>
|
||
</section>
|
||
|
||
<section id="portlet-controller-simple">
|
||
<title>Other simple controllers</title>
|
||
|
||
<para>Although you can extend <classname>AbstractController</classname>,
|
||
Spring Portlet MVC provides a number of concrete implementations which offer
|
||
functionality that is commonly used in simple MVC applications.</para>
|
||
|
||
<para>The <classname>ParameterizableViewController</classname> is
|
||
basically the same as the example above, except for the fact that
|
||
you can specify the view name that it will return in the web
|
||
application context (no need to hard-code the view name).</para>
|
||
|
||
<para>The <classname>PortletModeNameViewController</classname> uses
|
||
the current mode of the portlet as the view name. So, if your
|
||
portlet is in View mode (i.e. <literal>PortletMode.VIEW</literal>)
|
||
then it uses "view" as the view name.</para>
|
||
</section>
|
||
|
||
<section id="portlet-controller-command">
|
||
<title>Command Controllers</title>
|
||
|
||
<para>Spring Portlet MVC has the exact same hierarchy of
|
||
<emphasis>command controllers</emphasis> as Spring Web MVC. They
|
||
provide a way to interact with data objects and dynamically bind
|
||
parameters from the <interfacename>PortletRequest</interfacename> to
|
||
the data object specified. Your data objects don't have to
|
||
implement a framework-specific interface, so you can directly
|
||
manipulate your persistent objects if you desire. Let's examine what
|
||
command controllers are available, to get an overview of what you can do
|
||
with them:</para>
|
||
|
||
<itemizedlist>
|
||
<listitem><para><classname>AbstractCommandController</classname>
|
||
- a command controller you can use to create your own command
|
||
controller, capable of binding request parameters to a data
|
||
object you specify. This class does not offer form
|
||
functionality, it does however offer validation features and
|
||
lets you specify in the controller itself what to do with the
|
||
command object that has been filled with the parameters from the
|
||
request.</para></listitem>
|
||
|
||
<listitem><para><classname>AbstractFormController</classname> -
|
||
an abstract controller offering form submission support. Using
|
||
this controller you can model forms and populate them using a
|
||
command object you retrieve in the controller. After a user has
|
||
filled the form, <classname>AbstractFormController</classname>
|
||
binds the fields, validates, and hands the object back to the
|
||
controller to take appropriate action. Supported features are:
|
||
invalid form submission (resubmission), validation, and normal
|
||
form workflow. You implement methods to determine which views
|
||
are used for form presentation and success. Use this controller
|
||
if you need forms, but don't want to specify what views you're
|
||
going to show the user in the application
|
||
context.</para></listitem>
|
||
|
||
<listitem><para><classname>SimpleFormController</classname> - a
|
||
concrete <classname>AbstractFormController</classname> that
|
||
provides even more support when creating a form with a
|
||
corresponding command object. The
|
||
<classname>SimpleFormController</classname> lets you specify a
|
||
command object, a viewname for the form, a viewname for the page you
|
||
want to show the user when form submission has succeeded, and
|
||
more.</para></listitem>
|
||
|
||
<listitem><para><classname>AbstractWizardFormController</classname> –
|
||
a concrete <classname>AbstractFormController</classname> that
|
||
provides a wizard-style interface for editing the contents of a
|
||
command object across multiple display pages. Supports multiple
|
||
user actions: finish, cancel, or page change, all of which are
|
||
easily specified in request parameters from the
|
||
view.</para></listitem>
|
||
</itemizedlist>
|
||
|
||
<para>These command controllers are quite powerful, but they do
|
||
require a detailed understanding of how they operate in order to use
|
||
them efficiently. Carefully review the Javadocs for this entire
|
||
hierarchy and then look at some sample implementations before you
|
||
start using them.</para>
|
||
</section>
|
||
|
||
<section id="portlet-controller-wrapping">
|
||
<title><classname>PortletWrappingController</classname></title>
|
||
|
||
<para>Instead of developing new controllers, it is possible to use
|
||
existing portlets and map requests to them from a
|
||
<classname>DispatcherPortlet</classname>. Using the
|
||
<classname>PortletWrappingController</classname>, you can
|
||
instantiate an existing <interfacename>Portlet</interfacename> as a
|
||
<interfacename>Controller</interfacename> as follows:</para>
|
||
|
||
<programlisting language="xml"><![CDATA[<bean id="myPortlet" class="org.springframework.web.portlet.mvc.PortletWrappingController">
|
||
<property name="portletClass" value="sample.MyPortlet"/>
|
||
<property name="portletName" value="my-portlet"/>
|
||
<property name="initParameters">
|
||
<value>config=/WEB-INF/my-portlet-config.xml</value>
|
||
</property>
|
||
</bean>]]></programlisting>
|
||
|
||
<para>This can be very valuable since you can then use interceptors
|
||
to pre-process and post-process requests going to these portlets.
|
||
Since JSR-168 does not support any kind of filter mechanism, this is
|
||
quite handy. For example, this can be used to wrap the Hibernate
|
||
<classname>OpenSessionInViewInterceptor</classname> around a MyFaces
|
||
JSF Portlet.</para>
|
||
</section>
|
||
</section>
|
||
|
||
<section id="portlet-handlermapping">
|
||
<title>Handler mappings</title>
|
||
|
||
<para>Using a handler mapping you can map incoming portlet requests to
|
||
appropriate handlers. There are some handler mappings you can use out
|
||
of the box, for example, the
|
||
<classname>PortletModeHandlerMapping</classname>, but let's first
|
||
examine the general concept of a
|
||
<interfacename>HandlerMapping</interfacename>.</para>
|
||
|
||
<para>Note: We are intentionally using the term “Handler” here instead
|
||
of “Controller”. <classname>DispatcherPortlet</classname> is designed
|
||
to be used with other ways to process requests than just Spring Portlet
|
||
MVC’s own Controllers. A Handler is any Object that can handle portlet
|
||
requests. Controllers are an example of Handlers, and they are of
|
||
course the default. To use some other framework with
|
||
<classname>DispatcherPortlet</classname>, a corresponding implementation
|
||
of <interfacename>HandlerAdapter</interfacename> is all that is needed.</para>
|
||
|
||
<para>The functionality a basic
|
||
<interfacename>HandlerMapping</interfacename> provides is the delivering
|
||
of a <classname>HandlerExecutionChain</classname>, which must contain
|
||
the handler that matches the incoming request, and may also contain a
|
||
list of handler interceptors that are applied to the request. When a
|
||
request comes in, the <classname>DispatcherPortlet</classname> will hand
|
||
it over to the handler mapping to let it inspect the request and come up
|
||
with an appropriate <classname>HandlerExecutionChain</classname>. Then
|
||
the <classname>DispatcherPortlet</classname> will execute the handler
|
||
and interceptors in the chain (if any). These concepts are all exactly
|
||
the same as in Spring Web MVC.</para>
|
||
|
||
<para>The concept of configurable handler mappings that can optionally
|
||
contain interceptors (executed before or after the actual handler was
|
||
executed, or both) is extremely powerful. A lot of supporting
|
||
functionality can be built into a custom
|
||
<interfacename>HandlerMapping</interfacename>. Think of a custom handler
|
||
mapping that chooses a handler not only based on the portlet mode of the
|
||
request coming in, but also on a specific state of the session
|
||
associated with the request.</para>
|
||
|
||
<para>In Spring Web MVC, handler mappings are commonly based on URLs.
|
||
Since there is really no such thing as a URL within a Portlet, we must
|
||
use other mechanisms to control mappings. The two most common are the
|
||
portlet mode and a request parameter, but anything available to the
|
||
portlet request can be used in a custom handler mapping.</para>
|
||
|
||
<para>The rest of this section describes three of Spring Portlet MVC's
|
||
most commonly used handler mappings. They all extend
|
||
<classname>AbstractHandlerMapping</classname> and share the following
|
||
properties:</para>
|
||
|
||
<itemizedlist>
|
||
<listitem><para><literal>interceptors</literal>: The list of
|
||
interceptors to use.
|
||
<interfacename>HandlerInterceptor</interfacename>s are discussed in
|
||
<xref linkend="portlet-handlermapping-interceptor"/>.</para></listitem>
|
||
|
||
<listitem><para><literal>defaultHandler</literal>: The default
|
||
handler to use, when this handler mapping does not result in a
|
||
matching handler.</para></listitem>
|
||
|
||
<listitem><para><literal>order</literal>: Based on the value of the
|
||
order property (see the
|
||
<interfacename>org.springframework.core.Ordered</interfacename>
|
||
interface), Spring will sort all handler mappings available in the
|
||
context and apply the first matching handler.</para></listitem>
|
||
|
||
<listitem><para><literal>lazyInitHandlers</literal>: Allows for lazy
|
||
initialization of singleton handlers (prototype handlers are always
|
||
lazily initialized). Default value is false. This property is
|
||
directly implemented in the three concrete
|
||
Handlers.</para></listitem>
|
||
</itemizedlist>
|
||
|
||
<section id="portlet-handlermapping-portletmode">
|
||
<title><classname>PortletModeHandlerMapping</classname></title>
|
||
|
||
<para>This is a simple handler mapping that maps incoming requests
|
||
based on the current mode of the portlet (e.g. ‘view’, ‘edit’,
|
||
‘help’). An example:</para>
|
||
|
||
<programlisting language="xml"><![CDATA[<bean class="org.springframework.web.portlet.handler.PortletModeHandlerMapping">
|
||
<property name="portletModeMap">
|
||
<map>
|
||
<entry key="view" value-ref="viewHandler"/>
|
||
<entry key="edit" value-ref="editHandler"/>
|
||
<entry key="help" value-ref="helpHandler"/>
|
||
</map>
|
||
</property>
|
||
</bean>]]></programlisting>
|
||
</section>
|
||
|
||
<section id="portlet-handlermapping-parameter">
|
||
<title><classname>ParameterHandlerMapping</classname></title>
|
||
|
||
<para>If we need to navigate around to multiple controllers without
|
||
changing portlet mode, the simplest way to do this is with a request
|
||
parameter that is used as the key to control the mapping.</para>
|
||
|
||
<para><classname>ParameterHandlerMapping</classname> uses the value
|
||
of a specific request parameter to control the mapping. The default
|
||
name of the parameter is <literal>'action'</literal>, but can be changed
|
||
using the <literal>'parameterName'</literal> property.</para>
|
||
|
||
<para>The bean configuration for this mapping will look something
|
||
like this:</para>
|
||
|
||
<programlisting language="xml"><![CDATA[<bean class="org.springframework.web.portlet.handler.ParameterHandlerMapping”>
|
||
<property name="parameterMap">
|
||
<map>
|
||
<entry key="add" value-ref="addItemHandler"/>
|
||
<entry key="edit" value-ref="editItemHandler"/>
|
||
<entry key="delete" value-ref="deleteItemHandler"/>
|
||
</map>
|
||
</property>
|
||
</bean>]]></programlisting>
|
||
</section>
|
||
|
||
<section id="portlet-handlermapping-portletmodeparameter">
|
||
<title><classname>PortletModeParameterHandlerMapping</classname></title>
|
||
|
||
<para>The most powerful built-in handler mapping,
|
||
<classname>PortletModeParameterHandlerMapping</classname> combines
|
||
the capabilities of the two previous ones to allow different
|
||
navigation within each portlet mode.</para>
|
||
|
||
<para>Again the default name of the parameter is "action", but can
|
||
be changed using the <literal>parameterName</literal>
|
||
property.</para>
|
||
|
||
<para>By default, the same parameter value may not be used in two
|
||
different portlet modes. This is so that if the portal itself
|
||
changes the portlet mode, the request will no longer be valid in the
|
||
mapping. This behavior can be changed by setting the
|
||
<literal>allowDupParameters</literal> property to true. However,
|
||
this is not recommended.</para>
|
||
|
||
<para>The bean configuration for this mapping will look something
|
||
like this:</para>
|
||
|
||
<programlisting language="xml"><![CDATA[<bean class="org.springframework.web.portlet.handler.PortletModeParameterHandlerMapping">
|
||
<property name="portletModeParameterMap">
|
||
<map>
|
||
<entry key="view"> ]]><lineannotation><!-- 'view' portlet mode --></lineannotation><![CDATA[
|
||
<map>
|
||
<entry key="add" value-ref="addItemHandler"/>
|
||
<entry key="edit" value-ref="editItemHandler"/>
|
||
<entry key="delete" value-ref="deleteItemHandler"/>
|
||
</map>
|
||
</entry>
|
||
<entry key="edit"> ]]><lineannotation><!-- 'edit' portlet mode --></lineannotation><![CDATA[
|
||
<map>
|
||
<entry key="prefs" value-ref="prefsHandler"/>
|
||
<entry key="resetPrefs" value-ref="resetPrefsHandler"/>
|
||
</map>
|
||
</entry>
|
||
</map>
|
||
</property>
|
||
</bean>]]></programlisting>
|
||
|
||
<para>This mapping can be chained ahead of a
|
||
<classname>PortletModeHandlerMapping</classname>, which can then provide
|
||
defaults for each mode and an overall default as well.</para>
|
||
</section>
|
||
|
||
<section id="portlet-handlermapping-interceptor">
|
||
<title>Adding <interfacename>HandlerInterceptor</interfacename>s</title>
|
||
|
||
<para>Spring's handler mapping mechanism has a notion of handler
|
||
interceptors, which can be extremely useful when you want to apply
|
||
specific functionality to certain requests, for example, checking
|
||
for a principal. Again Spring Portlet MVC implements these concepts
|
||
in the same way as Web MVC.</para>
|
||
|
||
<para>Interceptors located in the handler mapping must implement
|
||
<interfacename>HandlerInterceptor</interfacename> from the
|
||
<literal>org.springframework.web.portlet</literal> package. Just
|
||
like the servlet version, this interface defines three methods: one
|
||
that will be called before the actual handler will be executed
|
||
(<literal>preHandle</literal>), one that will be called after the
|
||
handler is executed (<literal>postHandle</literal>), and one that is
|
||
called after the complete request has finished
|
||
(<literal>afterCompletion</literal>). These three methods should
|
||
provide enough flexibility to do all kinds of pre- and post-
|
||
processing.</para>
|
||
|
||
<para>The <literal>preHandle</literal> method returns a boolean
|
||
value. You can use this method to break or continue the processing
|
||
of the execution chain. When this method returns
|
||
<literal>true</literal>, the handler execution chain will continue.
|
||
When it returns <literal>false</literal>, the
|
||
<classname>DispatcherPortlet</classname> assumes the interceptor
|
||
itself has taken care of requests (and, for example, rendered an
|
||
appropriate view) and does not continue executing the other
|
||
interceptors and the actual handler in the execution chain.</para>
|
||
|
||
<para>The <literal>postHandle</literal> method is only called on a
|
||
<interfacename>RenderRequest</interfacename>. The
|
||
<literal>preHandle</literal> and <literal>afterCompletion</literal>
|
||
methods are called on both an
|
||
<interfacename>ActionRequest</interfacename> and a
|
||
<interfacename>RenderRequest</interfacename>. If you need to
|
||
execute logic in these methods for just one type of request, be sure
|
||
to check what kind of request it is before processing it.</para>
|
||
</section>
|
||
|
||
<section id="portlet-handlermapping-interceptoradapter">
|
||
<title><classname>HandlerInterceptorAdapter</classname></title>
|
||
|
||
<para>As with the servlet package, the portlet package has a
|
||
concrete implementation of
|
||
<interfacename>HandlerInterceptor</interfacename> called
|
||
<classname>HandlerInterceptorAdapter</classname>. This class has
|
||
empty versions of all the methods so that you can inherit from this
|
||
class and implement just one or two methods when that is all you
|
||
need.</para>
|
||
|
||
</section>
|
||
|
||
<section id="portlet-handlermapping-parameterinterceptor">
|
||
<title><classname>ParameterMappingInterceptor</classname></title>
|
||
|
||
<para>The portlet package also has a concrete interceptor named
|
||
<classname>ParameterMappingInterceptor</classname> that is meant to
|
||
be used directly with <classname>ParameterHandlerMapping</classname>
|
||
and <classname>PortletModeParameterHandlerMapping</classname>. This
|
||
interceptor will cause the parameter that is being used to control
|
||
the mapping to be forwarded from an
|
||
<interfacename>ActionRequest</interfacename> to the subsequent
|
||
<interfacename>RenderRequest</interfacename>. This will help ensure
|
||
that the <interfacename>RenderRequest</interfacename> is mapped to
|
||
the same Handler as the
|
||
<interfacename>ActionRequest</interfacename>. This is done in the
|
||
<literal>preHandle</literal> method of the interceptor, so you can
|
||
still modify the parameter value in your handler to change where the
|
||
<interfacename>RenderRequest</interfacename> will be mapped.</para>
|
||
|
||
<para>Be aware that this interceptor is calling
|
||
<literal>setRenderParameter</literal> on the
|
||
<interfacename>ActionResponse</interfacename>, which means that you
|
||
cannot call <literal>sendRedirect</literal> in your handler when
|
||
using this interceptor. If you need to do external redirects then
|
||
you will either need to forward the mapping parameter manually or
|
||
write a different interceptor to handle this for you.</para>
|
||
</section>
|
||
</section>
|
||
|
||
<section id="portlet-viewresolver">
|
||
<title>Views and resolving them</title>
|
||
|
||
<para>As mentioned previously, Spring Portlet MVC directly reuses all
|
||
the view technologies from Spring Web MVC. This includes not only the
|
||
various <interfacename>View</interfacename> implementations themselves,
|
||
but also the <interfacename>ViewResolver</interfacename> implementations.
|
||
For more information, refer to <xref linkend="view"/> and
|
||
<xref linkend="mvc-viewresolver"/> respectively.</para>
|
||
|
||
<para>A few items on using the existing <interfacename>View</interfacename> and
|
||
<interfacename>ViewResolver</interfacename> implementations are worth mentioning:</para>
|
||
|
||
<itemizedlist>
|
||
<listitem><para>Most portals expect the result of rendering a
|
||
portlet to be an HTML fragment. So, things like JSP/JSTL, Velocity,
|
||
FreeMarker, and XSLT all make sense. But it is unlikely that views
|
||
that return other document types will make any sense in a portlet
|
||
context.</para></listitem>
|
||
|
||
<listitem><para>There is no such thing as an HTTP redirect from
|
||
within a portlet (the <literal>sendRedirect(..)</literal> method of
|
||
<interfacename>ActionResponse</interfacename> cannot
|
||
be used to stay within the portal). So, <classname>RedirectView</classname>
|
||
and use of the <literal>'redirect:'</literal> prefix will
|
||
<emphasis role="bold">not</emphasis> work correctly from within Portlet MVC.</para></listitem>
|
||
|
||
<listitem><para>It may be possible to use the <literal>'forward:'</literal> prefix from
|
||
within Portlet MVC. However, remember that since you are in a
|
||
portlet, you have no idea what the current URL looks like. This
|
||
means you cannot use a relative URL to access other resources in
|
||
your web application and that you will have to use an absolute
|
||
URL.</para></listitem>
|
||
</itemizedlist>
|
||
|
||
<para>Also, for JSP development, the new Spring Taglib and the new
|
||
Spring Form Taglib both work in portlet views in exactly the same way
|
||
that they work in servlet views.</para>
|
||
</section>
|
||
|
||
<section id="portlet-multipart">
|
||
<title>Multipart (file upload) support</title>
|
||
|
||
<para>Spring Portlet MVC has built-in multipart support to handle file
|
||
uploads in portlet applications, just like Web MVC does. The design for
|
||
the multipart support is done with pluggable
|
||
<interfacename>PortletMultipartResolver</interfacename> objects, defined
|
||
in the <literal>org.springframework.web.portlet.multipart</literal>
|
||
package. Spring provides a <interfacename>PortletMultipartResolver</interfacename>
|
||
for use with
|
||
<ulink url="http://jakarta.apache.org/commons/fileupload">Commons FileUpload</ulink>.
|
||
How uploading files is supported will be described in the rest of this section.</para>
|
||
|
||
<para>By default, no multipart handling will be done by Spring Portlet
|
||
MVC, as some developers will want to handle multiparts themselves. You
|
||
will have to enable it yourself by adding a multipart resolver to the
|
||
web application's context. After you have done that,
|
||
<classname>DispatcherPortlet</classname> will inspect each request to
|
||
see if it contains a multipart. If no multipart is found, the request
|
||
will continue as expected. However, if a multipart is found in the
|
||
request, the <interfacename>PortletMultipartResolver</interfacename>
|
||
that has been declared in your context will be used. After that, the
|
||
multipart attribute in your request will be treated like any other
|
||
attribute.</para>
|
||
|
||
<note>
|
||
<para>Any configured <interfacename>PortletMultipartResolver</interfacename>
|
||
bean <emphasis>must</emphasis> have the following id (or name):
|
||
"<literal>portletMultipartResolver</literal>". If you have defined your
|
||
<interfacename>PortletMultipartResolver</interfacename> with any other name,
|
||
then the <classname>DispatcherPortlet</classname> will <emphasis>not</emphasis>
|
||
find your <interfacename>PortletMultipartResolver</interfacename>, and
|
||
consequently no multipart support will be in effect.</para>
|
||
</note>
|
||
|
||
<section id="portlet-multipart-resolver">
|
||
<title>Using the <interfacename>PortletMultipartResolver</interfacename></title>
|
||
|
||
<para>The following example shows how to use the
|
||
<classname>CommonsPortletMultipartResolver</classname>:</para>
|
||
|
||
<programlisting language="xml"><![CDATA[<bean id="portletMultipartResolver"
|
||
class="org.springframework.web.portlet.multipart.CommonsPortletMultipartResolver">
|
||
|
||
]]><lineannotation><!-- one of the properties available; the maximum file size in bytes --></lineannotation><![CDATA[
|
||
<property name="maxUploadSize" value="100000"/>
|
||
</bean>]]></programlisting>
|
||
|
||
<para>Of course you also need to put the appropriate jars in your
|
||
classpath for the multipart resolver to work. In the case of the
|
||
<classname>CommonsMultipartResolver</classname>, you need to use
|
||
<literal>commons-fileupload.jar</literal>. Be sure to use at least
|
||
version 1.1 of Commons FileUpload as previous versions do not
|
||
support JSR-168 Portlet applications.</para>
|
||
|
||
<para>Now that you have seen how to set Portlet MVC up to handle
|
||
multipart requests, let's talk about how to actually use it. When
|
||
<classname>DispatcherPortlet</classname> detects a multipart
|
||
request, it activates the resolver that has been declared in your
|
||
context and hands over the request. What the resolver then does is
|
||
wrap the current <interfacename>ActionRequest</interfacename> in a
|
||
<interfacename>MultipartActionRequest</interfacename> that has
|
||
support for multipart file uploads. Using the
|
||
<interfacename>MultipartActionRequest</interfacename> you can get
|
||
information about the multiparts contained by this request and
|
||
actually get access to the multipart files themselves in your
|
||
controllers.</para>
|
||
|
||
<para>Note that you can only receive multipart file uploads as part
|
||
of an <interfacename>ActionRequest</interfacename>, not as part of a
|
||
<interfacename>RenderRequest</interfacename>.</para>
|
||
</section>
|
||
|
||
<section id="portlet-multipart-forms">
|
||
<title>Handling a file upload in a form</title>
|
||
|
||
<para>After the
|
||
<interfacename>PortletMultipartResolver</interfacename> has finished
|
||
doing its job, the request will be processed like any other. To use
|
||
the <interfacename>PortletMultipartResolver</interfacename>, create
|
||
a form with an upload field (see example below),
|
||
then let Spring bind the file onto your form (backing object). To
|
||
actually let the user upload a file, we have to create a (JSP/HTML)
|
||
form:</para>
|
||
|
||
<programlisting language="xml"><![CDATA[<h1>Please upload a file</h1>
|
||
<form method="post" action="<portlet:actionURL/>" enctype="multipart/form-data">
|
||
<input type="file" name="file"/>
|
||
<input type="submit"/>
|
||
</form>]]></programlisting>
|
||
|
||
<para>As you can see, we've created a field named “file” that matches the
|
||
property of the bean that holds the <literal>byte[]</literal> array.
|
||
Furthermore we've added the encoding attribute
|
||
(<literal>enctype="multipart/form-data"</literal>), which is
|
||
necessary to let the browser know how to encode the multipart fields
|
||
(do not forget this!).</para>
|
||
|
||
<para>Just as with any other property that's not automagically
|
||
convertible to a string or primitive type, to be able to put binary
|
||
data in your objects you have to register a custom editor with the
|
||
<classname>PortletRequestDataBinder</classname>. There are a couple
|
||
of editors available for handling files and setting the results on
|
||
an object. There's a
|
||
<classname>StringMultipartFileEditor</classname> capable of
|
||
converting files to Strings (using a user-defined character set), and
|
||
there is a <classname>ByteArrayMultipartFileEditor</classname> which
|
||
converts files to byte arrays. They function analogous to the
|
||
<classname>CustomDateEditor</classname>.</para>
|
||
|
||
<para>So, to be able to upload files using a form, declare the
|
||
resolver, a mapping to a controller that will process the bean, and
|
||
the controller itself.</para>
|
||
|
||
<programlisting language="xml"><![CDATA[<bean id="portletMultipartResolver"
|
||
class="org.springframework.web.portlet.multipart.CommonsPortletMultipartResolver"/>
|
||
|
||
<bean class="org.springframework.web.portlet.handler.PortletModeHandlerMapping">
|
||
<property name="portletModeMap">
|
||
<map>
|
||
<entry key="view" value-ref="fileUploadController"/>
|
||
</map>
|
||
</property>
|
||
</bean>
|
||
|
||
<bean id="fileUploadController" class="examples.FileUploadController">
|
||
<property name="commandClass" value="examples.FileUploadBean"/>
|
||
<property name="formView" value="fileuploadform"/>
|
||
<property name="successView" value="confirmation"/>
|
||
</bean>]]></programlisting>
|
||
|
||
<para>After that, create the controller and the actual class to hold
|
||
the file property.</para>
|
||
|
||
<programlisting language="java"><![CDATA[public class FileUploadController extends SimpleFormController {
|
||
|
||
public void onSubmitAction(ActionRequest request, ActionResponse response,
|
||
Object command, BindException errors) throws Exception {
|
||
|
||
]]><lineannotation>// cast the bean</lineannotation><![CDATA[
|
||
FileUploadBean bean = (FileUploadBean) command;
|
||
|
||
]]><lineannotation>// let's see if there's content there</lineannotation><![CDATA[
|
||
byte[] file = bean.getFile();
|
||
if (file == null) {
|
||
]]><lineannotation>// hmm, that's strange, the user did not upload anything</lineannotation><![CDATA[
|
||
}
|
||
|
||
// do something with the file here
|
||
}
|
||
|
||
protected void initBinder(
|
||
PortletRequest request, PortletRequestDataBinder binder) throws Exception {
|
||
// to actually be able to convert Multipart instance to byte[]
|
||
// we have to register a custom editor
|
||
binder.registerCustomEditor(byte[].class, new ByteArrayMultipartFileEditor());
|
||
]]><lineannotation>// now Spring knows how to handle multipart object and convert</lineannotation><![CDATA[
|
||
}
|
||
}
|
||
|
||
public class FileUploadBean {
|
||
|
||
private byte[] file;
|
||
|
||
public void setFile(byte[] file) {
|
||
this.file = file;
|
||
}
|
||
|
||
public byte[] getFile() {
|
||
return file;
|
||
}
|
||
}]]></programlisting>
|
||
|
||
<para>As you can see, the <classname>FileUploadBean</classname> has
|
||
a property of type <literal>byte[]</literal> that holds the file. The
|
||
controller registers a custom editor to let Spring know how to
|
||
actually convert the multipart objects the resolver has found to
|
||
properties specified by the bean. In this example, nothing is done
|
||
with the <literal>byte[]</literal> property of the bean itself, but
|
||
in practice you can do whatever you want (save it in a database,
|
||
mail it to somebody, etc).</para>
|
||
|
||
<para>An equivalent example in which a file is bound straight to a
|
||
String-typed property on a form backing object might look like
|
||
this:</para>
|
||
|
||
<programlisting language="java"><![CDATA[public class FileUploadController extends SimpleFormController {
|
||
|
||
public void onSubmitAction(ActionRequest request, ActionResponse response,
|
||
Object command, BindException errors) throws Exception {
|
||
|
||
// cast the bean
|
||
FileUploadBean bean = (FileUploadBean) command;
|
||
|
||
// let's see if there's content there
|
||
String file = bean.getFile();
|
||
if (file == null) {
|
||
// hmm, that's strange, the user did not upload anything
|
||
}
|
||
|
||
// do something with the file here
|
||
}
|
||
|
||
protected void initBinder(
|
||
PortletRequest request, PortletRequestDataBinder binder) throws Exception {
|
||
|
||
// to actually be able to convert Multipart instance to a String
|
||
// we have to register a custom editor
|
||
binder.registerCustomEditor(String.class,
|
||
new StringMultipartFileEditor());
|
||
// now Spring knows how to handle multipart objects and convert
|
||
}
|
||
}
|
||
|
||
public class FileUploadBean {
|
||
|
||
private String file;
|
||
|
||
public void setFile(String file) {
|
||
this.file = file;
|
||
}
|
||
|
||
public String getFile() {
|
||
return file;
|
||
}
|
||
}]]></programlisting>
|
||
|
||
<para>Of course, this last example only makes (logical) sense in the
|
||
context of uploading a plain text file (it wouldn't work so well in
|
||
the case of uploading an image file).</para>
|
||
|
||
<para>The third (and final) option is where one binds directly to a
|
||
<interfacename>MultipartFile</interfacename> property declared on
|
||
the (form backing) object's class. In this case one does not need to
|
||
register any custom property editor because there is no type
|
||
conversion to be performed.</para>
|
||
|
||
<programlisting language="java"><![CDATA[public class FileUploadController extends SimpleFormController {
|
||
|
||
public void onSubmitAction(ActionRequest request, ActionResponse response,
|
||
Object command, BindException errors) throws Exception {
|
||
|
||
// cast the bean
|
||
FileUploadBean bean = (FileUploadBean) command;
|
||
|
||
// let's see if there's content there
|
||
MultipartFile file = bean.getFile();
|
||
if (file == null) {
|
||
// hmm, that's strange, the user did not upload anything
|
||
}
|
||
|
||
// do something with the file here
|
||
}
|
||
}
|
||
|
||
public class FileUploadBean {
|
||
|
||
private MultipartFile file;
|
||
|
||
public void setFile(MultipartFile file) {
|
||
this.file = file;
|
||
}
|
||
|
||
public MultipartFile getFile() {
|
||
return file;
|
||
}
|
||
}]]></programlisting>
|
||
</section>
|
||
</section>
|
||
|
||
<section id="portlet-exceptionresolver">
|
||
<title>Handling exceptions</title>
|
||
|
||
<para>Just like Servlet MVC, Portlet MVC provides
|
||
<interfacename>HandlerExceptionResolver</interfacename>s to ease the
|
||
pain of unexpected exceptions that occur while your request is being
|
||
processed by a handler that matched the request. Portlet MVC also
|
||
provides a portlet-specific, concrete
|
||
<classname>SimpleMappingExceptionResolver</classname> that enables you
|
||
to take the class name of any exception that might be thrown and map it
|
||
to a view name.</para>
|
||
</section>
|
||
|
||
<section id="portlet-annotation">
|
||
<title>Annotation-based controller configuration</title>
|
||
|
||
<para>Spring 2.5 introduced an annotation-based programming model for MVC
|
||
controllers, using annotations such as
|
||
<interfacename>@RequestMapping</interfacename>,
|
||
<interfacename>@RequestParam</interfacename>,
|
||
<interfacename>@ModelAttribute</interfacename>, etc. This annotation
|
||
support is available for both Servlet MVC and Portlet MVC. Controllers
|
||
implemented in this style do not have to extend specific base classes or
|
||
implement specific interfaces. Furthermore, they do not usually have
|
||
direct dependencies on Servlet or Portlet API's, although they can easily
|
||
get access to Servlet or Portlet facilities if desired.</para>
|
||
|
||
<!--
|
||
PetPortal is no longer included with the Spring distribution as of
|
||
Spring 3.0.0.RC1. Thus, the following is commented out until further
|
||
notice.
|
||
-->
|
||
<!--
|
||
<tip>
|
||
<para>The Spring distribution ships with the
|
||
<emphasis>PetPortal</emphasis> sample, which is a portal application that takes
|
||
advantage of the annotation support described in this section, in the context
|
||
of simple form processing. You can find the <emphasis>PetPortal</emphasis>
|
||
application in the <literal>'samples/petportal'</literal> directory.</para>
|
||
</tip>
|
||
-->
|
||
|
||
<para>The following sections document these annotations and how they are
|
||
most commonly used in a Portlet environment.</para>
|
||
|
||
<section id="portlet-ann-setup">
|
||
<title>Setting up the dispatcher for annotation support</title>
|
||
|
||
<para><emphasis><interfacename>@RequestMapping</interfacename> will only be processed
|
||
if a corresponding <interfacename>HandlerMapping</interfacename> (for type level annotations)
|
||
and/or <interfacename>HandlerAdapter</interfacename> (for method level annotations) is
|
||
present in the dispatcher.</emphasis> This is the case by default in both
|
||
<classname>DispatcherServlet</classname> and <classname>DispatcherPortlet</classname>.</para>
|
||
|
||
<para>However, if you are defining custom <interfacename>HandlerMappings</interfacename> or
|
||
<interfacename>HandlerAdapters</interfacename>, then you need to make sure that a
|
||
corresponding custom <classname>DefaultAnnotationHandlerMapping</classname>
|
||
and/or <classname>AnnotationMethodHandlerAdapter</classname> is defined as well
|
||
- provided that you intend to use <interfacename>@RequestMapping</interfacename>.</para>
|
||
|
||
<programlisting language="xml"><?xml version="1.0" encoding="UTF-8"?>
|
||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
|
||
|
||
<bean class="org.springframework.web.portlet.mvc.annotation.DefaultAnnotationHandlerMapping"/>
|
||
|
||
<bean class="org.springframework.web.portlet.mvc.annotation.AnnotationMethodHandlerAdapter"/>
|
||
|
||
<lineannotation>// ... (controller bean definitions) ...</lineannotation>
|
||
|
||
</beans>
|
||
</programlisting>
|
||
|
||
<para>Defining a <classname>DefaultAnnotationHandlerMapping</classname>
|
||
and/or <classname>AnnotationMethodHandlerAdapter</classname> explicitly
|
||
also makes sense if you would like to customize the mapping strategy, e.g.
|
||
specifying a custom <interfacename>WebBindingInitializer</interfacename> (see below).</para>
|
||
</section>
|
||
|
||
<section id="portlet-ann-controller">
|
||
<title>Defining a controller with
|
||
<interfacename>@Controller</interfacename></title>
|
||
|
||
<para>The <interfacename>@Controller</interfacename> annotation indicates
|
||
that a particular class serves the role of a <emphasis>controller</emphasis>.
|
||
There is no need to extend any controller base class or reference the
|
||
Portlet API. You are of course still able to reference Portlet-specific
|
||
features if you need to.</para>
|
||
|
||
<para>The basic purpose of the <interfacename>@Controller</interfacename>
|
||
annotation is to act as a stereotype for the annotated class, indicating
|
||
its role. The dispatcher will scan such annotated classes for mapped
|
||
methods, detecting <interfacename>@RequestMapping</interfacename>
|
||
annotations (see the next section).</para>
|
||
|
||
<para>Annotated controller beans may be defined explicitly,
|
||
using a standard Spring bean definition in the dispatcher's context.
|
||
However, the <interfacename>@Controller</interfacename> stereotype also
|
||
allows for autodetection, aligned with Spring 2.5's general support for
|
||
detecting component classes in the classpath and auto-registering bean
|
||
definitions for them.</para>
|
||
|
||
<para>To enable autodetection of such annotated controllers, you have to add
|
||
component scanning to your configuration. This is easily achieved by using
|
||
the <emphasis>spring-context</emphasis> schema as shown in the following
|
||
XML snippet:</para>
|
||
|
||
<programlisting language="xml"><?xml version="1.0" encoding="UTF-8"?>
|
||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||
xmlns:p="http://www.springframework.org/schema/p"
|
||
xmlns:context="http://www.springframework.org/schema/context"
|
||
xsi:schemaLocation="
|
||
http://www.springframework.org/schema/beans
|
||
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
|
||
http://www.springframework.org/schema/context
|
||
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
|
||
|
||
<context:component-scan base-package="org.springframework.samples.petportal.portlet"/>
|
||
|
||
<lineannotation>// ...</lineannotation>
|
||
|
||
</beans>
|
||
</programlisting>
|
||
</section>
|
||
|
||
<section id="portlet-ann-requestmapping">
|
||
<title>Mapping requests with
|
||
<interfacename>@RequestMapping</interfacename></title>
|
||
|
||
<para>The <interfacename>@RequestMapping</interfacename> annotation is used
|
||
to map portlet modes like 'VIEW'/'EDIT' onto an entire class or a particular
|
||
handler method. Typically the type-level annotation maps a specific mode
|
||
(or mode plus parameter condition) onto a form controller, with additional
|
||
method-level annotations 'narrowing' the primary mapping for specific
|
||
portlet request parameters.</para>
|
||
|
||
<tip>
|
||
<para><interfacename>@RequestMapping</interfacename> at the type
|
||
level may be used for plain implementations of the
|
||
<interfacename>Controller</interfacename> interface as well.
|
||
In this case, the request processing code would follow the
|
||
traditional <literal>handle(Action|Render)Request</literal> signature,
|
||
while the controller's mapping would be expressed through an
|
||
<interfacename>@RequestMapping</interfacename> annotation.
|
||
This works for pre-built <interfacename>Controller</interfacename>
|
||
base classes, such as <classname>SimpleFormController</classname>,
|
||
too.</para>
|
||
|
||
<para>In the following discussion, we'll focus on controllers
|
||
that are based on annotated handler methods.</para>
|
||
</tip>
|
||
|
||
<para>The following is an example of a form controller from the
|
||
PetPortal sample application using this annotation:</para>
|
||
|
||
<programlisting language="java">@Controller
|
||
<emphasis role="bold">@RequestMapping("EDIT")</emphasis>
|
||
@SessionAttributes("site")
|
||
public class PetSitesEditController {
|
||
|
||
private Properties petSites;
|
||
|
||
public void setPetSites(Properties petSites) {
|
||
this.petSites = petSites;
|
||
}
|
||
|
||
@ModelAttribute("petSites")
|
||
public Properties getPetSites() {
|
||
return this.petSites;
|
||
}
|
||
|
||
<emphasis role="bold">@RequestMapping</emphasis> // default (action=list)
|
||
public String showPetSites() {
|
||
return "petSitesEdit";
|
||
}
|
||
|
||
<emphasis role="bold">@RequestMapping(params = "action=add")</emphasis> // render phase
|
||
public String showSiteForm(Model model) {
|
||
// Used for the initial form as well as for redisplaying with errors.
|
||
if (!model.containsAttribute("site")) {
|
||
model.addAttribute("site", new PetSite());
|
||
}
|
||
return "petSitesAdd";
|
||
}
|
||
|
||
<emphasis role="bold">@RequestMapping(params = "action=add")</emphasis> // action phase
|
||
public void populateSite(
|
||
@ModelAttribute("site") PetSite petSite, BindingResult result,
|
||
SessionStatus status, ActionResponse response) {
|
||
|
||
new PetSiteValidator().validate(petSite, result);
|
||
if (!result.hasErrors()) {
|
||
this.petSites.put(petSite.getName(), petSite.getUrl());
|
||
status.setComplete();
|
||
response.setRenderParameter("action", "list");
|
||
}
|
||
}
|
||
|
||
<emphasis role="bold">@RequestMapping(params = "action=delete")</emphasis>
|
||
public void removeSite(@RequestParam("site") String site, ActionResponse response) {
|
||
this.petSites.remove(site);
|
||
response.setRenderParameter("action", "list");
|
||
}
|
||
}</programlisting>
|
||
</section>
|
||
|
||
<section id="portlet-ann-requestmapping-arguments">
|
||
<title>Supported handler method arguments</title>
|
||
|
||
<para>Handler methods which are annotated with
|
||
<classname>@RequestMapping</classname> are allowed to have very flexible
|
||
signatures. They may have arguments of the following types, in arbitrary
|
||
order (except for validation results, which need to follow right after
|
||
the corresponding command object, if desired):
|
||
|
||
<itemizedlist>
|
||
<listitem>
|
||
<para>Request and/or response objects (Portlet API).
|
||
You may choose any specific request/response type, e.g. PortletRequest /
|
||
ActionRequest / RenderRequest. An explicitly declared action/render
|
||
argument is also used for mapping specific request types onto a handler
|
||
method (in case of no other information given that differentiates
|
||
between action and render requests).</para>
|
||
</listitem>
|
||
|
||
<listitem>
|
||
<para>Session object (Portlet API): of type PortletSession. An argument
|
||
of this type will enforce the presence of a corresponding session.
|
||
As a consequence, such an argument will never be <literal>null</literal>.</para>
|
||
</listitem>
|
||
|
||
<listitem>
|
||
<para><classname>org.springframework.web.context.request.WebRequest</classname>
|
||
or <classname>org.springframework.web.context.request.NativeWebRequest</classname>.
|
||
Allows for generic request parameter access as well as request/session
|
||
attribute access, without ties to the native Servlet/Portlet API.</para>
|
||
</listitem>
|
||
|
||
<listitem>
|
||
<para><classname>java.util.Locale</classname> for the current request
|
||
locale (the portal locale in a Portlet environment).</para>
|
||
</listitem>
|
||
|
||
<listitem>
|
||
<para><classname>java.io.InputStream</classname> /
|
||
<classname>java.io.Reader</classname> for access to the request's content.
|
||
This will be the raw InputStream/Reader as exposed by the Portlet API.</para>
|
||
</listitem>
|
||
|
||
<listitem>
|
||
<para><classname>java.io.OutputStream</classname> /
|
||
<classname>java.io.Writer</classname> for generating the response's content.
|
||
This will be the raw OutputStream/Writer as exposed by the Portlet API.</para>
|
||
</listitem>
|
||
|
||
<listitem>
|
||
<para><classname>@RequestParam</classname> annotated parameters
|
||
for access to specific Portlet request parameters. Parameter values
|
||
will be converted to the declared method argument type.</para>
|
||
</listitem>
|
||
|
||
<listitem>
|
||
<para><interfacename>java.util.Map</interfacename> /
|
||
<interfacename>org.springframework.ui.Model</interfacename> /
|
||
<classname>org.springframework.ui.ModelMap</classname> for
|
||
enriching the implicit model that will be exposed to the web view.</para>
|
||
</listitem>
|
||
|
||
<listitem>
|
||
<para>Command/form objects to bind parameters to: as bean
|
||
properties or fields, with customizable type conversion, depending
|
||
on <classname>@InitBinder</classname> methods and/or the
|
||
HandlerAdapter configuration - see the
|
||
"<literal>webBindingInitializer</literal>" property on
|
||
<classname>AnnotationMethodHandlerAdapter</classname>. Such
|
||
command objects along with their validation results will be
|
||
exposed as model attributes, by default using the non-qualified
|
||
command class name in property notation (e.g. "orderAddress" for
|
||
type "mypackage.OrderAddress"). Specify a parameter-level
|
||
<classname>ModelAttribute</classname> annotation for declaring a
|
||
specific model attribute name.</para>
|
||
</listitem>
|
||
|
||
<listitem>
|
||
<para><classname>org.springframework.validation.Errors</classname> /
|
||
<classname>org.springframework.validation.BindingResult</classname>
|
||
validation results for a preceding command/form object (the
|
||
immediate preceding argument).</para>
|
||
</listitem>
|
||
|
||
<listitem>
|
||
<para><classname>org.springframework.web.bind.support.SessionStatus</classname>
|
||
status handle for marking form processing as complete (triggering
|
||
the cleanup of session attributes that have been indicated by the
|
||
<classname>@SessionAttributes</classname> annotation at the
|
||
handler type level).</para>
|
||
</listitem>
|
||
</itemizedlist></para>
|
||
|
||
<para>The following return types are supported for handler methods:
|
||
|
||
<itemizedlist>
|
||
<listitem>
|
||
<para>A <classname>ModelAndView</classname> object, with the model implicitly
|
||
enriched with command objects and the results of <literal>@ModelAttribute</literal>
|
||
annotated reference data accessor methods.</para>
|
||
</listitem>
|
||
|
||
<listitem>
|
||
<para>A <interfacename>Model</interfacename> object, with the view name implicitly
|
||
determined through a <interfacename>RequestToViewNameTranslator</interfacename>
|
||
and the model implicitly enriched with command objects and the results of
|
||
<literal>@ModelAttribute</literal> annotated reference data accessor methods.</para>
|
||
</listitem>
|
||
|
||
<listitem>
|
||
<para>A <interfacename>Map</interfacename> object for exposing a model, with the view name
|
||
implicitly determined through a <interfacename>RequestToViewNameTranslator</interfacename>
|
||
and the model implicitly enriched with command objects and the results of
|
||
<literal>@ModelAttribute</literal> annotated reference data accessor methods.</para>
|
||
</listitem>
|
||
|
||
<listitem>
|
||
<para>A <interfacename>View</interfacename> object, with the model implicitly
|
||
determined through command objects and <literal>@ModelAttribute</literal>
|
||
annotated reference data accessor methods. The handler method may also
|
||
programmatically enrich the model by declaring a <interfacename>Model</interfacename>
|
||
argument (see above).</para>
|
||
</listitem>
|
||
|
||
<listitem>
|
||
<para>A <classname>String</classname> value which is interpreted as view name,
|
||
with the model implicitly determined through command objects and
|
||
<literal>@ModelAttribute</literal> annotated reference data accessor methods.
|
||
The handler method may also programmatically enrich the model by declaring a
|
||
<interfacename>Model</interfacename> argument (see above).</para>
|
||
</listitem>
|
||
|
||
<listitem>
|
||
<para><literal>void</literal> if the method handles the response itself
|
||
(e.g. by writing the response content directly).</para>
|
||
</listitem>
|
||
|
||
<listitem>
|
||
<para>Any other return type will be considered a single model attribute
|
||
to be exposed to the view, using the attribute name specified through
|
||
<literal>@ModelAttribute</literal> at the method level (or the default
|
||
attribute name based on the return type's class name otherwise). The model
|
||
will be implicitly enriched with command objects and the results of
|
||
<literal>@ModelAttribute</literal> annotated reference data accessor methods.</para>
|
||
</listitem>
|
||
</itemizedlist></para>
|
||
</section>
|
||
|
||
<section id="portlet-ann-requestparam">
|
||
<title>Binding request parameters to method parameters with
|
||
<classname>@RequestParam</classname></title>
|
||
|
||
<para>The <classname>@RequestParam</classname> annotation is used to
|
||
bind request parameters to a method parameter in your controller.</para>
|
||
|
||
<para>The following code snippet from the PetPortal sample application
|
||
shows the usage:</para>
|
||
|
||
<programlisting language="java">@Controller
|
||
@RequestMapping("EDIT")
|
||
@SessionAttributes("site")
|
||
public class PetSitesEditController {
|
||
|
||
<lineannotation>// ...</lineannotation>
|
||
|
||
public void removeSite(<emphasis role="bold">@RequestParam("site")</emphasis> String site, ActionResponse response) {
|
||
this.petSites.remove(site);
|
||
response.setRenderParameter("action", "list");
|
||
}
|
||
|
||
<lineannotation>// ...</lineannotation>
|
||
}
|
||
</programlisting>
|
||
|
||
<para>Parameters using this annotation are required by default, but you
|
||
can specify that a parameter is optional by setting
|
||
<interfacename>@RequestParam</interfacename>'s
|
||
<literal>required</literal> attribute to <literal>false</literal> (e.g.,
|
||
<literal>@RequestParam(value="id", required=false)</literal>).</para>
|
||
</section>
|
||
|
||
<section id="portlet-ann-modelattrib">
|
||
<title>Providing a link to data from the model with
|
||
<classname>@ModelAttribute</classname></title>
|
||
|
||
<para><classname>@ModelAttribute</classname> has two usage scenarios in
|
||
controllers. When placed on a method parameter,
|
||
<classname>@ModelAttribute</classname> is used to map a model attribute
|
||
to the specific, annotated method parameter (see the
|
||
<literal>populateSite()</literal> method below). This is how the
|
||
controller gets a reference to the object holding the data entered in
|
||
the form. In addition, the parameter can be declared as the specific
|
||
type of the form backing object rather than as a generic
|
||
<classname>java.lang.Object</classname>, thus increasing type
|
||
safety.</para>
|
||
|
||
<para><classname>@ModelAttribute</classname> is also used at the method
|
||
level to provide <emphasis>reference data</emphasis> for the model (see
|
||
the <literal>getPetSites()</literal> method below). For this usage
|
||
the method signature can contain the same types as documented above for
|
||
the <classname>@RequestMapping</classname> annotation.</para>
|
||
|
||
<para><emphasis>Note:</emphasis> <classname>@ModelAttribute</classname>
|
||
annotated methods will be executed <emphasis>before</emphasis> the
|
||
chosen <classname>@RequestMapping</classname> annotated handler method.
|
||
They effectively pre-populate the implicit model with specific attributes,
|
||
often loaded from a database. Such an attribute can then already be
|
||
accessed through <classname>@ModelAttribute</classname> annotated
|
||
handler method parameters in the chosen handler method, potentially
|
||
with binding and validation applied to it.</para>
|
||
|
||
<para>The following code snippet shows these two usages of this
|
||
annotation:</para>
|
||
|
||
<programlisting language="java">@Controller
|
||
@RequestMapping("EDIT")
|
||
@SessionAttributes("site")
|
||
public class PetSitesEditController {
|
||
|
||
<lineannotation>// ...</lineannotation>
|
||
|
||
<emphasis role="bold">@ModelAttribute("petSites")</emphasis>
|
||
public Properties getPetSites() {
|
||
return this.petSites;
|
||
}
|
||
|
||
@RequestMapping(params = "action=add") // action phase
|
||
public void populateSite(
|
||
<emphasis role="bold">@ModelAttribute("site")</emphasis> PetSite petSite, BindingResult result,
|
||
SessionStatus status, ActionResponse response) {
|
||
|
||
new PetSiteValidator().validate(petSite, result);
|
||
if (!result.hasErrors()) {
|
||
this.petSites.put(petSite.getName(), petSite.getUrl());
|
||
status.setComplete();
|
||
response.setRenderParameter("action", "list");
|
||
}
|
||
}
|
||
}</programlisting>
|
||
</section>
|
||
|
||
<section id="portlet-ann-sessionattrib">
|
||
<title>Specifying attributes to store in a Session with
|
||
<classname>@SessionAttributes</classname></title>
|
||
|
||
<para>The type-level <classname>@SessionAttributes</classname>
|
||
annotation declares session attributes used by a specific handler.
|
||
This will typically list the names of model attributes or types of
|
||
model attributes which should be
|
||
transparently stored in the session or some conversational storage,
|
||
serving as form-backing beans between subsequent requests.</para>
|
||
|
||
<para>The following code snippet shows the usage of this
|
||
annotation:</para>
|
||
|
||
<programlisting language="java">@Controller
|
||
@RequestMapping("EDIT")
|
||
<emphasis role="bold">@SessionAttributes("site")</emphasis>
|
||
public class PetSitesEditController {
|
||
<lineannotation>// ...</lineannotation>
|
||
}
|
||
</programlisting>
|
||
</section>
|
||
|
||
<section id="portlet-ann-webdatabinder">
|
||
<title>Customizing <classname>WebDataBinder</classname>
|
||
initialization</title>
|
||
|
||
<para>To customize request parameter binding with PropertyEditors, etc.
|
||
via Spring's <classname>WebDataBinder</classname>, you can either use
|
||
<interfacename>@InitBinder</interfacename>-annotated methods within your
|
||
controller or externalize your configuration by providing a custom
|
||
<interfacename>WebBindingInitializer</interfacename>.</para>
|
||
|
||
<section id="portlet-ann-initbinder">
|
||
<title>Customizing data binding with
|
||
<interfacename>@InitBinder</interfacename></title>
|
||
|
||
<para>Annotating controller methods with
|
||
<interfacename>@InitBinder</interfacename> allows you to configure web
|
||
data binding directly within your controller class.
|
||
<interfacename>@InitBinder</interfacename> identifies methods which
|
||
initialize the <classname>WebDataBinder</classname> which will be used
|
||
for populating command and form object arguments of annotated handler
|
||
methods.</para>
|
||
|
||
<para>Such init-binder methods support all arguments that
|
||
<interfacename>@RequestMapping</interfacename> supports, except for
|
||
command/form objects and corresponding validation result objects.
|
||
Init-binder methods must not have a return value. Thus, they are
|
||
usually declared as <literal>void</literal>. Typical arguments include
|
||
<classname>WebDataBinder</classname> in combination with
|
||
<interfacename>WebRequest</interfacename> or
|
||
<classname>java.util.Locale</classname>, allowing code to register
|
||
context-specific editors.</para>
|
||
|
||
<para>The following example demonstrates the use of
|
||
<interfacename>@InitBinder</interfacename> for configuring a
|
||
<classname>CustomDateEditor</classname> for all
|
||
<classname>java.util.Date</classname> form properties.</para>
|
||
|
||
<programlisting language="java">@Controller
|
||
public class MyFormController {
|
||
|
||
<emphasis role="bold">@InitBinder</emphasis>
|
||
public void initBinder(WebDataBinder binder) {
|
||
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
|
||
dateFormat.setLenient(false);
|
||
binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false));
|
||
}
|
||
|
||
<lineannotation>// ...</lineannotation>
|
||
}</programlisting>
|
||
</section>
|
||
|
||
<section id="portlet-ann-webbindinginitializer">
|
||
<title>Configuring a custom
|
||
<interfacename>WebBindingInitializer</interfacename></title>
|
||
|
||
<para>To externalize data binding initialization, you can provide a
|
||
custom implementation of the
|
||
<interfacename>WebBindingInitializer</interfacename> interface, which
|
||
you then enable by supplying a custom bean configuration for an
|
||
<classname>AnnotationMethodHandlerAdapter</classname>, thus overriding
|
||
the default configuration.</para>
|
||
</section>
|
||
</section>
|
||
</section>
|
||
|
||
<section id="portlet-deployment">
|
||
<title>Portlet application deployment</title>
|
||
|
||
<para>The process of deploying a Spring Portlet MVC application is no
|
||
different than deploying any JSR-168 Portlet application. However, this
|
||
area is confusing enough in general that it is worth talking about here
|
||
briefly.</para>
|
||
|
||
<para>Generally, the portal/portlet container runs in one webapp in your
|
||
servlet container and your portlets run in another webapp in your
|
||
servlet container. In order for the portlet container webapp to make
|
||
calls into your portlet webapp it must make cross-context calls to a
|
||
well-known servlet that provides access to the portlet services defined
|
||
in your <literal>portlet.xml</literal> file.</para>
|
||
|
||
<para>The JSR-168 specification does not specify exactly how this should
|
||
happen, so each portlet container has its own mechanism for this, which
|
||
usually involves some kind of “deployment process” that makes changes to
|
||
the portlet webapp itself and then registers the portlets within the
|
||
portlet container.</para>
|
||
|
||
<para>At a minimum, the <literal>web.xml</literal> file in your portlet
|
||
webapp is modified to inject the well-known servlet that the portlet
|
||
container will call. In some cases a single servlet will service all
|
||
portlets in the webapp, in other cases there will be an instance of the
|
||
servlet for each portlet.</para>
|
||
|
||
<para>Some portlet containers will also inject libraries and/or
|
||
configuration files into the webapp as well. The portlet container must
|
||
also make its implementation of the Portlet JSP Tag Library available to
|
||
your webapp.</para>
|
||
|
||
<para>The bottom line is that it is important to understand the
|
||
deployment needs of your target portal and make sure they are met
|
||
(usually by following the automated deployment process it provides).
|
||
Be sure to carefully review the documentation from your portal for this
|
||
process.</para>
|
||
|
||
<para>Once you have deployed your portlet, review the resulting
|
||
<literal>web.xml</literal> file for sanity. Some older portals have
|
||
been known to corrupt the definition of the
|
||
<classname>ViewRendererServlet</classname>, thus breaking the rendering
|
||
of your portlets.</para>
|
||
</section>
|
||
|
||
</chapter>
|