Spring Roo comes with a Web MVC add-on, which supports creating Spring MVC controllers and JSPX views from Roo-managed JPA entities. Spring Roo provides multiple commands, processed by a Web MVC add-on, to help with the auto-generation of the Spring MVC controllers and JSPX views. By default the controllers generated by Roo support creating, reading, updating, and deleting JPA entities from the data store.
The following commands are provided by Roo for creating controllers:
controller all
: It is used for scaffolding a Spring Web MVC controller for each JPA entity in the application for which a controller doesn't already exist. The controller all
command doesn't give you any control over application functionality supported by generated controllers.controller scaffold
: It is used for scaffolding a Spring Web MVC controller corresponding to a JPA entity. Unlike controller all
command, the controller scaffold
command provides optional arguments, which allow you to control some of the application functionalities supported by the generated controller. For instance, you can specify that methods for creating, updating, and deleting the JPA entity are not generated for the controller.controller class
: It is used for manually creating a controller. The controller class
command generates the skeleton of a controller, leaving the implementation of the controller to the developer.In this recipe, we'll look at the controller all
command and discuss in detail what happens behind the scenes when you execute the controller all
command. Creating a Spring MVC controller for a specific JPA entity recipe shows usage of the controller scaffold command and manually creating a Spring MVC controller for a JPA entity recipe shows how to use the controller class command to manually implement a controller.
Create a sub-directory ch04-recipe
inside the C:
oo-cookbook
directory.
Copy the ch04_web-app.roo
script into the ch04-recipe
directory.
Execute the ch04_web-app.roo
script that creates a flight-app
Roo project, sets up Hibernate as a persistence provider, configures MySQL as the database for the application, creates Flight
and FlightDescription
JPA entities, and defines a many-to-one relationship between Flight
and FlightDescription
entities. If you are using a different database than MySQL or your connection settings are different than what is specified in the script, then modify the script accordingly.
Start the Roo shell from the C:
oo-cookbookch04-recipe
directory.
To create views using the controller all
command follow the given steps:
controller
all
command, specifying ~.web as the value of the package argument, as shown here:.. roo> controller all --package ~.web Created SRC_MAIN_JAVA..web Created SRC_MAIN_JAVA..webFlightDescriptionController.java Created SRC_MAIN_WEBAPPWEB-INFspring Created SRC_MAIN_WEBAPPWEB-INFspringwebmvc-config.xml Created SRC_MAIN_JAVA..webApplicationConversionServiceFacto ryBean.java Created SRC_MAIN_JAVA..webFlightDescriptionController_Roo_C ontroller.aj Created SRC_MAIN_JAVA..webApplicationConversionServiceFactoryBean_Roo_ConversionService.aj Created SRC_MAIN_JAVA..webFlightDescriptionController_Roo_Controller_Finder.aj .. Created SRC_MAIN_WEBAPPimages Created SRC_MAIN_WEBAPPstyles Created SRC_MAIN_WEBAPPWEB-INFclasses Created SRC_MAIN_WEBAPPWEB-INFclassesalt.properties Created SRC_MAIN_WEBAPPWEB-INFclassesstandard.properties Created SRC_MAIN_WEBAPPWEB-INFlayouts Created SRC_MAIN_WEBAPPWEB-INFlayoutsdefault.jspx Created SRC_MAIN_WEBAPPWEB-INFlayoutslayouts.xml Created SRC_MAIN_WEBAPPWEB-INFviews Created SRC_MAIN_WEBAPPWEB-INFviewsflightdescriptions Created SRC_MAIN_WEBAPPWEB-INFviewsflights
The output of the controller all
command shows the creation of JSPX views, controllers, directories for images, styles, and so on. Note that only a partial output has been shown in the given code for brevity.
flight-app
project during processing of the controller
all
command, execute the perform eclipse command of Roo to update the .classpath file of the Eclipse project:..roo> perform eclipse
As the output of executing the controller all
command shows, controllers, views, and so on, are created. To summarize, the following table describes the various directories that are created in the flight-app
Roo project:
Apart from creating directories and files, first-time execution of the controller all
command also converts the nature of the Roo project from a normal Java project to a web project. The change in the nature of the project is reflected by the <packaging>
element of the pom.xml
file of the flight-app
project, as shown here:
<project xmlns="http://maven.apache.org/POM/4.0.0 ..>
..
<artifactId>flight-app</artifactId>
<packaging>war</packaging>
<version>0.1.0.BUILD-SNAPSHOT</version>
<name>flight-app</name>
</project>
The value war
of the <packaging>
element suggests that the project is a web project and not a normal Java project. A normal Java project has the value of a <packaging>
element as jar
. We will see in Packaging your web application recipe how the value of a <packaging>
element affects the output of a perform package
Roo command.
The first-time execution of a controller
all
command also results in the creation of a web.xml
file—the web application deployment descriptor.
We will now look in detail at the various artifacts generated by a controller all
command. Let's first look at the configuration information contained in a Roo-generated web.xml
file.
The web.xml
file configures DispatcherServlet
, root web application context, exception pages, and so on. In this section, we'll look at the configurations defined in the web.xml
file of the flight-app
project:
The contextConfigLocation
context initialization parameter identifies Spring's root web application context XML file(s), as shown here:
<context-param> <param-name>contextConfigLocation</param-name> <param-value> classpath*:META-INF/spring/applicationContext*.xml </param-value> </context-param>
The META-INF/spring/applicationContext.xml
file contains bean definitions that are shared by all servlets and filters defined in the flight-app
web application. These bean definitions are available to application contexts loaded by DispatcherServlet
. As we saw in the Creating a Roo project recipe of Chapter 1, Getting Started with Spring Roo and in Setting up a JPA provider for your project recipe of Chapter 2, Persisting Objects Using JPA an applicationContext.xml
file contains bean definitions for configuring data sources, services, transactions, and so on, and needs to be shared across the web application.
If you create additional application context XML files, which contain bean definitions that you want to share across the web application, then you can add them to the value of the contextConfigLocation
parameter using commas or space separated values, as shown here:
<param-value> classpath*:META-INF/spring/applicationContext*.xml, META-INF/spring/mycontext.xml </param-value>
The root web application context, identified by the contextConfigLocation
context initialization parameter, is loaded by the ContextLoaderListener
, which implements javax.servlet.ServletContextListener
, as shown here:
<listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> </listener>
The webmvc-config.xml
file created by Roo identifies the web application context of the Roo-generated web application. The webmvc-config.xml
file contains tile definitions, handler (or controller) definitions, handler mappings, view and exception resolution strategies, and so on. The DispatcherServlet
of Spring is responsible for loading the webmvc-config.xml
file and dispatching requests to appropriate handlers, resolving views, exceptions, and so on. In the web.xml of the flight-app
, DispatcherServlet
is configured to load webmvc-config.xml
, as shown here:
<servlet> <servlet-name>flight-app</servlet-name> <servlet-class> org.springframework.web.servlet.DispatcherServlet </servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value> /WEB-INF/spring/webmvc-config.xml </param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet>
As shown in the given code, the DispatcherServlet
accepts the contextConfigLocation
initialization parameter that identifies the Spring's web application context XML file. The scope of the application context loaded by the DispatcherServlet
is limited to the requests that are mapped to the DispatcherServlet
.
If you want to modularize your application, then consider creating a different web application context for each module and configure a DispatcherServlet
for each module in the web.xml
file.
As you might be lazy about loading JPA entities in your web application, Roo configures the
OpenEntityManagerInViewFilter
servlet filter in web.xml to bind the JPA EntityManager to the thread in which a request is handled, as shown here:
<filter> <filter-name> Spring OpenEntityManagerInViewFilter </filter-name> <filter-class>org.springframework.orm.jpa.support. OpenEntityManagerInViewFilter </filter-class> </filter>
The OpenEntityManagerInViewFilter
assumes that the EntityManagerFactory
for looking up the EntityManager
instance is registered with the root web application context and has the name entityManagerFactory
. If you change the id of the LocalContainerEntityManagerFactory
bean defined in META-INF/spring/applicationContext.xml
file, then add a entityManagerFactoryBeanName
initialization parameter to the OpenEntityManagerInViewFilter
definition in web.xml to inform it about the LocalContainerEntityManagerFactory
bean.
HTTP specification defines four methods: GET
, POST
, PUT,
and DELETE
, but HTML only supports GET
and POST
methods. As the semantics of different HTTP methods differ, in the REST approach it is recommended to use an appropriate HTTP method for sending an HTTP request. For instance, if you want to delete Flight
instances from the database, then you should use the DELETE
HTTP method and if you are updating Flight
instances, then use a PUT
HTTP method. The HTTP method with which the HTTP request was made is obtained using the getMethod()
method of javax.servlet.HttpServletRequest
.
So, to perform PUT
and DELETE
operations in your web application, you can do a normal POST
and along with it send an additional request parameter with the name _method
whose value is either PUT
or DELETE
. Spring provides a HiddenHttpMethodFilter
servlet filter, which reads the value of the _method
parameter, creates an HttpServletRequestWrapper
and overrides the getMethod
method of HttpServletRequest
to return the value of the _method
parameter—making it possible to perform PUT
and DELETE
HTTP operations in your web application.
Spring Roo automatically configures HiddenHttpMethodFilter
in the web.xml
file generated for the web application, as shown here for the flight-app
application:
<filter> <filter-name>HttpMethodFilter</filter-name> <filter-class>org.springframework.web.filter. HiddenHttpMethodFilter </filter-class> </filter>
The Spring Web MVC's form
tag library provides built-in support for dealing with different HTTP methods. For instance, the following form
tag will result in performing an HTTP POST
and send an additional request parameter named _method
whose value is PUT
:
<form:form method="put"> <input type="submit" value="Delete Flight"/> </form:form>
In the given code, the method
attribute of the form
tag identifies the value that needs to be set for the _method
request parameter when the form is submitted.
By default, Roo configures exception pages for situations when unexpected exceptions occur or a resource is not found (HTTP status code 404), as shown here for unexpected exceptions:
<error-page> <exception-type>java.lang.Exception</exception-type> <location>/uncaughtException</location> </error-page>
As the Roo-generated application uses Apache
Tiles
2
framework, the <location>
element is not mapped to the actual HTML or JSPX view that is shown in response to the exception. Later in this recipe, we'll see how Spring's exception resolvers are used by Roo-generated web applications to resolve exceptions to logical views.
Now, that we have seen configurations that form part of the web.xml
file generated by Roo for the flight-app
application, let's now look at the web application context XML file: webmvc-config.xml
, which is loaded by DispatcherServlet
. The web application context, loaded by DispatcherServlet
, is the place where request handlers, exception resolvers, theme and locale change interceptors, and so on, are registered.
The webmvc-config.xml
file contains configurations and bean definitions that are loaded by DispatcherServlet
. Let's now look at the webmvc-config.xml
file in detail:
In Roo-generation web applications, controllers are auto-detected using the <component-scan>
element of Spring's context
schema, as shown here:
<context:component-scan base-package="sample.roo.flightapp" use-default-filters="false"> <context:include-filter expression= "org.springframework.stereotype.Controller" type="annotation" /> </context:component-scan>
The base-package
attribute specifies a comma separated list of packages, which are scanned by Spring for classes annotated with @Repository
, @Component
, @Controller
, and @Service
annotations. The use-default-filters
attribute specifies if auto-detection of classes annotated with @Repository
, @Component
, @Controller
, and @Service
are enabled or disabled. By default, auto-detection is enabled. The value false
indicates that auto-detection of these annotated classes are disabled. As the web application context loaded by DispatcherServlet
should contain controller or handler components, the <include-filter>
element specifies that only classes annotated with @Controller
annotation are auto-detected by Spring.
If you manually create controllers in a different package, then add the package containing these controllers to the base-package
attribute's value.
Spring's mvc
schema provides an <annotation-driven>
element, which configures annotation-driven development support for Spring MVC applications, as shown here in the flight-app
web application:
<mvc:annotation-driven conversion-service="applicationConversionService"/> <bean id="applicationConversionService" class="sample.roo.flightapp.web.ApplicationConversionServiceFactoryBean" />
It is an <annotation-driven>
element, which ensures that incoming requests are mapped to controllers (annotated with @Controller
annotation) and to a particular @RequestMapping
annotated method of the controller. The conversion-service
attribute configures Spring's ConversionService
where custom converters and formatters are registered. Before we go into the details of what other Spring features are configured by the <annotation-driven>
element, let's take a quick look at Converter and Formatter SPIs introduced in Spring 3 for type conversion and formatting purposes.
In your application, you can either use a Converter SPI or a Formatter SPI to perform type conversions. A Converter SPI is suitable when you want to perform general-purpose type conversions from one Java type to another Java type. For instance, when you want to convert java.util.Number
to java.long.Date
, you can use a Converter SPI. The Formatter SPI addresses the conversion requirements typical of web applications, where you need to convert a String
value to a particular Java type when an HTML form is submitted and to convert a Java type to a String
value for displaying it to the user when the form is rendered. You can say that the Formatter SPI is a simplified version of the Converter SPI, and is more suitable for web application environments where localization of String
values is also required.
You can access conversion and formatting functionalities using the unified ConversionService
API provided by Spring. The ConversionService
is backed by a registry of converters and formatters, which are applied when you perform a type conversion using ConversionService
. GenericConversionService
is a concrete implementation of ConversionService
, which is appropriate for use in most applications. We will see later in this recipe, how the flight-app
application makes use of the GenericConversionService
API to perform a general-purpose type conversion at runtime.
If the conversion-service
attribute is not specified, the <annotation-driven>
element registers a default FormattingConversionService
(which extends GenericConversionService
) for performing conversions to and from java.lang.Number
, java.util.Date
, java.util.Calendar
, and java.long.Long
. Also, full support for date and time formatting is installed if the Joda
Time
(http://joda-time.sourceforge.net/) library is found in the application's classpath.
In a Roo-generated flight-app
application, you will find a dependency of the application on the Joda Time
library in the pom.xml
file; therefore, formatting support for the Joda Time
library is installed for your Roo-generated web application.
The <annotation-driven>
element makes use of the FormattingConversionServiceFactoryBean
as a factory for creating a FormattingConversionService
instance. The use of the FormattingConversionService
ensures that type conversion and formatting is applied for common types such as numbers and dates during data binding to fields of controller model objects (also referred to as command objects). It is important to note that it is the FormattingConversionService
that provides support for the @DateTimeFormat
(refer to Flight.java
) and for @NumberFormat
annotations.
To wire custom converters and formatters into the ConversionService
instance, the conversion-service
attribute of the <annotation-driven>
element sets the ConversionService
instance that is configured with custom converters and formatters, as shown here:
<mvc:annotation-driven conversion-service ="myConversionService" /> <bean id="myConversionService" class= "..format.FactoryConversionServiceFactoryBean"> <property name="converters"> <list> <bean class="com.flight.myCustomConverter"/> </list> </property> </bean>
The given configuration shows that you can set custom converters using the converters
property of the FormattingConversionServiceFactoryBean
class. If you take a quick look at the FormattingConversionServiceFactoryBean
, you will find that it doesn't support a formatters
property to allow configuring custom formatters. So, how do we register custom formatters? To register custom formatters, you will need to extend the FormattingConversionServiceFactoryBean
class and override its installFormatters
method to set your custom formatters.
In our Roo-generated flight-app
application, the conversion-service
attribute refers to the Roo-generated ApplicationConversionServiceFactoryBean
, which extends Spring's FormattingConversionServiceFactoryBean
. the ApplicationConversionServiceFactoryBean
defines converters, which convert Flight
and FlightDescription
JPA entity instances into String
values consisting of entity instance property names and their values.
The following code listing from the ApplicationConversionServiceFactoryBean.java
file shows the Roo-generated ApplicationConversionServiceFactoryBean
class:
import org.springframework.format.FormatterRegistry; import org.springframework.format.support. FormattingConversionServiceFactoryBean; import org.springframework.roo.addon.web.mvc.controller. RooConversionService; @RooConversionService public class ApplicationConversionServiceFactoryBean extends FormattingConversionServiceFactoryBean { @Override protected void installFormatters(FormatterRegistry registry) { super.installFormatters(registry); } }
In the given code, the ApplicationConversionServiceFactoryBean
represents an application-wide ConversionService
with which application converters and formatters are registered. You can register your custom formatters and converters inside the installFormatters
method.
The @RooConversionService
triggers a generation of a *_Roo_ConversionService.aj
AspectJ ITD file. The *_Roo_ConversionService.aj
defines converters for JPA entities in the application. These converters convert a JPA entity instance into a String
representation consisting of entity field names and their values. The String
representation of a JPA entity instance is used for displaying the entity instance as a String
on the Roo-generated web pages. The following code listing shows the ApplicationConversionServiceFactoryBean_Roo_ConversionService.aj
file of the flight-app
application:
privileged aspect ApplicationConversionServiceFactoryBean_Roo_ConversionService { static class ApplicationConversionServiceFactoryBean.FlightConverter implements Converter<Flight, String> { public String convert(Flight flight) { return new StringBuilder(). append(flight.getDepartureDate()).append(" "). append(flight.getArrivalDate()).toString(); } } static class ApplicationConversionServiceFactoryBean. FlightDescriptionConverter implements Converter< FlightDescription, java.lang.String> { public String convert(FlightDescription flightDescription) { ... } } public void ApplicationConversionServiceFactoryBean. installLabelConverters(FormatterRegistry registry) { registry.addConverter(getFlightConverter()); registry.addConverter(getFlightDescriptionConverter()); } public void ApplicationConversionServiceFactoryBean. afterPropertiesSet() { super.afterPropertiesSet(); installLabelConverters(getObject()); } }
In the given code, the AspectJ ITD file introduces the following methods and static classes into the ApplicationConversionServiceFactoryBean.java
:
FlightConverter
static class: It is the converter for the Flight
entity. It implements Spring's Converter
interface and provides implementation of its convert
method. The convert
method converts the Flight
entity instance to String
.FlightDescriptionConverter
static class: It is the converter for the FlightDescription
entity. It implements Spring's Converter
interface and provides implementation of its convert
method. The convert
method converts the FlightDescription
entity instance to String
.installLabelConverters
: It registers converters for Flight
and FlightDescription
JPA entities with Spring's FormatterRegistry
, which extends Spring's ConverterRegistry
for registering converters.afterPropertiesSet
: This is the method that is invoked to initialize the ApplicationConversionServiceFactoryBean
. The method invokes the installLabelConverters
to register converters for JPA entities.By default, the <annotation-driven>
element also configures JSR 303 – Bean Validation if a JSR 303 provider is found in an application's classpath. As we will see later in this recipe, JSR 303 validation is used for validating model objects (also referred to as command objects) of Spring MVC controllers.
In the Roo-generated flight-app
application, you'll find dependency of the application on the Hibernate Validator library in the pom.xml
file; therefore, you can be sure that JSR 303 support is installed for your application.
If instead of using JSR 303 validation you want to use a custom validator based on Spring's Validation API for validating model objects, then configure it using the validator
property of the <annotation-driven>
element.
As web applications need to serve static resources such as images, CSS, and JS files, Roo configures Spring's ResourceHttpRequestHandler
via the <resources>
element of the mvc
schema, as shown here:
<mvc:resources location="/, classpath:/META-INF/web-resources/" mapping="/resources/**" />
The location
attribute specifies the locations from which to serve resources. It accepts comma-separated values for resource locations. The /
value refers to the web application root and the classpath:/META-INF/web-resources
value indicates that static resources can also be served from the META-INF/web-resources
directory of any JAR file in the classpath. It is important to note that a resource is searched in the order of locations specified in the location
attribute.
The mapping
attribute specifies the URL mapping pattern of the incoming resource request to which the ResourceHttpRequestHandler
applies.
Roo maps the DispatcherServlet
to /
(refer to the web.xml
file of the flight-app
application), to which the default servlet of the servlet container is also mapped. As the default servlet of a servlet container is responsible for serving static resources, mapping DispatcherServlet
to /
overrides the default resource serving behavior. To address the static resource serving issue arising from mapping DispatcherServlet
to /
, Roo makes use of the <default-servlet-handler>
element of the mvc
schema to configure Spring's DefaultServletHttpRequestHandler
, which delegates a resource serving to the servlet container's default servlet, as shown here:
<mvc:default-servlet-handler />
As most web applications are expected to support multiple locales and themes, Roo configures Spring's LocaleChangeInterceptor
and ThemeChangeInterceptor
beans in the web application context XML to simplify changing locale and the theme of the web application, as shown here:
<mvc:interceptors> <bean class="org.springframework.web.servlet.theme. ThemeChangeInterceptor"/> <bean class="org.springframework.web.servlet.i18n. LocaleChangeInterceptor" p:paramName="lang" /> </mvc:interceptors>
In the given code, the <interceptors>
element of Spring's mvc
schema is used to configure interceptors for pre/post-processing requests before/after the request is handled by controllers. By default, ThemeChangeInterceptor
inspects the request parameter named theme
to determine the theme to be applied and LocaleChangeInterceptor
inspects the request parameter named locale
to determine the current locale associated with the incoming request.
In a Spring MVC application, controllers are responsible for processing the incoming request and returning a logical view name and view data. The DispatcherServlet
hands over the logical view name to Spring's ViewResolver
(configured in the web application context), which resolves the actual view corresponding to the logical view name. The DispatcherServlet
then renders the actual view. So, does it mean that even if a web application needs to show a static web page, we will need to create a Spring MVC controller for it? Well, this is where the ParameterizableViewController
built-in controller of Spring MVC comes into picture. The ParameterizableViewController
simply returns a pre-configured view name, which is resolved by the ViewResolver
and rendered by the DispatcherServlet
. This saves the effort of creating a custom controller, which does nothing but return the name of view to be rendered. You can either directly configure a ParameterizableViewController
in your web application context or use Spring's mvc
schema's <view-controller>
element to do it for you.
In case of our flight-app
application, Roo makes use of the <view-controller>
element for static views, such as the home page of the flight-app
application and the various exception pages, as shown here:
<mvc:view-controller path="/" view-name="index" /> <mvc:view-controller path="/uncaughtException" />
In the <view-controller>
element the view-name
attribute identifies the name of the view mapped to the URL path identified by the path
attribute. So, if the URL used to access the flight-app
application is http://localhost:8080/flight-app/
, the view name corresponding to this URL is index
.
If the view-name
attribute is not specified for the view-controller
element (as in the given code for an uncaught exception related <view-controller>
element), then the RequestToViewNameTranslator
, configured for the DispatcherServlet
, is used to resolve the name of the view. The RequestToViewNameTranslator
is configured in the web application context and is used by the DispatcherServlet
to determine the view name if no view name is returned by the controller handling the request. As no RequestToViewNameTranslator
is configured in the web application context by Roo, the default implementation: the DefaultRequestToViewNameTranslator
, is used for determining the view name. The DefaultRequestToViewNameTranslator
simply removes the leading and trailing slashes and any file extension associated with the URI, and the resulting value is used as the view name. So, if we attempt to invoke the ParameterizableViewController
corresponding to the uncaught exception view-controller element using the following URL: http://localhost:8080/flight-app/uncaughtException
, then the DefaultRequestToViewNameTranslator
will simply return a uncaughtException
as the name of the view that should be rendered by the DispatcherServlet
.
Spring Web MVC applications require a ViewResolver
to resolve actual view from the logical view name returned by controllers. When creating Spring Web MVC applications, Roo configures Spring's UrlBasedViewResolver
(an implementation of the ViewResolver
interface) in the web application context, which returns the actual view. As every view in Spring Web MVC is represented by a class that implements a View
interface, the UrlBasedViewResolver
must be informed about the actual View
class that it must generate corresponding to the logical view name. The following fragment shows the Roo-generated UrlBasedViewResolver
configuration for the flight-app
application:
<bean class="org.springframework.web.servlet.view. UrlBasedViewResolver" id="tilesViewResolver"> <property name="viewClass" value="org.springframework.web.servlet.view.tiles2. TilesView" /> </bean>
The
UrlBasedViewResolver
resolves a view name to a URL, without requiring you to explicitly map each view name to a URL. For instance, if the view name is mypage
, then the UrlBasedReviewResolver
can be used to attach a prefix (using the prefix
property) /WEB-INF/jsp
and a suffix (using the suffix
property) .jsp
to the view name to create a URL pointing to the mypage.jsp
page in the /WEB-INF/jsp
folder of your web application. Here, view name is mypage
and the URL to which it is mapped by the UrlBasedViewResolver
is /WEB-INF/jsp/mypage.jsp
. As the Roo configured UrlBasedViewResolver
doesn't make use of the suffix
and the prefix
properties, the URL for the actual view is the same as the name of the logical view returned by the controller.
If you want to redirect or forward a request to another URL in a Spring MVC controller implementation, then instead of returning a view name you can return a String
value with a prefix as redirect:
or forward:
. If a controller returns a redirect or forward URL, then the UrlBasedViewResolver
doesn't perform view resolution. Instead, it redirects or forwards requests to the URL returned by the controller.
As Roo uses a Apache Tiles 2 framework to simplify JSP development, the viewClass
property of the UrlBasedViewResolver
is set to TilesView
. Tiles support is configured in the web application context using Spring's TilesConfigurer
, as we will see shortly. The name of the tile definition to which TilesView
corresponds to is the actual-view URL resolved by the UrlBasedViewResolver
. In case of the flight-app
, we discussed that the actual-view URL generated by the UrlBasedViewResolver
is the same as the logical name of the view; therefore, the name of the tile definition is also the same as the logical name of the view returned by the controller.
To get a complete picture of how views are resolved in Roo-generated web applications, consider the controller configured by the following <view-controller>
element:
<mvc:view-controller path="/" view-name="index" />
We discussed earlier that the ParameterizableViewController
configured by the given view-controller
element will return the view name as index
. As no suffix
or prefix
properties of the UrlBasedViewResolver
are configured, the actual view URL is also an index
. Now, TilesView
refers to the tile definition, which has the same name as the actual-view URL value generated by the UrlBasedViewResolver
; therefore, the name of the tile definition is also index
. To summarize, the ParameterizableViewController
configured we saw earlier will result in showing the view whose tile definition name is index
(you will find this tile definition name in the WEB-INF/views/views.xml
file).
Tiles definitions are defined in XML files and configured in the web application context using TilesConfigurer
, as shown here:
<bean class="org.springframework.web.servlet.view. tiles2.TilesConfigurer" id="tilesConfigurer"> <property name="definitions"> <list> <value>/WEB-INF/layouts/layouts.xml</value> <value>/WEB-INF/views/**/views.xml</value> </list> </property> </bean>
The definitions
property of the TilesConfigurer
specifies the tiles definitions XML files. The path to these files can also use wildcard characters. For instance, the /WEB-INF/views/**/views.xml
path loads tiles definitions from all views.xml
files, which are inside the /WEB-INF/views/
directory.
In a typical web application, it is required to gracefully handle exceptions thrown by controllers. When an exception is thrown by a controller, the DispatcherServlet
makes use of Spring's HandlerExceptionResolver
for resolving exceptions. Roo configures the SimpleMappingExceptionResolver
(an implementation of the HandlerExceptionResolver
) as the exception resolver for the flight-app
application, as shown here:
<bean class="org.springframework.web.servlet.handler. SimpleMappingExceptionResolver" p:defaultErrorView="uncaughtException"> <property name="exceptionMappings"> <props> <prop key=".DataAccessException">dataAccessFailure</prop> .... </props> </property> </bean>
The SimpleMappingExceptionResolver
maps exception class names to error view names. In the given code, this mapping is set via the exceptionMappings
property. You can either specify the fully-qualified class name of the exception or you can use a substring to match multiple exception class names to an error view. For instance, the.DataAccessException
will map to the my.custom.DataAccessException
as well as the org.springframework.dao.EmptyResultDataAccessException
, and in both cases the DispatcherServlet
will attempt to render the dataAccessFailure
view. The defaultError
view attribute of the SimpleMappingExceptionResolver
identifies the view to which an exception is resolved if no exception mapping is found.
Spring Roo also configures the following classes in webmvc-config.xml
:
ReloadableResourceBundleMessageSource
: Spring is a built-in MessageSource
implementation, which loads resource bundles from WEB-INF/i18n
folder.ResourceBundleThemeSource
: Spring is a built-in ThemeSource
implementation for loading the ResourceBundle
for each theme supported by the web application. In the context of the flight-app
application, this configuration loads the alt.properties
and standard.properties
theme files.CookieThemeResolver
: Spring is a built-in ThemeResolver
implementation, which stores a cookie in the browser for identifying the theme chosen by the user.CookieLocaleResolver
: Spring is a built in LocaleResolver
implementation, which stores a cookie in the browser for identifying the locale chosen by the user.CommonsMultipartResolver
: Spring is a built-in MultipartResolver
implementation, which makes use of the Jakarta
Commons
FileUpload
1.2
(http://commons.apache.org/fileupload/) or higher to support uploading files in web applications.Now, that we know the configurations created by Spring Roo in our flight-app
application; let's look at the controller classes generated by Roo.
Roo generates a controller for each JPA entity corresponding to which a controller doesn't already exist. For instance, in case the of the flight-app
application, Roo creates the FlightController
and the FlightDescriptionController
controllers corresponding to the Flight
and the FlightDescription
JPA entities, respectively. The following code from the FlightController.java
file shows the FlightController
generated by Spring Roo:
@RooWebScaffold(path = "flights", formBackingObject = Flight.class) @RequestMapping("/flights") @Controller public class FlightController {}
The @Controller
annotation indicates that the FlightController
is a Spring MVC controller component.
The @RequestMapping
class-level annotation maps incoming requests to controller classes. The URI to which the controller map is specified by the value specified in the @RequestMapping
annotation. For instance, the FlightController
maps to the /flights
URI. As we will see soon, @RequestMapping
annotation can also be used at a method-level to narrow down the mapping specified at the class-level.
The @RooWebScaffold
Roo annotation instructs Roo to generate an ITD containing CRUD operations for the Flight
JPA entity (identified by the formBackingObject
attribute) and creates JSPX views for performing CRUD operations on the Flight
JPA entity. The path
attribute identifies the sub-directory inside the /WEB-INF/views/
in which view artifacts (JSPX views and tiles definitions XML) are created for the FlightController
. Refer the Creating a Spring MVC controller for a specific JPA entity recipe to see a detailed list of the @RooWebScaffold
annotation attributes.
The following code shows the AspectJ ITD created for FlightController
:
import javax.validation.Valid; privileged aspect FlightController_Roo_Controller { @RequestMapping(method = RequestMethod.POST) public String FlightController.create(@Valid Flight flight, ...) { if (bindingResult.hasErrors()) { ... return "flights/create"; } ... flight.persist(); return "redirect:/flights/" + ...; } ... }
The following are the important points to notice about the given code:
method
attribute of the @RequestMapping
method-level annotation specifies that the create
method of a FrontController
is invoked if an HTTP POST request is received by the controller. The create
method represents the controller method in which the Flight
entity is persisted in the database.create
method is either a flight/create
or redirect:flights/...
String
value. If the return value is flight/create
, then the flight-app
application shows the web page, which maps to the flight/create
tiles definition name. If the return value is redirect:flights/
, then it is interpreted as a redirect URL (as mentioned earlier), to which the DispatcherServlet
redirects the request.Flight
parameter of the create
method is annotated with a @Valid
JSR 303-bean validation annotation. The use of the @Valid
annotation results in invoking validation of the Flight
entity before it is persisted in the database.The following createForm
method, defined in the FlightController_Roo_Controller.aj
ITD, highlights another way in which the @RequestMapping
annotation is used:
@RequestMapping(params = "form", method = RequestMethod.GET)
public String FlightController.createForm(Model uiModel)
{
..
return "flights/create";
}
The createForm
method shows the HTML form in which the user enters details required for creating the Flight
entity in the database. The createForm
method is invoked if the request received by the FlightController
handler contains a request parameter named form
(as specified by params
attribute) and the request method used for the request is HTTP GET
(as specified by the method
attribute).
The following delete
method, defined in the FlightController_Roo_Controller.aj
ITD, shows yet another way of using the @RequestMapping
annotation:
@RequestMapping(value = "/{flightId}", method = RequestMethod.DELETE) public String FlightController.delete( @PathVariable("flightId") Long flightId, ..) { .. }
The delete
method deletes a Flight
JPA entity from the database. The value
attribute of the method-level @RequestMapping
narrows down the mapping specified by the @RequestMapping
annotation at the class-level. We earlier saw that the class-level @RequestMapping
for the FlightController
specifies /flights
as the URI to which the FlightController
maps. This means, the delete
method maps to the /flights/{flightId}
URI-template. Now, the {flightId}
is a variable whose value is determined from the submitted request. When the value of the {flightId}
variable is substituted in the URI-template, then it becomes a URI. So, where does the value of the {flightId}
variable come from? It comes from the request URI. For instance, if the request URI is /flights/1
, then the value of the {flightId}
variable is 1. As you will notice in the given code, the @PathVariable
annotation has been used for the method parameter named flightId
. The annotation @PathVariable("flightId")
retrieves the value of the {flightId}
variable and binds it to the Long
type flightId
method parameter.
The @PathVariable
not only binds the value of the URI variables to method parameters, but also performs type conversion.
If you look at the code for the FlightController_Roo_Controller.aj
or the FlightDescriptionController_Roo_Controller.aj
ITD, then you will find that the controller methods responsible for processing the HTTP POST
, PUT
, and DELETE
methods return a redirect URL (specified using redirect:
prefix), that is, the Spring MVC controllers automatically implement the PRG
(Post-Redirect-Get) pattern.
In this recipe, we saw that Spring Roo does a lot of work behind the scenes to give us a fully-functional Spring Web MVC application. Depending upon your choice of web frontend, you can also use Roo's built-in support for Flex and GWT to create web applications. In the next chapter, we will look at Spring Roo's support for creating web applications using Flex, GWT, and the Spring Web Flow framework.
3.21.159.82