| | + title | Ref. /chapter[1]/title[1] |
| Original | Traduction | Integrating view technologies |
| sect1
| | + title | Ref. /chapter[1]/sect1[1]/title[1] |
| Original | Traduction | Introduction |
|
| + para | Ref. /chapter[1]/sect1[1]/para[1] |
| Original | Traduction | One of the areas in which Spring excels is in the separation of view
technologies from the rest of the MVC framework. For example, deciding to
use Velocity or XSLT in place of an existing JSP is primarily a matter of
configuration. This chapter covers the major view technologies that work
with Spring and touches briefly on how to add new ones. This chapter
assumes you are already familiar with
which covers the basics of how views in general are coupled to the MVC
framework. |
|
| | sect1
| | + title | Ref. /chapter[1]/sect1[2]/title[1] |
| Original | Traduction | JSP & JSTL |
|
| + para | Ref. /chapter[1]/sect1[2]/para[1] |
| Original | Traduction | Spring provides a couple of out-of-the-box solutions for JSP and
JSTL views. Using JSP or JSTL is done using a normal viewresolver defined
in the WebApplicationContext. Furthermore, of course you need to write
some JSPs that will actually render the view. This part describes some of
the additional features Spring provides to facilitate JSP
development. |
| sect2
| | + title | Ref. /chapter[1]/sect1[2]/sect2[1]/title[1] |
| Original | Traduction | View resolvers |
|
| + para | Ref. /chapter[1]/sect1[2]/sect2[1]/para[1] |
| Original | Traduction | Just as with any other view technology you're integrating with
Spring, for JSPs you'll need a view resolver that will resolve your
views. The most commonly used view resolvers when developing with JSPs
are the InternalResourceViewResolver and the
ResourceBundleViewResolver. Both are declared in the
WebApplicationContext: |
|
| + programlisting | Ref. /chapter[1]/sect1[2]/sect2[1]/programlisting[1] |
| Original | Traduction | # The ResourceBundleViewResolver:
<bean id="viewResolver" class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
<property name="basename"><value>views</value></property>
</bean>
# And a sample properties file is uses (views.properties in WEB-INF/classes):
welcome.class=org.springframework.web.servlet.view.JstlView
welcome.url=/WEB-INF/jsp/welcome.jsp
productList.class=org.springframework.web.servlet.view.JstlView
productList.url=/WEB-INF/jsp/productlist.jsp |
|
| + para | Ref. /chapter[1]/sect1[2]/sect2[1]/para[2] |
| Original | Traduction | As you can see, the ResourceBundleViewResolver needs a properties
file defining the view names mapped to 1) a class and 2) a URL. With a
ResourceBundleViewResolver you can mix different types of views using
only one resolver. |
|
| + programlisting | Ref. /chapter[1]/sect1[2]/sect2[1]/programlisting[2] |
| Original | Traduction | <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass"><value>org.springframework.web.servlet.view.JstlView</value></property>
<property name="prefix"><value>/WEB-INF/jsp/</value></property>
<property name="suffix"><value>.jsp</value></property>
</bean> |
|
| + para | Ref. /chapter[1]/sect1[2]/sect2[1]/para[3] |
| Original | Traduction | The InternalResourceBundleViewResolver can be configured for using
JSPs as described above. As a best practice, we strongly encourage
placing your JSP files in a a directory under the WEB-INF directory, so
there can be no direct access by clients. |
|
| | sect2
| | + title | Ref. /chapter[1]/sect1[2]/sect2[2]/title[1] |
| Original | Traduction | 'Plain-old' JSPs versus JSTL |
|
| + para | Ref. /chapter[1]/sect1[2]/sect2[2]/para[1] |
| Original | Traduction | When using Java Standard Tag Library you must use a special view
class, the JstlView, as JSTL needs some preparation
before things such as the i18N features will work. |
|
| | sect2
| | + title | Ref. /chapter[1]/sect1[2]/sect2[3]/title[1] |
| Original | Traduction | Additional tags facilitating development |
|
| + para | Ref. /chapter[1]/sect1[2]/sect2[3]/para[1] |
| Original | Traduction | Spring provides data binding of request parameters to command
objects as described in earlier chapters. To facilitate the development
of JSP pages in combination with those data binding features, Spring
provides a few tags that make things even easier. All Spring tags have
html escaping features to enable or disable
escaping of characters. |
|
| + para | Ref. /chapter[1]/sect1[2]/sect2[3]/para[2] |
| Original | Traduction | The tag library descriptor (TLD) is included in the
spring.jar as well in the distribution itself. More
information about the individual tags can be found online: . |
|
| |
| | sect1
| | + title | Ref. /chapter[1]/sect1[3]/title[1] |
| Original | Traduction | Tiles |
|
| + para | Ref. /chapter[1]/sect1[3]/para[1] |
| Original | Traduction | It is possible to integrate Tiles - just as any other view
technology - in web applications using Spring. The following describes in a
broad way how to do this. |
| sect2
| | + title | Ref. /chapter[1]/sect1[3]/sect2[1]/title[1] |
| Original | Traduction | Dependencies |
|
| + para | Ref. /chapter[1]/sect1[3]/sect2[1]/para[1] |
| Original | Traduction | To be able to use Tiles you have to have a couple of additional
dependencies included in your project. The following is the list of
dependencies you need. |
|
struts version 1.1
commons-beanutils
commons-digester
commons-logging
commons-lang
| + para | Ref. /chapter[1]/sect1[3]/sect2[1]/para[3] |
| Original | Traduction | The dependencies are all available in the Spring
distribution. |
|
| | sect2
| | + title | Ref. /chapter[1]/sect1[3]/sect2[2]/title[1] |
| Original | Traduction | How to integrate Tiles |
|
| + para | Ref. /chapter[1]/sect1[3]/sect2[2]/para[1] |
| Original | Traduction | To be able to use Tiles, you have to configure it using files
containing definitions (for basic information on definitions and other
Tiles concepts, please have a look at ). In Spring this is done
using the TilesConfigurer. Have a look at the
following piece of example ApplicationContext configuration:
| + programlisting | Ref. /chapter[1]/sect1[3]/sect2[2]/para[1]/programlisting[1] |
| Original | Traduction | <bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles.TilesConfigurer">
<property name="factoryClass">
<value>org.apache.struts.tiles.xmlDefinition.I18nFactorySet</value>
</property>
<property name="definitions">
<list>
<value>/WEB-INF/defs/general.xml</value>
<value>/WEB-INF/defs/widgets.xml</value>
<value>/WEB-INF/defs/administrator.xml</value>
<value>/WEB-INF/defs/customer.xml</value>
<value>/WEB-INF/defs/templates.xml</value>
</list>
</property>
</bean> |
|
|
|
| + para | Ref. /chapter[1]/sect1[3]/sect2[2]/para[2] |
| Original | Traduction | As you can see, there are five files containing definitions, which
are all located in the WEB-INF/defs directory. At initialization of the
WebApplicationContext, the files will be loaded and the
definitionsfactory defined by the
factoryClass-property is initialized. After that has
been done, the tiles includes in the definition files can be used as
views within your Spring web application. To be able to use the views you
have to have a ViewResolver just as with any other
view technology used with Spring. Below you can find two possibilities,
the InternalResourceViewResolver and the
ResourceBundleViewResolver. |
|
InternalResourceViewResolver
| + para | Ref. /chapter[1]/sect1[3]/sect2[2]/sect3[1]/para[1] |
| Original | Traduction | The InternalResourceViewResolver instantiates the given
viewClass for each view it has to resolve.
| + programlisting | Ref. /chapter[1]/sect1[3]/sect2[2]/sect3[1]/para[1]/programlisting[1] |
| Original | Traduction | <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="requestContextAttribute"><value>requestContext</value></property>
<property name="viewClass">
<value>org.springframework.web.servlet.view.tiles.TilesView</value>
</property>
</bean> |
|
|
|
ResourceBundleViewResolver
| + para | Ref. /chapter[1]/sect1[3]/sect2[2]/sect3[2]/para[1] |
| Original | Traduction | The ResourceBundleViewResolver has to be provided with a
property file containing viewnames and viewclasses the resolver can
use: | + programlisting | Ref. /chapter[1]/sect1[3]/sect2[2]/sect3[2]/para[1]/programlisting[1] |
| Original | Traduction | <bean id="viewResolver" class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
<property name="basename"><value>views</value></property>
</bean> |
|
|
|
| + para | Ref. /chapter[1]/sect1[3]/sect2[2]/sect3[2]/para[2] |
| Original | Traduction | | + programlisting | Ref. /chapter[1]/sect1[3]/sect2[2]/sect3[2]/para[2]/programlisting[1] |
| Original | Traduction | ...
welcomeView.class=org.springframework.web.servlet.view.tiles.TilesView
welcomeView.url=welcome (<b>this is the name of a definition</b>)
vetsView.class=org.springframework.web.servlet.view.tiles.TilesView
vetsView.url=vetsView (<b>again, this is the name of a definition</b>)
findOwnersForm.class=org.springframework.web.servlet.view.JstlView
findOwnersForm.url=/WEB-INF/jsp/findOwners.jsp
... |
| As you can see, when using the
ResourceBundleViewResolver, you can mix view using different view
technologies. |
|
| |
| | sect1
| | + title | Ref. /chapter[1]/sect1[4]/title[1] |
| Original | Traduction | Velocity & FreeMarker |
|
| + para | Ref. /chapter[1]/sect1[4]/para[1] |
| Original | Traduction | Velocity and
FreeMarker are two
templating languages that can both be used as view technologies within
Spring MVC applications. The languages are quite similar and serve similar
needs and so are considered together in this section. For semantic and
syntactic differences between the two languages, see the FreeMarker web site. |
| sect2
| | + title | Ref. /chapter[1]/sect1[4]/sect2[1]/title[1] |
| Original | Traduction | Dependencies |
|
| + para | Ref. /chapter[1]/sect1[4]/sect2[1]/para[1] |
| Original | Traduction | Your web application will need to include
velocity-1.x.x.jar or
freemarker-2.x.jar in order to work with Velocity or
FreeMarker respectively and commons-collections.jar
needs also to be available for Velocity. Typically they are included in
the WEB-INF/lib folder where they are guaranteed to
be found by a J2EE server and added to the classpath for your
application. It is of course assumed that you already have the
spring.jar in your WEB-INF/lib
folder too! The latest stable velocity, freemarker and commons
collections jars are supplied with the Spring framework and can be
copied from the relevant /lib/ sub-directories. If
you make use of Spring's dateToolAttribute or numberToolAttribute in
your Velocity views, you will also need to include the
velocity-tools-generic-1.x.jar |
|
| | sect2
| | + title | Ref. /chapter[1]/sect1[4]/sect2[2]/title[1] |
| Original | Traduction | Context configuration |
|
| + para | Ref. /chapter[1]/sect1[4]/sect2[2]/para[1] |
| Original | Traduction | A suitable configuration is initialized by adding the relevant
configurer bean definition to your *-servlet.xml as shown below: |
|
| + programlisting | Ref. /chapter[1]/sect1[4]/sect2[2]/para[2]/programlisting[1] |
| Original | Traduction | <!--
This bean sets up the Velocity environment for us based on a root path for templates.
Optionally, a properties file can be specified for more control over the Velocity
environment, but the defaults are pretty sane for file based template loading.
-->
<bean
id="velocityConfig"
class="org.springframework.web.servlet.view.velocity.VelocityConfigurer">
<property name="resourceLoaderPath"><value>/WEB-INF/velocity/</value></property>
</bean>
<!--
View resolvers can also be configured with ResourceBundles or XML files. If you need
different view resolving based on Locale, you have to use the resource bundle resolver.
-->
<bean
id="viewResolver"
class="org.springframework.web.servlet.view.velocity.VelocityViewResolver">
<property name="cache"><value>true</value></property>
<property name="prefix"><value></value></property>
<property name="suffix"><value>.vm</value></property>
</bean> |
|
| + programlisting | Ref. /chapter[1]/sect1[4]/sect2[2]/programlisting[1] |
| Original | Traduction | <!-- freemarker config -->
<bean
id="freemarkerConfig"
class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
<property name="templateLoaderPath"><value>/WEB-INF/freemarker/</value></property>
</bean>
<!--
View resolvers can also be configured with ResourceBundles or XML files. If you need
different view resolving based on Locale, you have to use the resource bundle resolver.
-->
<bean
id="viewResolver"
class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
<property name="cache"><value>true</value></property>
<property name="prefix"><value></value></property>
<property name="suffix"><value>.ftl</value></property>
</bean> |
| NB: For non web-apps add a
VelocityConfigurationFactoryBean or a
FreeMarkerConfigurationFactoryBean to your
application context definition file. | | sect2
| | + title | Ref. /chapter[1]/sect1[4]/sect2[3]/title[1] |
| Original | Traduction | Creating templates |
|
| + para | Ref. /chapter[1]/sect1[4]/sect2[3]/para[1] |
| Original | Traduction | Your templates need to be stored in the directory specified by the
*Configurer bean shown above in This document does not cover
details of creating templates for the two languages - please see their
relevant websites for information. If you use the view resolvers
highlighted, then the logical view names relate to the template file
names in similar fashion to
InternalResourceViewResolver for JSP's. So if your
controller returns a ModelAndView object containing a view name of
"welcome" then the resolvers will look for the
/WEB-INF/freemarker/welcome.ftl or
/WEB-INF/velocity/welcome.vm template as
appropriate. |
|
| | sect2
| | + title | Ref. /chapter[1]/sect1[4]/sect2[4]/title[1] |
| Original | Traduction | Advanced configuration |
|
| + para | Ref. /chapter[1]/sect1[4]/sect2[4]/para[1] |
| Original | Traduction | The basic configurations highlighted above will be suitable for
most application requirements, however additional configuration options
are available for when unusual or advanced requirements dictate. |
|
| + title | Ref. /chapter[1]/sect1[4]/sect2[4]/sect3[1]/title[1] |
| Original | Traduction | velocity.properties |
|
| + para | Ref. /chapter[1]/sect1[4]/sect2[4]/sect3[1]/para[1] |
| Original | Traduction | This file is completely optional, but if specified, contains the
values that are passed to the Velocity runtime in order to configure
velocity itself. Only required for advanced configurations, if you
need this file, specify its location on the
VelocityConfigurer bean definition above. |
|
| + programlisting | Ref. /chapter[1]/sect1[4]/sect2[4]/sect3[1]/programlisting[1] |
| Original | Traduction | <bean
id="velocityConfig"
class="org.springframework.web.servlet.view.velocity.VelocityConfigurer">
<property name="configLocation">
<value>/WEB-INF/velocity.properties</value>
</property>
</bean> |
|
| + para | Ref. /chapter[1]/sect1[4]/sect2[4]/sect3[1]/para[2] |
| Original | Traduction | Alternatively, you can specify velocity properties directly in
the bean definition for the Velocity config bean by replacing the
"configLocation" property with the following inline properties. |
|
| + programlisting | Ref. /chapter[1]/sect1[4]/sect2[4]/sect3[1]/programlisting[2] |
| Original | Traduction | <bean
id="velocityConfig"
class="org.springframework.web.servlet.view.velocity.VelocityConfigurer">
<property name="velocityProperties">
<props>
<prop key="resource.loader">file</prop>
<prop key="file.resource.loader.class">
org.apache.velocity.runtime.resource.loader.FileResourceLoader
</prop>
<prop key="file.resource.loader.path">${webapp.root}/WEB-INF/velocity</prop>
<prop key="file.resource.loader.cache">false</prop>
</props>
</property>
</bean> |
|
| + para | Ref. /chapter[1]/sect1[4]/sect2[4]/sect3[1]/para[3] |
| Original | Traduction | Refer to the API
documentation for Spring configuration of Velocity, or the
Velocity documentation for examples and definitions of the
velocity.properties file itself. |
|
| + title | Ref. /chapter[1]/sect1[4]/sect2[4]/sect3[2]/title[1] |
| Original | Traduction | FreeMarker |
|
| + para | Ref. /chapter[1]/sect1[4]/sect2[4]/sect3[2]/para[1] |
| Original | Traduction | FreeMarker 'Settings' and 'SharedVariables' can be passed
directly to the FreeMarker Configuration object
managed by Spring by setting the appropriate bean properties on the
FreeMarkerConfigurer bean. The
freemarkerSettings property requires a
java.util.Properties object and the
freemarkerVariables property requires a
java.util.Map. |
|
| + programlisting | Ref. /chapter[1]/sect1[4]/sect2[4]/sect3[2]/programlisting[1] |
| Original | Traduction | <bean
id="freemarkerConfig"
class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
<property name="templateLoaderPath"><value>/WEB-INF/freemarker/</value></property>
<property name="freemarkerVariables">
<map>
<entry key="xml_escape"><ref local="fmXmlEscape"/></entry>
</map>
</property>
</bean>
<bean id="fmXmlEscape" class="freemarker.template.utility.XmlEscape"/> |
|
| + para | Ref. /chapter[1]/sect1[4]/sect2[4]/sect3[2]/para[2] |
| Original | Traduction | See the FreeMarker documentation for details of settings and
variables as they apply to the Configuration
object. |
|
| | sect2
| | + title | Ref. /chapter[1]/sect1[4]/sect2[5]/title[1] |
| Original | Traduction | Bind support and form handling |
|
| + para | Ref. /chapter[1]/sect1[4]/sect2[5]/para[1] |
| Original | Traduction | Spring provides a tag library for use in JSP's that contains
(amongst other things) a <spring:bind> tag.
This tag primarily enables forms to display values from form backing
objects and to show the results of failed validations from a
Validator in the web or business tier. From version
1.1, Spring now has support for the same functionality in both Velocity
and FreeMarker, with additional convenience macros for generating form
input elements themselves. |
|
| + title | Ref. /chapter[1]/sect1[4]/sect2[5]/sect3[1]/title[1] |
| Original | Traduction | the bind macros |
|
| + para | Ref. /chapter[1]/sect1[4]/sect2[5]/sect3[1]/para[1] |
| Original | Traduction | A standard set of macros are maintained within the
spring.jar file for both languages, so they are
always available to a suitably configured application. However they
can only be used if your view sets the bean property
exposeSpringMacroHelpers to true
. The same property can be set on
VelocityViewResolver or
FreeMarkerViewResolver too if you happen to be
using it, in which case all of your views will inherit the value from
it. Note that this property is not
required for any aspect of HTML form handling except where you wish to take advantage of the
Spring macros. Below is an example of a view.properties file showing
correct configuration of such a view for either language; |
|
| + programlisting | Ref. /chapter[1]/sect1[4]/sect2[5]/sect3[1]/programlisting[1] |
| Original | Traduction | personFormV.class=org.springframework.web.servlet.view.velocity.VelocityView
personFormV.url=personForm.vm
personFormV.exposeSpringMacroHelpers=true |
|
| + programlisting | Ref. /chapter[1]/sect1[4]/sect2[5]/sect3[1]/programlisting[2] |
| Original | Traduction | personFormF.class=org.springframework.web.servlet.view.freemarker.FreeMarkerView
personFormF.url=personForm.ftl
personFormF.exposeSpringMacroHelpers=true |
|
| + para | Ref. /chapter[1]/sect1[4]/sect2[5]/sect3[1]/para[2] |
| Original | Traduction | Some of the macros defined in the Spring libraries are
considered internal (private) but no such scoping exists in the macro
definitions making all macros visible to calling code and user
templates. The following sections concentrate only on the macros you
need to be directly calling from within your templates. If you wish to
view the macro code directly, the files are called spring.vm /
spring.ftl and are in the packages
org.springframework.web.servlet.view.velocity or
org.springframework.web.servlet.view.freemarker
respectively. |
|
| + title | Ref. /chapter[1]/sect1[4]/sect2[5]/sect3[2]/title[1] |
| Original | Traduction | simple binding |
|
| + para | Ref. /chapter[1]/sect1[4]/sect2[5]/sect3[2]/para[1] |
| Original | Traduction | In your html forms (vm / ftl templates) that act as the
'formView' for a Spring form controller, you can use code similar to
the following to bind to field values and display error messages for
each input field in similar fashion to the JSP equivalent. Note that
the name of the command object is "command" by default, but can be
overridden in your MVC configuration by setting the 'commandName' bean
property on your form controller. Example code is shown below for the
personFormV and personFormF
views configured earlier; |
|
| + programlisting | Ref. /chapter[1]/sect1[4]/sect2[5]/sect3[2]/programlisting[1] |
| Original | Traduction | <!-- velocity macros are automatically available -->
<html>
...
<form action="" method="POST">
Name:
#springBind( "command.name" )
<input type="text"
name="${status.expression}"
value="$!status.value" /><br>
#foreach($error in $status.errorMessages) <b>$error</b> <br> #end
<br>
...
<input type="submit" value="submit"/>
</form>
...
</html> |
|
| + programlisting | Ref. /chapter[1]/sect1[4]/sect2[5]/sect3[2]/programlisting[2] |
| Original | Traduction | <!-- freemarker macros have to be imported into a namespace. We strongly
recommend sticking to 'spring' -->
<#import "spring.ftl" as spring />
<html>
...
<form action="" method="POST">
Name:
<@spring.bind "command.name" />
<input type="text"
name="${spring.status.expression}"
value="${spring.status.value?default("")}" /><br>
<#list spring.status.errorMessages as error> <b>${error}</b> <br> </#list>
<br>
...
<input type="submit" value="submit"/>
</form>
...
</html> |
|
| + para | Ref. /chapter[1]/sect1[4]/sect2[5]/sect3[2]/para[2] |
| Original | Traduction | #springBind /
<@spring.bind> requires a 'path' argument
which consists of the name of your command object (it will be
'command' unless you changed it in your FormController properties)
followed by a period and the name of the field on the command object
you wish to bind to. Nested fields can be used too such as
"command.address.street". The bind macro assumes
the default HTML escaping behavior specified by the ServletContext
parameter defaultHtmlEscape in web.xml |
|
| + para | Ref. /chapter[1]/sect1[4]/sect2[5]/sect3[2]/para[3] |
| Original | Traduction | The optional form of the macro called
#springBindEscaped /
<@spring.bindEscaped> takes a second argument
and explicitly specifies whether HTML escaping should be used in the
status error messages or values. Set to true or false as required.
Additional form handling macros simplify the use of HTML escaping and
these macros should be used wherever possible. They are explained in
the next section. |
|
| + title | Ref. /chapter[1]/sect1[4]/sect2[5]/sect3[3]/title[1] |
| Original | Traduction | form input generation macros |
|
| + para | Ref. /chapter[1]/sect1[4]/sect2[5]/sect3[3]/para[1] |
| Original | Traduction | Additional convenience macros for both languages simplify both
binding and form generation (including validation error display). It
is never necessary to use these macros to generate form input fields,
and they can be mixed and matched with simple HTML or calls direct to
the spring bind macros highlighted previously. |
|
| + para | Ref. /chapter[1]/sect1[4]/sect2[5]/sect3[3]/para[2] |
| Original | Traduction | The following table of available macros show the VTL and FTL
definitions and the parameter list that each takes. |
|
| + title | Ref. /chapter[1]/sect1[4]/sect2[5]/sect3[3]/table[1]/title[1] |
| Original | Traduction | table of macro definitions |
|
| + entry | Ref. /chapter[1]/sect1[4]/sect2[5]/sect3[3]/table[1]/tgroup[1]/thead[1]/row[1]/entry[1] |
| Original | Traduction | macro |
|
| + entry | Ref. /chapter[1]/sect1[4]/sect2[5]/sect3[3]/table[1]/tgroup[1]/thead[1]/row[1]/entry[2] |
| Original | Traduction | VTL definition |
|
| + entry | Ref. /chapter[1]/sect1[4]/sect2[5]/sect3[3]/table[1]/tgroup[1]/thead[1]/row[1]/entry[3] |
| Original | Traduction | FTL definition |
|
| + entry | Ref. /chapter[1]/sect1[4]/sect2[5]/sect3[3]/table[1]/tgroup[1]/tbody[1]/row[1]/entry[1] |
| Original | Traduction | formInput (standard
input field for gathering user input) |
|
#springFormInput($path
$attributes)
<@spring.formInput path,
attributes/>
| + entry | Ref. /chapter[1]/sect1[4]/sect2[5]/sect3[3]/table[1]/tgroup[1]/tbody[1]/row[2]/entry[1] |
| Original | Traduction | formTextarea (large
text field for gathering long, freeform text input) |
|
#springFormTextarea($path
$attributes)
<@spring.formTextarea path,
attributes/>
| + entry | Ref. /chapter[1]/sect1[4]/sect2[5]/sect3[3]/table[1]/tgroup[1]/tbody[1]/row[3]/entry[1] |
| Original | Traduction | formSingleSelect (drop
down box of options allowing a single required value to be
selected) |
|
#springFormSingleSelect( $path $options
$attributes)
<@spring.formSingleSelect path, options,
attributes/>
| + entry | Ref. /chapter[1]/sect1[4]/sect2[5]/sect3[3]/table[1]/tgroup[1]/tbody[1]/row[4]/entry[1] |
| Original | Traduction | formMultiSelect (a
list box of options allowing the user to select 0 or more
values) |
|
#springFormMultiSelect($path $options
$attributes)
<@spring.formMultiSelect path, options,
attributes/>
| + entry | Ref. /chapter[1]/sect1[4]/sect2[5]/sect3[3]/table[1]/tgroup[1]/tbody[1]/row[5]/entry[1] |
| Original | Traduction | formRadioButtons (a
set of radio buttons allowing a single selection to be made
from the available choices) |
|
#springFormRadioButtons($path $options
$separator $attributes)
<@spring.formRadioButtons path, options
separator, attributes/>
| + entry | Ref. /chapter[1]/sect1[4]/sect2[5]/sect3[3]/table[1]/tgroup[1]/tbody[1]/row[6]/entry[1] |
| Original | Traduction | formCheckboxes (a set
of checkboxes allowing 0 or more values to be
selected) |
|
#springFormCheckboxes($path $options
$separator $attributes)
<@spring.formCheckboxes path, options,
separator, attributes/>
| + entry | Ref. /chapter[1]/sect1[4]/sect2[5]/sect3[3]/table[1]/tgroup[1]/tbody[1]/row[7]/entry[1] |
| Original | Traduction | showErrors (simplify
display of validation errors for the bound field) |
|
#springShowErrors($separator
$classOrStyle)
<@spring.showErrors separator,
classOrStyle/>
| + para | Ref. /chapter[1]/sect1[4]/sect2[5]/sect3[3]/para[3] |
| Original | Traduction | The parameters to any of the above macros have consistent
meanings: |
|
| + para | Ref. /chapter[1]/sect1[4]/sect2[5]/sect3[3]/itemizedlist[1]/listitem[1]/para[1] |
| Original | Traduction | path: the name of the field to bind to (ie
"command.name") |
|
| + para | Ref. /chapter[1]/sect1[4]/sect2[5]/sect3[3]/itemizedlist[1]/listitem[2]/para[1] |
| Original | Traduction | options: a Map of all the available values that can be
selected from in the input field. The keys to the map represent
the values that will be POSTed back from the form and bound to the
command object. Map objects stored against the keys are the labels
displayed on the form to the user and may be different from the
corresponding values posted back by the form. Usually such a map
is supplied as reference data by the controller. Any Map
implementation can be used depending on required behavior. For
strictly sorted maps, a SortedMap such as a
TreeMap with a suitable Comparator may be used
and for arbitrary Maps that should return values in insertion
order, use a LinkedHashMap or a
LinkedMap from commons-collections. |
|
| + para | Ref. /chapter[1]/sect1[4]/sect2[5]/sect3[3]/itemizedlist[1]/listitem[3]/para[1] |
| Original | Traduction | separator: where multiple options are available as discreet
elements (radio buttons or checkboxes), the sequence of characters
used to separate each one in the list (ie "<br>"). |
|
| + para | Ref. /chapter[1]/sect1[4]/sect2[5]/sect3[3]/itemizedlist[1]/listitem[4]/para[1] |
| Original | Traduction | attributes: an additional string of arbitrary tags or text
to be included within the HTML tag itself. This string is echoed
literally by the macro. For example, in a textarea field you may
supply attributes as 'rows="5" cols="60"' or you could pass style
information such as 'style="border:1px solid silver"'. |
|
| + para | Ref. /chapter[1]/sect1[4]/sect2[5]/sect3[3]/itemizedlist[1]/listitem[5]/para[1] |
| Original | Traduction | classOrStyle: for the showErrors macro, the name of the CSS
class that the span tag wrapping each error will use. If no
information is supplied (or the value is empty) then the errors
will be wrapped in <b></b> tags. |
|
| + para | Ref. /chapter[1]/sect1[4]/sect2[5]/sect3[3]/para[4] |
| Original | Traduction | Examples of the macros are outlined below some in FTL and some
in VTL. Where usage differences exist between the two languages, they
are explained in the notes. |
|
| + title | Ref. /chapter[1]/sect1[4]/sect2[5]/sect3[3]/sect4[1]/title[1] |
| Original | Traduction | Input Fields |
|
| + programlisting | Ref. /chapter[1]/sect1[4]/sect2[5]/sect3[3]/sect4[1]/para[1]/programlisting[1] |
| Original | Traduction | <!-- the Name field example from above using form macros in VTL -->
...
Name:
#springFormInput("command.name" "")<br>
#springShowErrors("<br>" "")<br> |
|
| + para | Ref. /chapter[1]/sect1[4]/sect2[5]/sect3[3]/sect4[1]/para[2] |
| Original | Traduction | The formInput macro takes the path parameter (command.name)
and an additional attributes parameter which is empty in the example
above. The macro, along with all other form generation macros,
performs an implicit spring bind on the path parameter. The binding
remains valid until a new bind occurs so the showErrors macro
doesn't need to pass the path parameter again - it simply operates
on whichever field a bind was last created for. |
|
| + para | Ref. /chapter[1]/sect1[4]/sect2[5]/sect3[3]/sect4[1]/para[3] |
| Original | Traduction | The showErrors macro takes a separator parameter (the
characters that will be used to separate multiple errors on a given
field) and also accepts a second parameter, this time a class name
or style attribute. Note that FreeMarker is able to specify default
values for the attributes parameter, unlike Velocity, and the two
macro calls above could be expressed as follows in FTL: |
|
| + programlisting | Ref. /chapter[1]/sect1[4]/sect2[5]/sect3[3]/sect4[1]/programlisting[1] |
| Original | Traduction | <@spring.formInput "command.name"/>
<@spring.showErrors "<br>"/> |
|
| + para | Ref. /chapter[1]/sect1[4]/sect2[5]/sect3[3]/sect4[1]/para[4] |
| Original | Traduction | Output is shown below of the form fragment generating the name
field, and displaying a validation error after the form was submitted
with no value in the field. Validation occurs through Spring's
Validation framework. |
|
| + para | Ref. /chapter[1]/sect1[4]/sect2[5]/sect3[3]/sect4[1]/para[5] |
| Original | Traduction | The generated HTML looks like this: |
|
| + programlisting | Ref. /chapter[1]/sect1[4]/sect2[5]/sect3[3]/sect4[1]/programlisting[2] |
| Original | Traduction | Name:
<input type="text" name="name" value=""
>
<br>
<b>required</b>
<br>
<br> |
|
| + para | Ref. /chapter[1]/sect1[4]/sect2[5]/sect3[3]/sect4[1]/para[6] |
| Original | Traduction | The formTextarea macro works the same way as the formInput
macro and accepts the same parameter list. Commonly, the second
parameter (attributes) will be used to pass style information or
rows and cols attributes for the textarea. |
|
| + title | Ref. /chapter[1]/sect1[4]/sect2[5]/sect3[3]/sect4[2]/title[1] |
| Original | Traduction | Selection Fields |
|
| + para | Ref. /chapter[1]/sect1[4]/sect2[5]/sect3[3]/sect4[2]/para[1] |
| Original | Traduction | Four selection field macros can be used to generate common UI
value selection inputs in your HTML forms. |
|
| + para | Ref. /chapter[1]/sect1[4]/sect2[5]/sect3[3]/sect4[2]/itemizedlist[1]/listitem[1]/para[1] |
| Original | Traduction | formSingleSelect |
|
| + para | Ref. /chapter[1]/sect1[4]/sect2[5]/sect3[3]/sect4[2]/itemizedlist[1]/listitem[2]/para[1] |
| Original | Traduction | formMultiSelect |
|
| + para | Ref. /chapter[1]/sect1[4]/sect2[5]/sect3[3]/sect4[2]/itemizedlist[1]/listitem[3]/para[1] |
| Original | Traduction | formRadioButtons |
|
| + para | Ref. /chapter[1]/sect1[4]/sect2[5]/sect3[3]/sect4[2]/itemizedlist[1]/listitem[4]/para[1] |
| Original | Traduction | formCheckboxes |
|
| + para | Ref. /chapter[1]/sect1[4]/sect2[5]/sect3[3]/sect4[2]/para[2] |
| Original | Traduction | Each of the four macros accepts a Map of options containing
the value for the form field, and the label corresponding to that
value. The value and the label can be the same. |
|
| + para | Ref. /chapter[1]/sect1[4]/sect2[5]/sect3[3]/sect4[2]/para[3] |
| Original | Traduction | An example of radio buttons in FTL is below. The form backing
object specifies a default value of 'London' for this field and so
no validation is necessary. When the form is rendered, the entire
list of cities to choose from is supplied as reference data in the
model under the name 'cityMap'. |
|
| + programlisting | Ref. /chapter[1]/sect1[4]/sect2[5]/sect3[3]/sect4[2]/programlisting[1] |
| Original | Traduction | ...
Town:
<@spring.formRadioButtons "command.address.town", cityMap, "" /><br><br> |
|
| + para | Ref. /chapter[1]/sect1[4]/sect2[5]/sect3[3]/sect4[2]/para[4] |
| Original | Traduction | This renders a line of radio buttons, one for each value in
cityMap using the separator "". No additional
attributes are supplied (the last parameter to the macro is
missing). The cityMap uses the same String for each key-value pair
in the map. The map's keys are what the form actually submits as
POSTed request parameters, map values are the labels that the user
sees. In the example above, given a list of three well known cities
and a default value in the form backing object, the HTML would
be |
|
| + programlisting | Ref. /chapter[1]/sect1[4]/sect2[5]/sect3[3]/sect4[2]/programlisting[2] |
| Original | Traduction | Town:
<input type="radio" name="address.town" value="London"
>
London
<input type="radio" name="address.town" value="Paris"
checked="checked"
>
Paris
<input type="radio" name="address.town" value="New York"
>
New York |
|
| + para | Ref. /chapter[1]/sect1[4]/sect2[5]/sect3[3]/sect4[2]/para[5] |
| Original | Traduction | If your application expects to handle cities by internal codes
for example, the map of codes would be created with suitable keys
like the example below. |
|
| + programlisting | Ref. /chapter[1]/sect1[4]/sect2[5]/sect3[3]/sect4[2]/programlisting[3] |
| Original | Traduction | protected Map referenceData(HttpServletRequest request) throws Exception {
Map cityMap = new LinkedHashMap();
cityMap.put("LDN", "London");
cityMap.put("PRS", "Paris");
cityMap.put("NYC", "New York");
Map m = new HashMap();
m.put("cityMap", cityMap);
return m;
} |
|
| + para | Ref. /chapter[1]/sect1[4]/sect2[5]/sect3[3]/sect4[2]/para[6] |
| Original | Traduction | The code would now produce output where the radio values are
the relevant codes but the user still sees the more user friendly
city names. |
|
| + programlisting | Ref. /chapter[1]/sect1[4]/sect2[5]/sect3[3]/sect4[2]/programlisting[4] |
| Original | Traduction | Town:
<input type="radio" name="address.town" value="LDN"
>
London
<input type="radio" name="address.town" value="PRS"
checked="checked"
>
Paris
<input type="radio" name="address.town" value="NYC"
>
New York |
|
| + title | Ref. /chapter[1]/sect1[4]/sect2[5]/sect3[4]/title[1] |
| Original | Traduction | overriding HTML escaping and making tags XHTML
compliant |
|
| + para | Ref. /chapter[1]/sect1[4]/sect2[5]/sect3[4]/para[1] |
| Original | Traduction | Default usage of the form macros above will result in HTML tags
that are HTML 4.01 compliant and that use the default value for HTML
escaping defined in your web.xml as used by Spring's bind support. In
order to make the tags XHTML compliant or to override the default HTML
escaping value, you can specify two variables in your template (or in
your model where they will be visible to your templates). The
advantage of specifying them in the templates is that they can be
changed to different values later in the template processing to
provide different behavior for different fields in your form. |
|
| + para | Ref. /chapter[1]/sect1[4]/sect2[5]/sect3[4]/para[2] |
| Original | Traduction | To switch to XHTML compliance for your tags, specify a value of
'true' for a model/context variable named xhtmlCompliant: |
|
| + programlisting | Ref. /chapter[1]/sect1[4]/sect2[5]/sect3[4]/programlisting[1] |
| Original | Traduction | ## for Velocity..
#set($springXhtmlCompliant = true)
<#-- for FreeMarker -->
<#assign xhtmlCompliant = true in spring> |
|
| + para | Ref. /chapter[1]/sect1[4]/sect2[5]/sect3[4]/para[3] |
| Original | Traduction | Any tags generated by the Spring macros will now be XHTML
compliant after processing this directive. |
|
| + para | Ref. /chapter[1]/sect1[4]/sect2[5]/sect3[4]/para[4] |
| Original | Traduction | In similar fashion, HTML escaping can be specified per
field: |
|
| + programlisting | Ref. /chapter[1]/sect1[4]/sect2[5]/sect3[4]/programlisting[2] |
| Original | Traduction | <#-- until this point, default HTML escaping is used -->
<#assign htmlEscape = true in spring>
<#-- next field will use HTML escaping -->
<@spring.formInput "command.name" />
<#assign htmlEscape = false in spring>
<#-- all future fields will be bound with HTML escaping off --> |
|
| |
| | sect1
| | + title | Ref. /chapter[1]/sect1[5]/title[1] |
| Original | Traduction | XSLT |
|
| + para | Ref. /chapter[1]/sect1[5]/para[1] |
| Original | Traduction | XSLT is a transformation language for XML and is popular as a view
technology within web applications. XSLT can be a good choice as a view
technology if your application naturally deals with XML, or if your model
can easily be converted to XML. The following section shows how to produce
an XML document as model data and have it transformed with XSLT in a
Spring application. |
| sect2
| | + title | Ref. /chapter[1]/sect1[5]/sect2[1]/title[1] |
| Original | Traduction | My First Words |
|
| + para | Ref. /chapter[1]/sect1[5]/sect2[1]/para[1] |
| Original | Traduction | This example is a trivial Spring application that creates a list
of words in the Controller and adds them to the model map. The map is
returned along with the view name of our XSLT view. See for details of Spring
Controllers. The XSLT view will turn the list of
words into a simple XML document ready for transformation. |
|
| + title | Ref. /chapter[1]/sect1[5]/sect2[1]/sect3[1]/title[1] |
| Original | Traduction | Bean definitions |
|
| + para | Ref. /chapter[1]/sect1[5]/sect2[1]/sect3[1]/para[1] |
| Original | Traduction | Configuration is standard for a simple Spring application. The
dispatcher servlet config file contains a reference to a
ViewResolver, URL mappings and a single controller
bean.. | + programlisting | Ref. /chapter[1]/sect1[5]/sect2[1]/sect3[1]/para[1]/programlisting[1] |
| Original | Traduction | <bean id="homeController"class="xslt.HomeController"/> |
|
..that implements our word generation 'logic'. |
|
| + title | Ref. /chapter[1]/sect1[5]/sect2[1]/sect3[2]/title[1] |
| Original | Traduction | Standard MVC controller code |
|
| + para | Ref. /chapter[1]/sect1[5]/sect2[1]/sect3[2]/para[1] |
| Original | Traduction | The controller logic is encapsulated in a subclass of
AbstractController, with the handler method being defined like so..
| + programlisting | Ref. /chapter[1]/sect1[5]/sect2[1]/sect3[2]/para[1]/programlisting[1] |
| Original | Traduction | protected ModelAndView handleRequestInternal(
HttpServletRequest req,
HttpServletResponse resp)
throws Exception {
Map map = new HashMap();
List wordList = new ArrayList();
wordList.add("hello");
wordList.add("world");
map.put("wordList", wordList);
return new ModelAndView("home", map);
} |
|
|
|
| + para | Ref. /chapter[1]/sect1[5]/sect2[1]/sect3[2]/para[2] |
| Original | Traduction | So far we've done nothing that's XSLT specific. The model data
has been created in the same way as you would for any other Spring MVC
application. Depending on the configuration of the application now,
that list of words could be rendered by JSP/JSTL by having them added
as request attributes, or they could be handled by Velocity by adding
the object to the VelocityContext. In order to have XSLT render them,
they of course have to be converted into an XML document somehow.
There are software packages available that will automatically 'domify'
an object graph, but within Spring, you have complete flexibility to
create the DOM from your model in any way you choose. This prevents
the transformation of XML playing too great a part in the structure of
your model data which is a danger when using tools to manage the
domification process. |
|
| + title | Ref. /chapter[1]/sect1[5]/sect2[1]/sect3[3]/title[1] |
| Original | Traduction | Convert the model data to XML |
|
| + para | Ref. /chapter[1]/sect1[5]/sect2[1]/sect3[3]/para[1] |
| Original | Traduction | In order to create a DOM document from our list of words or any
other model data, we subclass
org.springframework.web.servlet.view.xslt.AbstractXsltView.
In doing so, we must implement the abstract method
createDomNode(). The first parameter passed to this
method is our model Map. Here's the complete listing of the HomePage
class in our trivial word application - it uses JDOM to build the XML
document before converting it to the required W3C Node, but this is
simply because I find JDOM (and Dom4J) easier API's to handle than the
W3C API. | + programlisting | Ref. /chapter[1]/sect1[5]/sect2[1]/sect3[3]/para[1]/programlisting[1] |
| Original | Traduction |
package xslt;
// imports omitted for brevity
public class HomePage extends AbstractXsltView {
protected Node createDomNode(
Map model, String rootName, HttpServletRequest req, HttpServletResponse res
) throws Exception {
org.jdom.Document doc = new org.jdom.Document();
Element root = new Element(rootName);
doc.setRootElement(root);
List words = (List) model.get("wordList");
for (Iterator it = words.iterator(); it.hasNext();) {
String nextWord = (String) it.next();
Element e = new Element("word");
e.setText(nextWord);
root.addContent(e);
}
// convert JDOM doc to a W3C Node and return
return new DOMOutputter().output( doc );
}
} |
|
|
|
| + title | Ref. /chapter[1]/sect1[5]/sect2[1]/sect3[3]/sect4[1]/title[1] |
| Original | Traduction | Adding stylesheet parameters |
|
| + para | Ref. /chapter[1]/sect1[5]/sect2[1]/sect3[3]/sect4[1]/para[1] |
| Original | Traduction | A series of parameter name/value pairs can optionally be
defined by your subclass which will be added to the transformation
object. The parameter names must match those defined in your XSLT
template declared with <xsl:param
name="myParam">defaultValue</xsl:param> To
specify the parameters, override the method
getParameters() from AbstractXsltView and return
a Map of the name/value pairs. If your parameters
need to derive information from the current request, you can (from
version 1.1) override the getParameters(HttpServletRequest
request) method instead. |
|
| + title | Ref. /chapter[1]/sect1[5]/sect2[1]/sect3[3]/sect4[2]/title[1] |
| Original | Traduction | Formatting dates and currency |
|
| + para | Ref. /chapter[1]/sect1[5]/sect2[1]/sect3[3]/sect4[2]/para[1] |
| Original | Traduction | Unlike JSTL and Velocity, XSLT has relatively poor support for
locale based currency and date formatting. In recognition of the
fact, Spring provides a helper class that you can use from within
your createDomNode() methods to get such support.
See the javadocs for
org.springframework.web.servlet.view.xslt.FormatHelper |
|
| + title | Ref. /chapter[1]/sect1[5]/sect2[1]/sect3[4]/title[1] |
| Original | Traduction | Defining the view properties |
|
| + para | Ref. /chapter[1]/sect1[5]/sect2[1]/sect3[4]/para[1] |
| Original | Traduction | The views.properties file (or equivalent xml definition if
you're using an XML based view resolver as we did in the Velocity
examples above) looks like this for the one-view application that is
'My First Words'.. | + programlisting | Ref. /chapter[1]/sect1[5]/sect2[1]/sect3[4]/para[1]/programlisting[1] |
| Original | Traduction | home.class=xslt.HomePage
home.stylesheetLocation=/WEB-INF/xsl/home.xslt
home.root=words |
| Here, you can see how the view is tied in
with the HomePage class just written which handles the model
domification in the first property '.class'. The stylesheetLocation
property obviously points to the XSLT file which will handle the XML
transformation into HTML for us and the final property '.root' is the
name that will be used as the root of the XML document. This gets
passed to the HomePage class above in the second parameter to the
createDomNode method. |
|
| + title | Ref. /chapter[1]/sect1[5]/sect2[1]/sect3[5]/title[1] |
| Original | Traduction | Document transformation |
|
| + para | Ref. /chapter[1]/sect1[5]/sect2[1]/sect3[5]/para[1] |
| Original | Traduction | Finally, we have the XSLT code used for transforming the above
document. As highlighted in the views.properties file, it is called
home.xslt and it lives in the war file under
WEB-INF/xsl. | + programlisting | Ref. /chapter[1]/sect1[5]/sect2[1]/sect3[5]/para[1]/programlisting[1] |
| Original | Traduction | <?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text/html" omit-xml-declaration="yes"/>
<xsl:template match="/">
<html>
<head><title>Hello!</title></head>
<body>
<h1>My First Words</h1>
<xsl:for-each select="wordList/word">
<xsl:value-of select="."/><br />
</xsl:for-each>
</body>
</html>
</xsl:template>
</xsl:stylesheet> |
|
|
|
| | sect2
| | + title | Ref. /chapter[1]/sect1[5]/sect2[2]/title[1] |
| Original | Traduction | Summary |
|
| + para | Ref. /chapter[1]/sect1[5]/sect2[2]/para[1] |
| Original | Traduction | A summary of the files discussed and their location in the WAR
file is shown in the simplified WAR structure below. | + programlisting | Ref. /chapter[1]/sect1[5]/sect2[2]/para[1]/programlisting[1] |
| Original | Traduction | ProjectRoot
|
+- WebContent
|
+- WEB-INF
|
+- classes
| |
| +- xslt
| | |
| | +- HomePageController.class
| | +- HomePage.class
| |
| +- views.properties
|
+- lib
| |
| +- spring.jar
|
+- xsl
| |
| +- home.xslt
|
+- frontcontroller-servlet.xml |
| You will also need
to ensure that an XML parser and an XSLT engine are available on the
classpath. JDK 1.4 provides them by default, and most J2EE containers
will also make them available by default, but it's a possible source of
errors to be aware of. |
|
| |
| | sect1
| | + title | Ref. /chapter[1]/sect1[6]/title[1] |
| Original | Traduction | Document views (PDF/Excel) |
| sect2
| | + title | Ref. /chapter[1]/sect1[6]/sect2[1]/title[1] |
| Original | Traduction | Introduction |
|
| + para | Ref. /chapter[1]/sect1[6]/sect2[1]/para[1] |
| Original | Traduction | Returning an HTML page isn't always the best way for the user to
view the model output, and Spring makes it simple to generate a PDF
document or an Excel spreadsheet dynamically from the model data. The
document is the view and will be streamed from the server with the
correct content type to (hopefully) enable the client PC to run their
spreadsheet or PDF viewer application in response. |
|
| + para | Ref. /chapter[1]/sect1[6]/sect2[1]/para[2] |
| Original | Traduction | In order to use Excel views, you need to add the 'poi' library to
your classpath, and for PDF generation, the iText.jar. Both are included
in the main Spring distribution. |
|
| | sect2
| | + title | Ref. /chapter[1]/sect1[6]/sect2[2]/title[1] |
| Original | Traduction | Configuration and setup |
|
| + para | Ref. /chapter[1]/sect1[6]/sect2[2]/para[1] |
| Original | Traduction | Document based views are handled in an almost identical fashion to
XSLT views, and the following sections build upon the previous one by
demonstrating how the same controller used in the XSLT example is
invoked to render the same model as both a PDF document and an Excel
spreadsheet (which can also be viewed or manipulated in Open
Office). |
|
| + title | Ref. /chapter[1]/sect1[6]/sect2[2]/sect3[1]/title[1] |
| Original | Traduction | Document view definitions |
|
| + para | Ref. /chapter[1]/sect1[6]/sect2[2]/sect3[1]/para[1] |
| Original | Traduction | Firstly, let's amend the views.properties file (or xml
equivalent) and add a simple view definition for both document types.
The entire file now looks like this with the XSLT view shown from
earlier.. | + programlisting | Ref. /chapter[1]/sect1[6]/sect2[2]/sect3[1]/para[1]/programlisting[1] |
| Original | Traduction | home.class=xslt.HomePage
home.stylesheetLocation=/WEB-INF/xsl/home.xslt
home.root=words
xl.class=excel.HomePage
pdf.class=pdf.HomePage |
| If you want to start with a
template spreadsheet to add your model data to, specify the location
as the 'url' property in the view definition |
|
| + title | Ref. /chapter[1]/sect1[6]/sect2[2]/sect3[2]/title[1] |
| Original | Traduction | Controller code |
|
| + para | Ref. /chapter[1]/sect1[6]/sect2[2]/sect3[2]/para[1] |
| Original | Traduction | The controller code we'll use remains exactly the same from the
XSLT example earlier other than to change the name of the view to use.
Of course, you could be clever and have this selected based on a URL
parameter or some other logic - proof that Spring really is very good
at decoupling the views from the controllers! |
|
| + title | Ref. /chapter[1]/sect1[6]/sect2[2]/sect3[3]/title[1] |
| Original | Traduction | Subclassing for Excel views |
|
| + para | Ref. /chapter[1]/sect1[6]/sect2[2]/sect3[3]/para[1] |
| Original | Traduction | Exactly as we did for the XSLT example, we'll subclass suitable
abstract classes in order to implement custom behavior in generating
our output documents. For Excel, this involves writing a subclass of
org.springframework.web.servlet.view.document.AbstractExcelView
and implementing the buildExcelDocument |
|
| + para | Ref. /chapter[1]/sect1[6]/sect2[2]/sect3[3]/para[2] |
| Original | Traduction | Here's the complete listing for our Excel view which displays
the word list from the model map in consecutive rows of the first
column of a new spreadsheet.. | + programlisting | Ref. /chapter[1]/sect1[6]/sect2[2]/sect3[3]/para[2]/programlisting[1] |
| Original | Traduction | package excel;
// imports omitted for brevity
public class HomePage extends AbstractExcelView {
protected void buildExcelDocument(
Map model,
HSSFWorkbook wb,
HttpServletRequest req,
HttpServletResponse resp)
throws Exception {
HSSFSheet sheet;
HSSFRow sheetRow;
HSSFCell cell;
// Go to the first sheet
// getSheetAt: only if wb is created from an existing document
//sheet = wb.getSheetAt( 0 );
sheet = wb.createSheet("Spring");
sheet.setDefaultColumnWidth((short)12);
// write a text at A1
cell = getCell( sheet, 0, 0 );
setText(cell,"Spring-Excel test");
List words = (List ) model.get("wordList");
for (int i=0; i < words.size(); i++) {
cell = getCell( sheet, 2+i, 0 );
setText(cell, (String) words.get(i));
}
}
} |
|
|
|
| + para | Ref. /chapter[1]/sect1[6]/sect2[2]/sect3[3]/para[3] |
| Original | Traduction | If you now amend the controller such that it returns
xl as the name of the view (return new
ModelAndView("xl", map);) and run your application again,
you should find that the Excel spreadsheet is created and downloaded
automagically when you request the same page as before. |
|
| + title | Ref. /chapter[1]/sect1[6]/sect2[2]/sect3[4]/title[1] |
| Original | Traduction | Subclassing for PDF views |
|
| + para | Ref. /chapter[1]/sect1[6]/sect2[2]/sect3[4]/para[1] |
| Original | Traduction | The PDF version of the word list is even simpler. This time, the
class extends
org.springframework.web.servlet.view.document.AbstractPdfView
and implements the buildPdfDocument() method as
follows.. | + programlisting | Ref. /chapter[1]/sect1[6]/sect2[2]/sect3[4]/para[1]/programlisting[1] |
| Original | Traduction | package pdf;
// imports omitted for brevity
public class PDFPage extends AbstractPdfView {
protected void buildPdfDocument(
Map model,
Document doc,
PdfWriter writer,
HttpServletRequest req,
HttpServletResponse resp)
throws Exception {
List words = (List) model.get("wordList");
for (int i=0; i<words.size(); i++)
doc.add( new Paragraph((String) words.get(i)));
}
} |
| Once again, amend the controller to return the
pdf view with a return new
ModelAndView("pdf", map); and reload the URL in your
application. This time a PDF document should appear listing each of
the words in the model map. |
|
| |
| | sect1
| | + title | Ref. /chapter[1]/sect1[7]/title[1] |
| Original | Traduction | Tapestry |
|
| + para | Ref. /chapter[1]/sect1[7]/para[1] |
| Original | Traduction | Tapestry is a powerful, component-oriented web application framework
from Apache's Jakarta project (). While Spring has its
own powerful web ui layer, there are a number of unique advantages to
building a J2EE application using a combination of Tapestry for the web
ui, and the Spring container for the lower layers. This document attempts
to detail a few best practices for combining these two frameworks. It is
expected that you are relatively familiar with both Tapestry and Spring
Framework basics, so they will not be explained here. General introductory
documentation for both Tapestry and Spring Framework are available on
their respective web sites. |
| sect2
| | + title | Ref. /chapter[1]/sect1[7]/sect2[1]/title[1] |
| Original | Traduction | Architecture |
|
| + para | Ref. /chapter[1]/sect1[7]/sect2[1]/para[1] |
| Original | Traduction | A typical layered J2EE application built with Tapestry and Spring
will consist of a top UI layer built with Tapestry, and a number of
lower layers, hosted out of one or more Spring Application
Contexts. |
|
User Interface Layer:
| + para | Ref. /chapter[1]/sect1[7]/sect2[1]/itemizedlist[1]/listitem[1]/para[2] |
| Original | Traduction | - concerned with the user interface |
|
| + para | Ref. /chapter[1]/sect1[7]/sect2[1]/itemizedlist[1]/listitem[1]/para[3] |
| Original | Traduction | - contains some application logic |
|
| + para | Ref. /chapter[1]/sect1[7]/sect2[1]/itemizedlist[1]/listitem[1]/para[4] |
| Original | Traduction | - provided by Tapestry |
|
| + para | Ref. /chapter[1]/sect1[7]/sect2[1]/itemizedlist[1]/listitem[1]/para[5] |
| Original | Traduction | - aside from providing UI via Tapestry, code in this layer
does its work via objects which implement interfaces from the
Service Layer. The actual objects which implement these service
layer interfaces are obtained from a Spring Application
Context. |
|
Service Layer:
| + para | Ref. /chapter[1]/sect1[7]/sect2[1]/itemizedlist[1]/listitem[2]/para[2] |
| Original | Traduction | - application specific 'service' code |
|
| + para | Ref. /chapter[1]/sect1[7]/sect2[1]/itemizedlist[1]/listitem[2]/para[3] |
| Original | Traduction | - works with domain objects, and uses the Mapper API to get
those domain objects into and out of some sort of repository
(database) |
|
| + para | Ref. /chapter[1]/sect1[7]/sect2[1]/itemizedlist[1]/listitem[2]/para[4] |
| Original | Traduction | - hosted in one or more Spring contexts |
|
| + para | Ref. /chapter[1]/sect1[7]/sect2[1]/itemizedlist[1]/listitem[2]/para[5] |
| Original | Traduction | - code in this layer manipulates objects in the domain model,
in an application specific fashion. It does its work via other code
in this layer, and via the Mapper API. An object in this layer is
given the specific mapper implementations it needs to work with, via
the Spring context. |
|
| + para | Ref. /chapter[1]/sect1[7]/sect2[1]/itemizedlist[1]/listitem[2]/para[6] |
| Original | Traduction | - since code in this layer is hosted in the Spring context, it
may be transactionally wrapped by the Spring context, as opposed to
managing its own transactions |
|
Domain Model:
| + para | Ref. /chapter[1]/sect1[7]/sect2[1]/itemizedlist[1]/listitem[3]/para[2] |
| Original | Traduction | - domain specific object hierarchy, which deals with data and
logic specific to this domain |
|
| + para | Ref. /chapter[1]/sect1[7]/sect2[1]/itemizedlist[1]/listitem[3]/para[3] |
| Original | Traduction | - although the domain object hierarchy is built with the idea
that it is persisted somehow and makes some general concessions to
this (for example, bidirectional relationships), it generally has no
knowledge of other layers. As such, it may be tested in isolation,
and used with different mapping implementations for production vs.
testing. |
|
| + para | Ref. /chapter[1]/sect1[7]/sect2[1]/itemizedlist[1]/listitem[3]/para[4] |
| Original | Traduction | - these objects may be standalone, or used in conjunction with
a Spring application context to take advantage of some of the
benefits of the context, e.g., isolation, inversion of control,
different strategy implementations, etc. |
|
Data Source Layer:
| + para | Ref. /chapter[1]/sect1[7]/sect2[1]/itemizedlist[1]/listitem[4]/para[2] |
| Original | Traduction | - Mapper API (also called Data Access Objects): an API used to
persist the domain model to a repository of some sort (generally a
DB, but could be the filesystem, memory, etc.) |
|
| + para | Ref. /chapter[1]/sect1[7]/sect2[1]/itemizedlist[1]/listitem[4]/para[3] |
| Original | Traduction | - Mapper API implementations: one or more specific
implementations of the Mapper API, for example, a Hibernate-specific
mapper, a JDO-specific mapper, JDBC-specific mapper, or a memory
mapper. |
|
| + para | Ref. /chapter[1]/sect1[7]/sect2[1]/itemizedlist[1]/listitem[4]/para[4] |
| Original | Traduction | - mapper implementations live in one or more Spring
Application Contexts. A service layer object is given the mapper
objects it needs to work with via the context. |
|
Database, filesystem, or other
repositories:
| + para | Ref. /chapter[1]/sect1[7]/sect2[1]/itemizedlist[1]/listitem[5]/para[2] |
| Original | Traduction | - objects in the domain model are stored into one or more
repositories via one or more mapper implementations |
|
| + para | Ref. /chapter[1]/sect1[7]/sect2[1]/itemizedlist[1]/listitem[5]/para[3] |
| Original | Traduction | - a repository may be very simple (e.g. filesystem), or may
have its own representation of the data from the domain model (i.e.
a schema in a db). It does not know about other layers
howerver. |
|
| | sect2
| | + title | Ref. /chapter[1]/sect1[7]/sect2[2]/title[1] |
| Original | Traduction | Implementation |
|
| + para | Ref. /chapter[1]/sect1[7]/sect2[2]/para[1] |
| Original | Traduction | The only real question (which needs to be addressed by this
document), is how Tapestry pages get access to service implementations,
which are simply beans defined in an instance of the Spring Application
Context. |
|
| + title | Ref. /chapter[1]/sect1[7]/sect2[2]/sect3[1]/title[1] |
| Original | Traduction | Sample application context |
|
| + para | Ref. /chapter[1]/sect1[7]/sect2[2]/sect3[1]/para[1] |
| Original | Traduction | Assume we have the following simple Application Context
definition, in xml form: | + programlisting | Ref. /chapter[1]/sect1[7]/sect2[2]/sect3[1]/para[1]/programlisting[1] |
| Original | Traduction | <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<!-- ========================= GENERAL DEFINITIONS ========================= -->
<!-- ========================= PERSISTENCE DEFINITIONS ========================= -->
<!-- the DataSource -->
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName"><value>java:DefaultDS</value></property>
<property name="resourceRef"><value>false</value></property>
</bean>
<!-- define a Hibernate Session factory via a Spring LocalSessionFactoryBean -->
<bean id="hibSessionFactory"
class="org.springframework.orm.hibernate.LocalSessionFactoryBean">
<property name="dataSource"><ref bean="dataSource"/></property>
</bean>
<!--
- Defines a transaction manager for usage in business or data access objects.
- No special treatment by the context, just a bean instance available as reference
- for business objects that want to handle transactions, e.g. via TransactionTemplate.
-->
<bean id="transactionManager"
class="org.springframework.transaction.jta.JtaTransactionManager">
</bean>
<bean id="mapper"
class="com.whatever.dataaccess.mapper.hibernate.MapperImpl">
<property name="sessionFactory"><ref bean="hibSessionFactory"/></property>
</bean>
<!-- ========================= BUSINESS DEFINITIONS ========================= -->
<!-- AuthenticationService, including tx interceptor -->
<bean id="authenticationServiceTarget"
class="com.whatever.services.service.user.AuthenticationServiceImpl">
<property name="mapper"><ref bean="mapper"/></property>
</bean>
<bean id="authenticationService"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager"><ref bean="transactionManager"/></property>
<property name="target"><ref bean="authenticationServiceTarget"/></property>
<property name="proxyInterfacesOnly"><value>true</value></property>
<property name="transactionAttributes">
<props>
<prop key="*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
<!-- UserService, including tx interceptor -->
<bean id="userServiceTarget"
class="com.whatever.services.service.user.UserServiceImpl">
<property name="mapper"><ref bean="mapper"/></property>
</bean>
<bean id="userService"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager"><ref bean="transactionManager"/></property>
<property name="target"><ref bean="userServiceTarget"/></property>
<property name="proxyInterfacesOnly"><value>true</value></property>
<property name="transactionAttributes">
<props>
<prop key="*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
</beans> |
| Inside the Tapestry application, we need to
load this application context, and allow Tapestry pages to get the
authenticationService and userService beans, which implement the
AuthenticationService and UserService interfaces, respectively. |
|
| + title | Ref. /chapter[1]/sect1[7]/sect2[2]/sect3[2]/title[1] |
| Original | Traduction | Obtaining beans in Tapestry pages |
|
| + para | Ref. /chapter[1]/sect1[7]/sect2[2]/sect3[2]/para[1] |
| Original | Traduction | At this point, the application context is available to a web
application by calling Spring's static utility function
WebApplicationContextUtils.getApplicationContext(servletContext),
where servletContext is the standard ServletContext
from the J2EE Servlet specification. As such, one simple mechanism for
a page to get an instance of the UserService, for example, would be
with code such as: | + programlisting | Ref. /chapter[1]/sect1[7]/sect2[2]/sect3[2]/para[1]/programlisting[1] |
| Original | Traduction | WebApplicationContext appContext = WebApplicationContextUtils.getApplicationContext(
getRequestCycle().getRequestContext().getServlet().getServletContext());
UserService userService = (UserService) appContext.getBean("userService");
... some code which uses UserService |
| This mechanism does
work. It can be made a lot less verbose by encapsulating most of the
functionality in a method in the base class for the page or component.
However, in some respects it goes against the Inversion of Control
approach which Spring encourages, which is being used in other layers
of this app, in that ideally you would like the page to not have to
ask the context for a specific bean by name, and in fact, the page
would ideally not know about the context at all. |
|
| + para | Ref. /chapter[1]/sect1[7]/sect2[2]/sect3[2]/para[2] |
| Original | Traduction | Luckily, there is a mechanism to allow this. We rely upon the
fact that Tapestry already has a mechanism to declaratively add
properties to a page, and it is in fact the preferred approach to
manage all properties on a page in this declarative fashion, so that
Tapestry can properly manage their lifecycle as part of the page and
component lifecycle. |
|
| + title | Ref. /chapter[1]/sect1[7]/sect2[2]/sect3[3]/title[1] |
| Original | Traduction | Exposing the application context to Tapestry |
|
| + para | Ref. /chapter[1]/sect1[7]/sect2[2]/sect3[3]/para[1] |
| Original | Traduction | First we need to make the ApplicationContext
available to the Tapestry page or Component without having to have the
ServletContext; this is because at the stage in the
page's/component's lifecycle when we need to access the
ApplicationContext, the
ServletContext won't be easily available to the
page, so we can't use
WebApplicationContextUtils.getApplicationContext(servletContext)
directly. One way is by defining a custom version of the Tapestry
IEngine which exposes this for us: | + programlisting | Ref. /chapter[1]/sect1[7]/sect2[2]/sect3[3]/para[1]/programlisting[1] |
| Original | Traduction | package com.whatever.web.xportal;
...
import ...
...
public class MyEngine extends org.apache.tapestry.engine.BaseEngine {
public static final String APPLICATION_CONTEXT_KEY = "appContext";
/**
* @see org.apache.tapestry.engine.AbstractEngine#setupForRequest(org.apache.tapestry.request.RequestContext)
*/
protected void setupForRequest(RequestContext context) {
super.setupForRequest(context);
// insert ApplicationContext in global, if not there
Map global = (Map) getGlobal();
ApplicationContext ac = (ApplicationContext) global.get(APPLICATION_CONTEXT_KEY);
if (ac == null) {
ac = WebApplicationContextUtils.getWebApplicationContext(
context.getServlet().getServletContext()
);
global.put(APPLICATION_CONTEXT_KEY, ac);
}
}
} |
| This engine class places the Spring Application Context as
an attribute called "appContext" in this Tapestry app's 'Global'
object. Make sure to register the fact that this special IEngine
instance should be used for this Tapestry application, with an entry
in the Tapestry application definition file. For example:
| + programlisting | Ref. /chapter[1]/sect1[7]/sect2[2]/sect3[3]/para[1]/programlisting[2] |
| Original | Traduction | file: xportal.application:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE application PUBLIC
"-//Apache Software Foundation//Tapestry Specification 3.0//EN"
"http://jakarta.apache.org/tapestry/dtd/Tapestry_3_0.dtd">
<application
name="Whatever xPortal"
engine-class="com.whatever.web.xportal.MyEngine">
</application> |
|
|
|
| + title | Ref. /chapter[1]/sect1[7]/sect2[2]/sect3[4]/title[1] |
| Original | Traduction | Component definition files |
|
| + para | Ref. /chapter[1]/sect1[7]/sect2[2]/sect3[4]/para[1] |
| Original | Traduction | Now in our page or component definition file (*.page or *.jwc),
we simply add property-specification elements to grab the beans we
need out of the ApplicationContext, and create page or component
properties for them. For example: | + programlisting | Ref. /chapter[1]/sect1[7]/sect2[2]/sect3[4]/para[1]/programlisting[1] |
| Original | Traduction | <property-specification name="userService"
type="com.whatever.services.service.user.UserService">
global.appContext.getBean("userService")
</property-specification>
<property-specification name="authenticationService"
type="com.whatever.services.service.user.AuthenticationService">
global.appContext.getBean("authenticationService")
</property-specification> |
| The OGNL expression
inside the property-specification specifies the initial value for the
property, as a bean obtained from the context. The entire page
definition might look like this: | + programlisting | Ref. /chapter[1]/sect1[7]/sect2[2]/sect3[4]/para[1]/programlisting[2] |
| Original | Traduction | <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE page-specification PUBLIC
"-//Apache Software Foundation//Tapestry Specification 3.0//EN"
"http://jakarta.apache.org/tapestry/dtd/Tapestry_3_0.dtd">
<page-specification class="com.whatever.web.xportal.pages.Login">
<property-specification name="username" type="java.lang.String"/>
<property-specification name="password" type="java.lang.String"/>
<property-specification name="error" type="java.lang.String"/>
<property-specification name="callback" type="org.apache.tapestry.callback.ICallback" persistent="yes"/>
<property-specification name="userService"
type="com.whatever.services.service.user.UserService">
global.appContext.getBean("userService")
</property-specification>
<property-specification name="authenticationService"
type="com.whatever.services.service.user.AuthenticationService">
global.appContext.getBean("authenticationService")
</property-specification>
<bean name="delegate" class="com.whatever.web.xportal.PortalValidationDelegate"/>
<bean name="validator" class="org.apache.tapestry.valid.StringValidator" lifecycle="page">
<set-property name="required" expression="true"/>
<set-property name="clientScriptingEnabled" expression="true"/>
</bean>
<component id="inputUsername" type="ValidField">
<static-binding name="displayName" value="Username"/>
<binding name="value" expression="username"/>
<binding name="validator" expression="beans.validator"/>
</component>
<component id="inputPassword" type="ValidField">
<binding name="value" expression="password"/>
<binding name="validator" expression="beans.validator"/>
<static-binding name="displayName" value="Password"/>
<binding name="hidden" expression="true"/>
</component>
</page-specification> |
|
|
|
| + title | Ref. /chapter[1]/sect1[7]/sect2[2]/sect3[5]/title[1] |
| Original | Traduction | Adding abstract accessors |
|
| + para | Ref. /chapter[1]/sect1[7]/sect2[2]/sect3[5]/para[1] |
| Original | Traduction | Now in the Java class definition for the page or component
itself, all we need to do is add an abstract getter method for the
properties we have defined, to access them. When the page or component
is actually loaded by Tapestry, it performs runtime code
instrumentation on the classfile to add the properties which have been
defined, and hook up the abstract getter methods to the newly created
fields. For example: | + programlisting | Ref. /chapter[1]/sect1[7]/sect2[2]/sect3[5]/para[1]/programlisting[1] |
| Original | Traduction | // our UserService implementation; will come from page definition
public abstract UserService getUserService();
// our AuthenticationService implementation; will come from page definition
public abstract AuthenticationService getAuthenticationService(); |
|
For completeness, the entire Java class, for a login page in this
example, might look like this: | + programlisting | Ref. /chapter[1]/sect1[7]/sect2[2]/sect3[5]/para[1]/programlisting[2] |
| Original | Traduction | package com.whatever.web.xportal.pages;
/**
* Allows the user to login, by providing username and password.
* After successfully logging in, a cookie is placed on the client browser
* that provides the default username for future logins (the cookie
* persists for a week).
*/
public abstract class Login extends BasePage implements ErrorProperty, PageRenderListener {
/** the key under which the authenticated user object is stored in the visit as */
public static final String USER_KEY = "user";
/**
* The name of a cookie to store on the user's machine that will identify
* them next time they log in.
**/
private static final String COOKIE_NAME = Login.class.getName() + ".username";
private final static int ONE_WEEK = 7 * 24 * 60 * 60;
// --- attributes
public abstract String getUsername();
public abstract void setUsername(String username);
public abstract String getPassword();
public abstract void setPassword(String password);
public abstract ICallback getCallback();
public abstract void setCallback(ICallback value);
public abstract UserService getUserService();
public abstract AuthenticationService getAuthenticationService();
// --- methods
protected IValidationDelegate getValidationDelegate() {
return (IValidationDelegate) getBeans().getBean("delegate");
}
protected void setErrorField(String componentId, String message) {
IFormComponent field = (IFormComponent) getComponent(componentId);
IValidationDelegate delegate = getValidationDelegate();
delegate.setFormComponent(field);
delegate.record(new ValidatorException(message));
}
/**
* Attempts to login.
*
* <p>If the user name is not known, or the password is invalid, then an error
* message is displayed.
*
**/
public void attemptLogin(IRequestCycle cycle) {
String password = getPassword();
// Do a little extra work to clear out the password.
setPassword(null);
IValidationDelegate delegate = getValidationDelegate();
delegate.setFormComponent((IFormComponent) getComponent("inputPassword"));
delegate.recordFieldInputValue(null);
// An error, from a validation field, may already have occurred.
if (delegate.getHasErrors())
return;
try {
User user = getAuthenticationService().login(getUsername(), getPassword());
loginUser(user, cycle);
}
catch (FailedLoginException ex) {
this.setError("Login failed: " + ex.getMessage());
return;
}
}
/**
* Sets up the {@link User} as the logged in user, creates
* a cookie for their username (for subsequent logins),
* and redirects to the appropriate page, or
* a specified page).
*
**/
public void loginUser(User user, IRequestCycle cycle) {
String username = user.getUsername();
// Get the visit object; this will likely force the
// creation of the visit object and an HttpSession.
Map visit = (Map) getVisit();
visit.put(USER_KEY, user);
// After logging in, go to the MyLibrary page, unless otherwise
// specified.
ICallback callback = getCallback();
if (callback == null)
cycle.activate("Home");
else
callback.performCallback(cycle);
// I've found that failing to set a maximum age and a path means that
// the browser (IE 5.0 anyway) quietly drops the cookie.
IEngine engine = getEngine();
Cookie cookie = new Cookie(COOKIE_NAME, username);
cookie.setPath(engine.getServletPath());
cookie.setMaxAge(ONE_WEEK);
// Record the user's username in a cookie
cycle.getRequestContext().addCookie(cookie);
engine.forgetPage(getPageName());
}
public void pageBeginRender(PageEvent event) {
if (getUsername() == null)
setUsername(getRequestCycle().getRequestContext().getCookieValue(COOKIE_NAME));
}
} |
|
|
|
| | sect2
| | + title | Ref. /chapter[1]/sect1[7]/sect2[3]/title[1] |
| Original | Traduction | Summary |
|
| + para | Ref. /chapter[1]/sect1[7]/sect2[3]/para[1] |
| Original | Traduction | In this example, we've managed to allow service beans defined in
the Spring ApplicationContext to be provided to the
page in a declarative fashion. The page class does not know where the
service implementations are coming from, and in fact it is easy to slip
in another implementation, for example, during testing. This inversion
of control is one of the prime goals and benefits of the Spring
Framework, and we have managed to extend it all the way up the J2EE
stack in this Tapestry application. |
|
| |
| | sect1
| | + title | Ref. /chapter[1]/sect1[8]/title[1] |
| Original | Traduction | JasperReports |
|
| + para | Ref. /chapter[1]/sect1[8]/para[1] |
| Original | Traduction |
JasperReports ()
is a powerful, open-source reporting engine that supports
the creation of report designs using an easily understood XML file formats.
JasperReports is capable of rendering reports output into four different
formats: CSV, Excel, HTML and PDF.
|
| sect2
| | + title | Ref. /chapter[1]/sect1[8]/sect2[1]/title[1] |
| Original | Traduction | Dependencies |
|
| + para | Ref. /chapter[1]/sect1[8]/sect2[1]/para[1] |
| Original | Traduction |
Your application will need to include the latest release of JasperReports,
which at the time of writing was 0.6.1. JasperReports itself depends on the
following projects: |
|
| + para | Ref. /chapter[1]/sect1[8]/sect2[1]/itemizedlist[1]/listitem[1]/para[1] |
| Original | Traduction | BeanShell |
|
| + para | Ref. /chapter[1]/sect1[8]/sect2[1]/itemizedlist[1]/listitem[2]/para[1] |
| Original | Traduction | Commons BeanUtils |
|
| + para | Ref. /chapter[1]/sect1[8]/sect2[1]/itemizedlist[1]/listitem[3]/para[1] |
| Original | Traduction | Commons Collections |
|
| + para | Ref. /chapter[1]/sect1[8]/sect2[1]/itemizedlist[1]/listitem[4]/para[1] |
| Original | Traduction | Commons Digester |
|
| + para | Ref. /chapter[1]/sect1[8]/sect2[1]/itemizedlist[1]/listitem[5]/para[1] |
| Original | Traduction | Commons Logging |
|
| + para | Ref. /chapter[1]/sect1[8]/sect2[1]/itemizedlist[1]/listitem[6]/para[1] |
| Original | Traduction | iText |
|
| + para | Ref. /chapter[1]/sect1[8]/sect2[1]/itemizedlist[1]/listitem[7]/para[1] |
| Original | Traduction | POI |
|
| + para | Ref. /chapter[1]/sect1[8]/sect2[1]/para[2] |
| Original | Traduction | JasperReports also requires a JAXP compliant XML parser.
|
|
| | sect2
| | + title | Ref. /chapter[1]/sect1[8]/sect2[2]/title[1] |
| Original | Traduction | Configuration |
|
| + para | Ref. /chapter[1]/sect1[8]/sect2[2]/para[1] |
| Original | Traduction |
To configure JasperReports views in your ApplicationContext
you have to define a ViewResolver to map view names to the
appropriate view class depending on which format you want your report rendered in.
|
|
| + title | Ref. /chapter[1]/sect1[8]/sect2[2]/sect3[1]/title[1] |
| Original | Traduction | Configuring the ViewResolver |
|
| + para | Ref. /chapter[1]/sect1[8]/sect2[2]/sect3[1]/para[1] |
| Original | Traduction |
Typically, you will use the ResourceBundleViewResolver
to map view names to view classes and files in a properties file
| + programlisting | Ref. /chapter[1]/sect1[8]/sect2[2]/sect3[1]/para[1]/programlisting[1] |
| Original | Traduction | <bean id="viewResolver" class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
<property name="basename">
<value>views</value>
</property>
</bean>
|
|
|
|
| + para | Ref. /chapter[1]/sect1[8]/sect2[2]/sect3[1]/para[2] |
| Original | Traduction | Here we've configured an instance of ResourceBundleViewResolver
which will look for view mappings in the resource bundle with base name views.
The exact contents of this file is described in the next section.
|
|
| + title | Ref. /chapter[1]/sect1[8]/sect2[2]/sect3[2]/title[1] |
| Original | Traduction | Configuring the Views |
|
| + para | Ref. /chapter[1]/sect1[8]/sect2[2]/sect3[2]/para[1] |
| Original | Traduction |
Spring contains four different View implementations for JasperReports
each of which corresponds to one of the four output formats supported
by JasperReports: |
|
| + title | Ref. /chapter[1]/sect1[8]/sect2[2]/sect3[2]/table[1]/title[1] |
| Original | Traduction | JasperReports View Classes |
|
| + entry | Ref. /chapter[1]/sect1[8]/sect2[2]/sect3[2]/table[1]/tgroup[1]/thead[1]/row[1]/entry[1] |
| Original | Traduction | Class Name |
|
| + entry | Ref. /chapter[1]/sect1[8]/sect2[2]/sect3[2]/table[1]/tgroup[1]/thead[1]/row[1]/entry[2] |
| Original | Traduction | Render Format |
|
JasperReportsCsvView
| + entry | Ref. /chapter[1]/sect1[8]/sect2[2]/sect3[2]/table[1]/tgroup[1]/tbody[1]/row[1]/entry[2] |
| Original | Traduction | CSV |
|
JasperReportsHtmlView
| + entry | Ref. /chapter[1]/sect1[8]/sect2[2]/sect3[2]/table[1]/tgroup[1]/tbody[1]/row[2]/entry[2] |
| Original | Traduction | HTML |
|
JasperReportsPdfView
| + entry | Ref. /chapter[1]/sect1[8]/sect2[2]/sect3[2]/table[1]/tgroup[1]/tbody[1]/row[3]/entry[2] |
| Original | Traduction | PDF |
|
JasperReportsXlsView
| + entry | Ref. /chapter[1]/sect1[8]/sect2[2]/sect3[2]/table[1]/tgroup[1]/tbody[1]/row[4]/entry[2] |
| Original | Traduction | Microsoft Excel |
|
| + para | Ref. /chapter[1]/sect1[8]/sect2[2]/sect3[2]/para[2] |
| Original | Traduction |
Mapping one of these classes to a view name and a report file is simply
a matter of adding the appropriate entries into the resource bundle configured
in the previous section as shown here:
|
|
| + programlisting | Ref. /chapter[1]/sect1[8]/sect2[2]/sect3[2]/programlisting[1] |
| Original | Traduction | simpleReport.class=org.springframework.web.servlet.view.jasperreports.JasperReportsPdfView
simpleReport.url=/WEB-INF/reports/DataSourceReport.jasper
|
|
| + para | Ref. /chapter[1]/sect1[8]/sect2[2]/sect3[2]/para[3] |
| Original | Traduction |
Here you can see that the view with name, simpleReport, is mapped
to the JasperReportsPdfView class. This will cause the output of
this report to be rendered in PDF format. The url property of the
view is set to the location of the underlying report file.
|
|
| + title | Ref. /chapter[1]/sect1[8]/sect2[2]/sect3[3]/title[1] |
| Original | Traduction | About Report Files |
|
| + para | Ref. /chapter[1]/sect1[8]/sect2[2]/sect3[3]/para[1] |
| Original | Traduction |
JasperReports has two distinct types of report file: the design file,
which has a .jrxml extension, and the compiled
report file, which has a .jasper extension. Typically,
you use the JasperReports Ant task to compile your .jrxml
design file into a .jasper file before deploying it
into your application. With Spring you can map either of these files to
your report file and Spring will take care of compiling the
.jrxml file on the fly for you. You should note that
after a .jrxml file is compiled by Spring, the compiled
report is cached for the life of the application. To make changes to the
file you will need to restart your application.
|
|
| | sect2
| | + title | Ref. /chapter[1]/sect1[8]/sect2[3]/title[1] |
| Original | Traduction | Populating the ModelAndView |
|
| + para | Ref. /chapter[1]/sect1[8]/sect2[3]/para[1] |
| Original | Traduction |
In order to render your report correctly in the format you have chosen, you must supply Spring with all
of the data needed to populate your report. For JasperReports this means you must pass in all
report parameters along with the report datasource. Report parameters are simple name/value pairs
and can be added be to the Map for your model as you would add any name/value pair.
|
|
| + para | Ref. /chapter[1]/sect1[8]/sect2[3]/para[2] |
| Original | Traduction |
When adding the datasource to the model you have two approaches to choose from. The first approach is to
add an instance of JRDataSource or Collection to the model
Map under any arbitrary key. Spring will then locate this object in the model
and treat it as the report datasource. For example, you may populate your model like this:
| + programlisting | Ref. /chapter[1]/sect1[8]/sect2[3]/para[2]/programlisting[1] |
| Original | Traduction | private Map getModel() {
Map model = new HashMap();
Collection beanData = getBeanData();
model.put("myBeanData", beanData);
return model;
} |
|
|
|
| + para | Ref. /chapter[1]/sect1[8]/sect2[3]/para[3] |
| Original | Traduction |
The second approach is to add the instance of JRDataSource
or Collection under a specific key and then configure this key using the reportDataKey
property of the view class. In both cases Spring will instances of Collection in a
JRBeanCollectionDataSource instance. For example:
| + programlisting | Ref. /chapter[1]/sect1[8]/sect2[3]/para[3]/programlisting[1] |
| Original | Traduction | private Map getModel() {
Map model = new HashMap();
Collection beanData = getBeanData();
Collection someData = getSomeData();
model.put("myBeanData", beanData);
model.put("someData", someData);
return model;
} |
|
Here you can see that two Collection instances are being added to the model. To
ensure that the correct one is used, we simply modify our view configuration as appropriate:
| + programlisting | Ref. /chapter[1]/sect1[8]/sect2[3]/para[3]/programlisting[2] |
| Original | Traduction | simpleReport.class=org.springframework.web.servlet.view.jasperreports.JasperReportsPdfView
simpleReport.url=/WEB-INF/reports/DataSourceReport.jasper
simpleReport.reportDataKey=myBeanData
|
|
|
|
| + para | Ref. /chapter[1]/sect1[8]/sect2[3]/para[4] |
| Original | Traduction | Be aware that when using the first approach, Spring will use the first instance of JRDataSource
or Collection that it encounters. If you need to place multiple instances of JRDataSource or
Collection into the model then you need to use the second approach.
|
|
| |
| |
|