Auto-generating Spring MVC controllers andJSPX views from JPA entities

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.

Getting ready

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.

How to do it...

To create views using the controller all command follow the given steps:

  1. Execute 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.

  2. As many dependencies are added to the pom.xml file of the 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
    
  3. Import the flight-app project into the Eclipse IDE to view the files and directories that form part of the application.

How it works...

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:

Directory

Description

sample ooflightappweb folder in SRC_MAIN_JAVA

Contains the scaffolded controllers (and their ITDs) corresponding to each JPA entity in the application. This folder is created based on the value of the package argument of the controller all command.

SRC_MAIN_WEBAPPWEB-INFspring

Contains the web application context XML file for the flight-app application that is loaded by Spring's DispatcherServlet. The name of the web application context XML file is webmvc-config.xml.

SRC_MAIN_WEBAPPimages

Contains images used by the JSPX views generated by Roo.

SRC_MAIN_WEBAPPstyles

Contains CSS stylesheets used by the JSPX views generated by Roo. By default it contains two CSS stylesheets: standard.css and alt.css.

SRC_MAIN_WEBAPPWEB-INFclasses

Roo creates the following two property files, which identify the resources that make up a theme: standard.properties and alt.properties.

SRC_MAIN_WEBAPPWEB-INFlayouts

Contains a tiles configuration XML file, layouts.xml, which contains tiles definitions. It also contains a layout template JSPX file, default.jspx, which is used as a template by the tiles definitions in the layouts.xml file.

SRC_MAIN_WEBAPPWEB-INFviews

Contains non-JPA entity specific JSPX views of the Roo-generated web application. For instance, it contains an index.jspx file, which shows the home page of the flight-app application and an uncaughtException.jspx file, which is rendered when an unexpected exception occurs in the web application. To simplify creating a custom home page of the web application, the directory also contains an index-template.jspx file.

The directory also contains a tiles configuration XML file, views.xml, which extends the tiles definitions defined in the layouts.xml file. The tiles definitions in the views.xml file are meant for showing non-JPA entity specific JSPX pages such as the home page and the page when unexpected exceptions occur during request processing. You should note that a Roo-generated web application makes use of Apache Tiles 2 framework to simplify developing user interfaces.

SRC_MAIN_WEBAPPWEB-INFviewsflightdescriptions

and

SRC_MAIN_WEBAPPWEB-INFviewsflights

flightDescriptions and flights directories contain JSPX views corresponding to FlightDescription and Flight JPA entities, respectively. Each directory also contains a tiles configuration XML file, views.xml, which contains tiles definitions for showing JPA entity specific web pages. By default, Roo creates JSPX views for performing CRUD operations on a JPA entity.

SRC_MAIN_WEBAPPWEB-INF ags

Contains tags that are installed by Roo to simplify developing JSPs. The tags are XML-only in nature, that is, they are not backed by a Java source code, making it possible to easily modify the behavior of these tags. We will see in the round-tripping support in Spring Roo for web controllers and the views recipe how the id attribute of these tags helps achieve round-tripping support in a Roo-generated web application.

SRC_MAIN_WEBAPPWEB-INFi18n

Contains resource bundles for the web user interface to support internationalization. By default Roo generates a messages.properties and an application.properties file.

The messages.properties file contains translations that are applicable to all web user interfaces generated by Roo.

The application.properties file contains application-specific translations. This is the reason why you will find translations containing words such as Flight and Flight Description only in the application.properties file.

The modifying Roo-generated views and The internationalizing Roo-generated web application recipes describe in detail how these property files are managed by Roo and the role they play in internationalizing a Roo-generated web application.

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.

Configuration information defined in web.xml

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:

contextConfigLocation initialization parameter

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>

ContextLoaderListener

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>

DispatcherServlet

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.

OpenEntityManagerInViewFilter

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.

HiddenHttpMethodFilter

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.

Tip

As Roo-generated JSPX files make heavy use of custom tags installed in the SRC_MAIN_WEBAPPWEB-INF ags folder, you will mainly find usage of the Spring MVC's form tag library in these custom tags.

Exception pages

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.

Beans and configurations defined in webmvc-config.xml

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:

Controller auto-detection

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.

Annotation-driven development support and conversion service

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.

Note

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.

Note

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.

ResourceHttpRequestHandler

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.

DefaultServletHttpRequestHandler

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 />

Theme and locale change interceptors

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.

ParameterizableViewController

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.

View resolution

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

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.

Exception handling

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.

Miscellaneous configuration

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-generated controllers

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:

  • The 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.
  • The return value from the 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.
  • You may notice that the 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.

Note

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.

There's more...

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.

See also

  • Refer to the Getting started with Flex application development and Scaffolding Flex application from JPA entities recipes to see how Spring Roo simplifies developing Flex applications.
  • Refer to the Scaffolding GWT application from JPA entities recipe to see how Spring Roo can be used to develop GWT based applications
  • Refer to the Getting started with Spring Web Flow recipe for details on how Spring Roo supports developing applications with the Spring Web Flow framework
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.21.159.82