The controllers in Portlet MVC are very similar to the Web MVC Controllers and porting code from one to the other should be simple.
The basis for the Portlet MVC controller architecture is the
org.springframework.web.portlet.mvc.Controller
interface, which is listed below.
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; }
As you can see, the Portlet
Controller
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
Controller
interface is quite abstract,
Spring Portlet MVC offers a lot of 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
Controller
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.
Of course, just a Controller
interface isn't enough. To provide a basic infrastructure, all of
Spring Portlet MVC's Controller
s
inherit from AbstractController
, a class
offering access to Spring's
ApplicationContext
and control over
caching.
Table 18.3. Features offered by the AbstractController
Parameter | Explanation |
---|---|
requireSession | Indicates whether or not this
Controller 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
SessionRequiredException . |
synchronizeSession | 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 handleRenderRequestInternal(..) and
handleActionRequestInternal(..) methods, which will be
synchronized on the user’s session if you specify
this variable. |
renderWhenMinimized | 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. |
cacheSeconds | 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 -1 , which does not change
the default caching. Setting it to 0
will ensure the result is never cached. |
The requireSession
and
cacheSeconds
properties are declared on the
PortletContentGenerator
class, which is the
superclass of AbstractController
) but are
included here for completeness.
When using the AbstractController
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
handleActionRequestInternal(ActionRequest,
ActionResponse)
method or the
handleRenderRequestInternal(RenderRequest,
RenderResponse)
method (or both), implement your logic,
and return a ModelAndView
object (in the case
of handleRenderRequestInternal
).
The default implementations of both
handleActionRequestInternal(..)
and
handleRenderRequestInternal(..)
throw a
PortletException
. This is consistent with
the behavior of GenericPortlet
from the JSR-
168 Specification API. So you only need to override the method that
your controller is intended to handle.
Here is short example consisting of a class and a declaration in the web application context.
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>
The class above and the declaration in the web application context is all you need besides setting up a handler mapping (see Section 18.5, “Handler mappings”) to get this very simple controller working.
Although you can extend AbstractController
,
Spring Portlet MVC provides a number of concrete implementations which offer
functionality that is commonly used in simple MVC applications.
The ParameterizableViewController
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).
The PortletModeNameViewController
uses
the current mode of the portlet as the view name. So, if your
portlet is in View mode (i.e. PortletMode.VIEW
)
then it uses "view" as the view name.
Spring Portlet MVC has the exact same hierarchy of
command controllers as Spring Web MVC. They
provide a way to interact with data objects and dynamically bind
parameters from the PortletRequest
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:
AbstractCommandController
- 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.
AbstractFormController
-
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, AbstractFormController
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.
SimpleFormController
- a
concrete AbstractFormController
that
provides even more support when creating a form with a
corresponding command object. The
SimpleFormController
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.
AbstractWizardFormController
–
a concrete AbstractFormController
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.
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.
Instead of developing new controllers, it is possible to use
existing portlets and map requests to them from a
DispatcherPortlet
. Using the
PortletWrappingController
, you can
instantiate an existing Portlet
as a
Controller
as follows:
<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>
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
OpenSessionInViewInterceptor
around a MyFaces
JSF Portlet.