forked from spring-projects/spring-framework
-
Notifications
You must be signed in to change notification settings - Fork 0
/
portlet.xml
1820 lines (1539 loc) · 84.5 KB
/
portlet.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="UTF-8"?>
<chapter xml:id="portlet"
xmlns="http://docbook.org/ns/docbook" version="5.0"
xmlns:xl="http://www.w3.org/1999/xlink"
xmlns:xi="http://www.w3.org/2001/XInclude"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://docbook.org/ns/docbook http://www.docbook.org/xml/5.0/xsd/docbook.xsd
http://www.w3.org/1999/xlink http://www.docbook.org/xml/5.0/xsd/xlink.xsd">
<title>Portlet MVC Framework</title>
<section xml: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
<link xl:href="http://developers.sun.com/prodtech/portalserver/reference/techart/jsr168/">"Introduction to JSR 168"</link>,
and of course the
<link xl:href="http://jcp.org/aboutJava/communityprocess/final/jsr168/">JSR-168 Specification</link> 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 xml: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 xml: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 xml: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 xml: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 xml: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" xml: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 xml: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 xml: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 xml: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" xml: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
base class 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 xml: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 xml: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 xml: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 xml: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 xml: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 xml: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 xml: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 xml: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 xml: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 xml: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 xml: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 xml: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
<link xl:href="http://jakarta.apache.org/commons/fileupload">Commons FileUpload</link>.
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>