You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1816 lines
84 KiB
1816 lines
84 KiB
<?xml version="1.0" encoding="UTF-8"?> |
|
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" |
|
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd"> |
|
|
|
<chapter id="portlet" 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>
|
|
|