Search This Blog

Sunday, January 8, 2012

Spring 3.1 MVC Example

I started Sleepless in Salt Lake City with an example of Spring MVC that used auto-wiring with Spring 2.5. Sadly, I have lost that code. On the bright side however, Spring has made some advances that I am hoping to catch up with a re-vamp of the original BLOG. This BLOG primarily hopes to demonstrate the use of Spring Java Config spanning different tiers of an application with a simple Spring MVC 3.1 example that one can use for learning as well.

Spring JavaConfig is a means of configuring the Spring Container using pure-java, i.e., without XML if one so desired. It relies on the features of Java 5.X+ such as Generics and Annotations to express what was previously done via XML. Java Config has the following advantages:

1. Injections, as it should be done using features like inheritance, polymorphism, etc.
2. Full control on creation and initialization of beans.
3. Makes refactoring easy without the need for something like Spring IDE
4. Ability to refrain from Classpath scanning as that can be expensive for container initialization
5. Use XML or property support in conjunction if desired

Most of this BLOG is in the form of code with a functional examples provided as well for download and execution. The code shown below assumes that a person is familiar with Spring MVC and does not delve into the basics of the same. The examples used in this BLOG are written with the following objectives:

1. Demonstrate Spring Java Config across the different tiers with no spring XML usage
2. Demonstrate transaction management
3. Demonstate a validator for the beans using JSR-303
4. Demonstate the REST features of Spring XML by having the application double as a RESTful Web Service.
5. Demonstate how Unit testing can be achieved across the tiers when using Java Config
6. Demonstates use of RestTemplate in an integration-test of the Web Service

In short, the making of a really long POST ;-)

The app references in this BLOG is a simple Flight reservation system where flights can be searched and booked. The web application itself is separated into 3 tiers, Web, Service and Data Access. Each of the tiers has its own Java Configuration as shown below with the WebConfig being the aggregator or top level Config:
With the interest of making unit testing easier, the Config classes are separated via interface/impl.

The data access tier defines a Java Config module as shown below:
@Configuration
@EnableTransactionManagement
public class DefaultDaoConfig implements DaoConfig, TransactionManagementConfigurer {
  
  // Dao Initialization
  @Bean
  public FlightDao getFlightDao() {
     return new FlightDaoImpl(getSessionFactory());
  }
   ...other DAOs

  // Session Factory configuration for Hibernate
  @Bean
  public SessionFactory getSessionFactory() {
    return new AnnotationConfiguration().addAnnotatedClass(Ticket.class)
      .addAnnotatedClass(Flight.class).addAnnotatedClass(Airport.class)
      .configure().buildSessionFactory();
  }

  // Transaction manager being used
  @Override
  public PlatformTransactionManager annotationDrivenTransactionManager() {
    return new HibernateTransactionManager(getSessionFactory());
  }
}

Of interest in the above is the @EnableTransactionManagement annotation which enables Spring's annotation driven transaction management capabilities for services annotated with @Transactional. The @Transactional annotation instructs the Spring container to provide transactional semantics for the method.  A service class annotated with @Transactional is shown below. Also note that the FlightDao and TicketDao beans are defined to be set explicitly via the constructor without the use of the @Autowired annotation. @Autowired could be used as well if desired for having a no-args constructor with the Spring Framework reflectively initializing the DAOs.

@Transactional(readOnly = true)
public class ReservationServiceImpl implements ReservationService {
  private final FlightDao flightDao;
  private final TicketDao ticketDao;

  public ReservationServiceImpl(FlightDao flightDao, TicketDao ticketDao) {...}

  @Override
  @Transactional(rollbackFor = { NoSeatAvailableException.class }, readOnly=false)
  public Ticket bookFlight(Reservation booking) throws  NoSeatAvailableException {
   ...
  }

  @Override
  public List<Ticket> getReservations() {...}
}

The Services tier also has a corresponding Config where the Services of the application are defined:
@Configuration
public class DefaultServiceConfig implements ServiceConfig {
  @Autowired
  private DaoConfig daoConfig;

  @Bean
  @Override
  public AirlineService getAirlineService() {
    return new AirlineServiceImpl(daoConfig.getFlightDao(), daoConfig.getAirportDao());
  }
  .........other services ...
}

One might ask the question as to how can one mock out the DaoConfig for testing purposes as it is set to be auto-wired, surely not reflection? Well, on the Configs one cannot use constructor based initialization as Spring's container expects a no-arg constructor. One can however define a setter for the Config and be able to mock the same out. The following sample of the ControllerConfig demonstrates the same:
@Configuration
public class ControllerConfig {
  private ServiceConfig serviceConfig;
  
  @Autowired 
  public void setServiceConfig(ServiceConfig serviceConfig) {
    this.serviceConfig = serviceConfig;
  }
 
  @Bean
  public FlightsController getFlightController() {
    return new FlightsController(serviceConfig.getAirlineService(), serviceConfig.getAirportService());
  }
  ... other controllers...
}
The FlightController is shown below:
@Controller
public class FlightsController {
  private final AirlineService airlineService;
  private final AirportService airportService;

  public FlightsController(AirlineService airlineService, AirportService airportService) {...}

  // Flight search page initialization
  @RequestMapping(value = "/searchFlights.html", method = RequestMethod.GET)
  public ModelAndView searchFlights() throws Exception {
    List<Airport> airports = airportService.getAirports();

    ModelAndView mav = new ModelAndView("searchFlights");

    mav.addObject("airports", airports);

    return mav;
  }

  // Searching for a flight. Note that the Critiera object is automatically bound with 
  // the corresponding form post parameters
  @RequestMapping(value = "/searchFlights.html", method = RequestMethod.POST)
  public ModelAndView searchFlights(FlightSearchCriteria criteria) throws Exception {

    ModelAndView mav = new ModelAndView("searchFlights");
     ....
    FlightSearchResults searchResult = airlineService.getFlights(criteria);

    mav.addObject("flightSearchResult", searchResult);

    return mav;
  }
}

Annotating a bean with @Controller indicates to Spring that the same is part of the MVC family and to use the same for mapping request urls and servicing web requests. A few other things to note about the controller:

1. Does not need to extend any particular base class
2. The @RequestMapping annotation defines the HTTP Method and resource serviced. In the example shown, "/searchFlights" responds differently to GET and POST requests

All these tier configurations are wired together via WebConfig which also defines the different components required to setup the Spring MVC Servlet. The @Import annotation indicates to the Spring Container which Config's need to be included. Note the same can also be done in the web.xml as part of the Servlet definition if so desired.
@EnableWebMvc
@Configuration
@Import({ ControllerConfig.class, DefaultServiceConfig.class, DefaultDaoConfig.class })
public class WebConfig extends WebMvcConfigurerAdapter 
// Validator for JSR 303 used across the application
  @Override
  public Validator getValidator() {...}

 // View Resolver..JSP, Freemarker etc
  @Bean
  public ViewResolver getViewResolver() {...}

 // Mapping handler for OXM
  @Bean
  public RequestMappingHandlerAdapter getHandlerAdapter() {.. }
 
 @Override
  public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
    configurer.enable();
  }
}
The annotation @EnableWebMvc is required to tell Spring that this is a MVC application. Implementing the WebMvcConfigurerAdapter allows for customization the Spring Servlet and path handling. Alternatively, one could also extend WebMvcConfigurationSupport directly and provide the necessary customization

Validation of Requests using JSR-303 Bean Validation API:

JSR-303 standardizes the validation for the Java Platform. Use it if you wish for validation. The annotations from the API are used to specify validation constraints and the runtime enforces the same. As this example utilizes Hibernate, adding Hibernate-Validator which is an implementation of the API is a natural choice. In the example provided the Reservation class is annotated with the annotations from the JSR to provide runtime validation and messages. The messages could be obtained from a resource bundle if desired:
public class Reservation {
  @XmlElement
  @NotBlank(message = "must not be blank")
  private String reservationName;

  @Min(1)
  @XmlElement
  private int quantity = 1;

  @XmlElement
  @NotNull(message = "Flight Id must be provided")
  private Long flightId;
  ....
}
The web deployment descriptor is where the application converges and  the DispatcherServlet is notified of the Context class to use and the location of the WebConfig. As previously mentioned, it is possible to provide a comma separated list of Configs in the web.xml if desired versus importing them in the WebConfig:
<web-app>
        <servlet>
                <servlet-name>springExample</servlet-name>
                <servlet-class>org.springframework.web.servlet.DispatcherServlet
                </servlet-class>
                <init-param>
                        <param-name>contextClass</param-name>
                        <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext
                        </param-value>
                </init-param>
                <!-- Tell Spring where to find the Config Class
                     This is the hook in to the application. -->
                <init-param>
                        <param-name>contextConfigLocation</param-name>
                        <param-value>com.welflex.web.WebConfig</param-value>
                </init-param>
                <load-on-startup>1</load-on-startup>
        </servlet>
        ....
</web-app>

Unit Testing:

Unit testing the tiers is very easily supported by the Spring Testing framework. When testing the service layer for example, one does not really need to integrate with the database and the corresponding interactions can be mocked out. For this reason, the objects in the DAO config are provided as mocks as shown below:
@Configuration
@EnableTransactionManagement
public class MockDaoConfig implements DaoConfig {
 
  @Bean
  public FlightDao getFlightDao() {
    return Mockito.mock(FlightDao.class);
  }
  ..// Other mocks
}

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {MockDaoConfig.class,
    DefaultServiceConfig.class }, loader = AnnotationConfigContextLoader.class)
public class AirportServiceTest {
  @Autowired
  private AirportDao airportDaoMock;

  @Autowired
  private ApplicationContext ctx;

  @Test
  public void getAirportCode() {
    // Obtain Airport service from the context
    AirportService a = ctx.getBean(AirportService.class);

    // Alternatively, instatiate an Airport Service by providing a mock
    AirportService a2 = new AirportServiceImpl(airportDaoMock);
  }
  ...
}
In the above test, the Config's being used are the concrete implementation of ServiceConfig and a mock of DaoConfig. This enables the testing of the Service tier by mocking the Data access tier. Some people prefer to test each class in isolation  by wiring the same up with the required dependencies explicitly while others prefer to obtain the class from the container with all dependencies wired up. The choice of style of testing is a topic for another BLOG but it is sufficient to accept that either style is supported. It is to be noted that one does not need to mock all the beans for testing and can define specific Config objects as required. As with the Service tier mocking out the Data Access tier, the  Controller tier can also be tested by mocking out the Service tier objects.

Spring MVC Restful Services:
 
One of the goals of this example was to demonstrate how the web application doubles as a service as well. Consider the following controller which provides representations for the web application and a service consumer:
@Controller
public class ReservationsController {
  private final AirlineService airlineService;
  private final ReservationService reservationService;

  public ReservationsController(ReservationService reservationService, AirlineService airlineService) {...}

  @RequestMapping("/reservations.html")
  public ModelAndView getReservationsHtml() {...}

  @RequestMapping(value = "/reservations/{reservationId}.html", method = RequestMethod.GET)
  public ModelAndView getReservationHtml(@PathVariable Long reservationId) {...}

  // Web Service Support to provide XML or JSON based of the Accept Header attribute
  @RequestMapping(value = "/reservations", method = RequestMethod.GET, produces = {
      MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE })
  @ResponseBody
  public Reservations getReservations() {...}

  @RequestMapping(value = "/reservations/{reservationId}", method = RequestMethod.GET, produces = {
      MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE })
  @ResponseBody
  public Ticket getReservation(@PathVariable Long reservationId) {...}
  ....
}
In the above shown controller, the getReservationsHtml() returns a HTML representation of the reservations while the call to getReservations() returns an XML or JSON representation of the reservations. The type of representation returned depends on the Accept header provided to the service. The @ResponseBody annotation indicates that a view is not targetted but that the resulting object should be translated directly. The example itself uses Spring OXM for marshalling and unmarshalling XML via JAXB. An integration test for the above controller using RestTemplate is shown below:
public class WebServiceIntegrationTest {
  private RestTemplate template;
  private static final String BASE_URL = "http://localhost:9091";

  @Before
  public void setUp() {
    // Create Rest Template with the different converters
    template = new RestTemplate();
    List<HttpMessageConverter<?>> converters = ...;
    template.setMessageConverters(converters);
  }

  private static final String FLIGHT_NUMBER = "LH 235";

  @Test
  public void reservations() {
    Flight singleFlight = template.getForObject(BASE_URL + "/flights/" + FLIGHT_NUMBER,
      Flight.class);

    Reservation res = new Reservation();
    res.setFlightId(singleFlight.getId());
    res.setQuantity(10);
    res.setReservationName("Sanjay Acharya")

   // Post to create a ticket
    Ticket ticket = template.postForObject(BASE_URL + "/bookFlight", res, Ticket.class);
    assertNotNull(ticket);
    System.out.println("Ticket reserved:" + ticket);

    // All Reservations
    Reservations reservations = template.getForObject(BASE_URL  + "/reservations", Reservations.class);
    assertTrue(reservations.getTickets().size() == 1);

    // Single reservation
    assertNotNull(template.getForObject(BASE_URL + "/reservations/" + ticket.getId(), Ticket.class));
  }
}

For more information on the RestTemplate, check out my other BLOG on Rest Client Frameworks.

Side Note:

In this example, I have used a framework called Pojomatic for the convenience it provides in implementing equals(), hashCode() and toString() for my POJOs. I prefer its brevity over using the the tools provided by the Apache commons project which I have used in my previous BLOGs. Check the same out.

Running the Example:

The example provided herewith demonstrates the Flight Spring MVC example. It uses an in memory database which is primed with data on start up. The model package in the example is overloaded as far as its responsibilities go. The POJO's there in serve as database model objects and data transfer objects. The example, unit tests or integration tests are by no means designed to be comprehensive and are only present only for demonstration purposes.

1. Download the example from HERE
2. Execute a mvn jetty:run to start the application.
3. Execute a mvn integration:test to see the Web Service calls being exercised
4. The Flight Web Application is available at http://localhost:8080
5. Web service url's can be accessed directly via:   http://localhost:8080/reservations , http://localhost:8080/reservations/{id}, http://locahost:8080/flights, http://localhost:8080/airports

Conclusion:

I find the clarity and control of object initialization and wiring really convenient with Spring Java Config. Walking the dependency tree is also very intuitive. In addition, due to the strong typing, refactoring is made easy even without a tool like Spring IDE. What I like about Spring MVC versus other web frameworks is its simplicity with the Front Controller/Dispatcher pattern. One can use whatever Ajaxian technology they like to enhance the user experience. The framework itself is very non-invasive, i.e., it does not require the use of Spring across all the tiers. The Restful support facilitates easy interaction with Ajax based technologies and makes the web app a true resource based system. I do not use Spring MVC on any production application sadly. If anyone who does have experience with the same stumbles upon this BLOG, I would love to hear of the same.

Take a look at the Spring Security Stateless Cookie Authentication BLOG for an example of the same MVC application but using Twitter Bootstrap and integrated with Spring Security.

Update - I have uploaded a simple presentation that I gave along with supporting code. You can view the same at Spring MVC 3.1 Presentation/Tutorial.

9 comments:

Dave Stockmann said...

Great example. You should put your code on github, then you won't lose it. :-)

Sanjay Acharya said...

@Dave, thanks for the suggestion. However, if I did not lose my code every once so often, I would not be tempted to make "Great Examples" like this ;-) Kidding of course.

Jörgen said...
This comment has been removed by the author.
Anonymous said...

Great by doesnt work on tomcat

Sanjay Acharya said...

Update your pom.xml to exclude version of javax.servlet and javax.jstl coming from joda-time-jsptags and it should run fine in tomcat.

Exclude the following from joda-timetags in pom.xml-
javax.servlet:jsp-api
javax.servlet:jstl

JVerstry said...

Superb code example !!! Really helpful to learn Spring 3.x !!!

Sanjay Acharya said...

@JVerstry thanks and glad it helped :-)

Anonymous said...

Wonderful and it works !! ... such a blessing to have a plug & run tutorial that actually works :D

Majid said...

Hi,
Thanks for this good tutorial, I tried :
mvn integration:test

But got this :


[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 3.420s
[INFO] Finished at: Mon Jul 08 13:34:35 EDT 2013
[INFO] Final Memory: 5M/15M
[INFO] ------------------------------------------------------------------------
[ERROR] No plugin found for prefix 'integration' in the current project and in t
he plugin groups [org.apache.maven.plugins, org.codehaus.mojo] available from th
e repositories [local (C:\Users\z994306\.m2\repository), central (http://repo.ma
ven.apache.org/maven2)] -> [Help 1]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e swit
ch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please rea
d the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/NoPluginFoundF
orPrefixException
C:\Users\z994306\Downloads\springMvcExample>