Maven by Example
7.6. The Simple Web Application Module

The web application is defined in a simple-webapp project. This
simple web application project is going to define two Spring MVC
Controllers: WeatherController and simple-weather and the
applicationContext-persist.xml file in simple-persist. The
component architecture of this simple web application is shown in
Figure 7.3, “Spring MVC Controllers Referencing Components in simple-weather and simple-persist.”.
The POM for simple-webapp is shown in
POM for simple-webapp.
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.sonatype.mavenbook.multispring</groupId>
<artifactId>simple-parent</artifactId>
<version>1.0</version>
</parent>
<artifactId>simple-webapp</artifactId>
<packaging>war</packaging>
<name>Simple Web Application</name>
<dependencies>
<dependency> (1)
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.4</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.sonatype.mavenbook.multispring</groupId>
<artifactId>simple-weather</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.sonatype.mavenbook.multispring</groupId>
<artifactId>simple-persist</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring</artifactId>
<version>2.0.7</version>
</dependency>
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity</artifactId>
<version>1.5</version>
</dependency>
</dependencies>
<build>
<finalName>simple-webapp</finalName>
<plugins>
<plugin> (2)
<groupId>org.mortbay.jetty</groupId>
<artifactId>maven-jetty-plugin</artifactId>
<dependencies> (3)
<dependency>
<groupId>hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<version>1.8.0.7</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId> (4)
<artifactId>hibernate3-maven-plugin</artifactId>
<version>2.0</version>
<configuration>
<components>
<component>
<name>hbm2ddl</name>
<implementation>annotationconfiguration</implementation> (5)
</component>
</components>
</configuration>
<dependencies>
<dependency>
<groupId>hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<version>1.8.0.7</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
</project>
As this book progresses and the examples become more and more substantial, you’ll notice that the pom.xml begins to take on some weight. In this POM, we’re configuring four dependencies and two plugins. Let’s go through this POM in detail and dwell on some of the important configuration points:
|
This |
|
|
The Maven Jetty plugin couldn’t be easier to add to this project;
we simply add a |
|
|
In our build configuration, we’re going to be configuring the Maven Hibernate3 Plugin to hit an embedded HSQLDB instance. For the Maven Hibernate 3 plugin to successfully connect to this database using JDBC, the plugin will need reference the HSQLDB JDBC driver on the classpath. To make a dependency available for a plugin, we add a dependency declaration right inside plugin declaration. In this case, we’re referencing hsqldb:hsqldb:1.8.0.7. The Hibernate plugin also needs the JDBC driver to create the database, so we have also added this dependency to its configuration. |
|
|
The Maven Hibernate plugin is when this POM starts to get
interesting. In the next section, we’re going to run the |
|
|
The Maven Hibernate3 plugin has different ways to obtain Hibernate
mapping information that are appropriate for different usage scenarios
of the Hibernate3 plugin. If you were using Hibernate Mapping XML
(.hbm.xml) files, and you wanted to generate model classes using the
|
Next, we turn our attention to the two Spring MVC controllers that
will handle all of the requests. Both of these controllers reference
the beans defined in simple-weather and simple-persist.
simple-webapp WeatherController.
package org.sonatype.mavenbook.web;
import org.sonatype.mavenbook.weather.model.Weather;
import org.sonatype.mavenbook.weather.persist.WeatherDAO;
import org.sonatype.mavenbook.weather.WeatherService;
import javax.servlet.http.*;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
public class WeatherController implements Controller {
private WeatherService weatherService;
private WeatherDAO weatherDAO;
public ModelAndView handleRequest(HttpServletRequest request,
HttpServletResponse response) throws Exception {
String zip = request.getParameter("zip");
Weather weather = weatherService.retrieveForecast(zip);
weatherDAO.save(weather);
return new ModelAndView("weather", "weather", weather);
}
public WeatherService getWeatherService() {
return weatherService;
}
public void setWeatherService(WeatherService weatherService) {
this.weatherService = weatherService;
}
public WeatherDAO getWeatherDAO() {
return weatherDAO;
}
public void setWeatherDAO(WeatherDAO weatherDAO) {
this.weatherDAO = weatherDAO;
}
}
WeatherController implements the Spring MVC Controller interface
that mandates the presence of a handleRequest() method with the
signature shown in the example. If you look at the meat of this
method, you’ll see that it invokes the retrieveForecast() method on
the weatherService instance variable. Unlike the previous chapter,
which had a Servlet that instantiated the WeatherService class, the
WeatherController is a bean with a weatherService property. The
Spring IoC container is responsible for wiring the controller to the
weatherService component. Also notice that we’re not using the
WeatherFormatter in this Spring controller implementation; instead,
we’re passing the Weather object returned by retrieveForecast() to
the constructor of ModelAndView. This ModelAndView class is going
to be used to render a Velocity template, and this template will have
references to a ${weather} variable. The weather.vm template
is stored in src/main/webapp/WEB-INF/vm and is shown in
weather.vm Template Rendered by WeatherController.
In the WeatherController, before we render the output of the
forecast, we pass the Weather object returned by the
WeatherService to the save() method on WeatherDAO. Here we are
saving this Weather object—using Hibernate—to an HSQLDB
database. Later, in HistoryController, we will see how we can
retrieve a history of weather forecasts that were saved by the
WeatherController.
weather.vm Template Rendered by WeatherController.
<b>Current Weather Conditions for:
${weather.location.city}, ${weather.location.region},
${weather.location.country}</b><br/>
<ul>
<li>Temperature: ${weather.condition.temp}</li>
<li>Condition: ${weather.condition.text}</li>
<li>Humidity: ${weather.atmosphere.humidity}</li>
<li>Wind Chill: ${weather.wind.chill}</li>
<li>Date: ${weather.date}</li>
</ul>
The syntax for this Velocity template is straightforward: variables
are referenced using ${} notation. The expression between the
curly braces references a property, or a property of a property on the
weather variable, which was passed to this template by the
WeatherController.
The HistoryController is used to retrieve recent forecasts that have
been requested by the WeatherController. Whenever we retrieve a
forecast from the WeatherController, that controller saves the
Weather object to the database via the WeatherDAO. WeatherDAO
then uses Hibernate to dissect the Weather object into a series of
rows in a set of related database tables. The HistoryController is
shown in simple-web HistoryController.
package org.sonatype.mavenbook.web;
import java.util.*;
import javax.servlet.http.*;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
import org.sonatype.mavenbook.weather.model.*;
import org.sonatype.mavenbook.weather.persist.*;
public class HistoryController implements Controller {
private LocationDAO locationDAO;
private WeatherDAO weatherDAO;
public ModelAndView handleRequest(HttpServletRequest request,
HttpServletResponse response) throws Exception {
String zip = request.getParameter("zip");
Location location = locationDAO.findByZip(zip);
List<Weather> weathers = weatherDAO.recentForLocation( location );
Map<String,Object> model = new HashMap<String,Object>();
model.put( "location", location );
model.put( "weathers", weathers );
return new ModelAndView("history", model);
}
public WeatherDAO getWeatherDAO() {
return weatherDAO;
}
public void setWeatherDAO(WeatherDAO weatherDAO) {
this.weatherDAO = weatherDAO;
}
public LocationDAO getLocationDAO() {
return locationDAO;
}
public void setLocationDAO(LocationDAO locationDAO) {
this.locationDAO = locationDAO;
}
}
The HistoryController is wired to two DAO objects defined in
simple-persist. The DAOs are bean properties of the
HistoryController: WeatherDAO and LocationDAO. The goal of the
HistoryController is to retrieve a List of Weather objects which
correspond to the zip parameter. When the WeatherDAO saves the
Weather object to the database, it doesn’t just store the zip code,
it stores a Location object which is related to the Weather object
in the simple-model. To retrieve a List of Weather objects, the
HistoryController first retrieves the Location object that
corresponds to the zip parameter. It does this by invoking the
findByZip() method on LocationDAO.
Once the Location object has been retrieved, the HistoryController
will then attempt to retrieve recent Weather objects that match the
given Location. Once the List<Weather> has been retrieved, a
HashMap is created to hold two variables for the history.vm
Velocity template shown in history.vm Rendered by the HistoryController.
history.vm Rendered by the HistoryController.
<b>
Weather History for: ${location.city}, ${location.region}, ${location.country}
</b>
<br/>
#foreach( $weather in $weathers )
<ul>
<li>Temperature: $weather.condition.temp</li>
<li>Condition: $weather.condition.text</li>
<li>Humidity: $weather.atmosphere.humidity</li>
<li>Wind Chill: $weather.wind.chill</li>
<li>Date: $weather.date</li>
</ul>
#end
The history.vm template in src/main/webapp/WEB-INF/vm references
the location variable to print out information about the location of
the forecasts retrieved from the WeatherDAO. This template then uses
a Velocity control structure, #foreach, to loop through each element
in the weathers variable. Each element in weathers is assigned to
a variable named weather and the template between #foreach and
#end is rendered for each forecast.
You’ve seen these Controller implementations, and you’ve seen that
they reference other beans defined in simple-weather and
simple-persist, they respond to HTTP requests, and they yield
control to some mysterious templating system that knows how to render
Velocity templates. All of this magic is configured in a Spring
application context in
src/main/webapp/WEB-INF/weather-servlet.xml. This XML configures the
controllers and references other Spring-managed beans, it is loaded by
a ServletContextListener which is also configured to load the
applicationContext-weather.xml and applicationContext-persist.xml
from the classpath. Let’s take a closer look at the
weather-servlet.xml shown in Spring Controller Configuration weather-servlet.xml.
Spring Controller Configuration weather-servlet.xml.
<beans>
<bean id="weatherController" (1)
class="org.sonatype.mavenbook.web.WeatherController">
<property name="weatherService" ref="weatherService"/>
<property name="weatherDAO" ref="weatherDAO"/>
</bean>
<bean id="historyController"
class="org.sonatype.mavenbook.web.HistoryController">
<property name="weatherDAO" ref="weatherDAO"/>
<property name="locationDAO" ref="locationDAO"/>
</bean>
<!-- you can have more than one handler defined -->
<bean id="urlMapping"
class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="urlMap">
<map>
<entry key="/weather.x"> (2)
<ref bean="weatherController" />
</entry>
<entry key="/history.x">
<ref bean="historyController" />
</entry>
</map>
</property>
</bean>
<bean id="velocityConfig" (3)
class="org.springframework.web.servlet.view.velocity.VelocityConfigurer">
<property name="resourceLoaderPath" value="/WEB-INF/vm/"/>
</bean>
<bean id="viewResolver" (4)
class="org.springframework.web.servlet.view.velocity.VelocityViewResolver">
<property name="cache" value="true"/>
<property name="prefix" value=""/>
<property name="suffix" value=".vm"/>
<property name="exposeSpringMacroHelpers" value="true"/>
</bean>
</beans>
|
The weather-servlet.xml defines the two controllers as
Spring-managed beans. |
|
|
The |
|
|
Since we are using the Velocity templating engine, we will need to
pass in some configuration options. In the |
|
|
Last, the |
Finally, the simple-webapp project was a web.xml which provides
the basic configuration for the web application. The web.xml file is
shown in web.xml for simple-webapp.
<web-app id="simple-webapp" version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<display-name>Simple Web Application</display-name>
<context-param> (1)
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:applicationContext-weather.xml
classpath:applicationContext-persist.xml
</param-value>
</context-param>
<context-param> (2)
<param-name>log4jConfigLocation</param-name>
<param-value>/WEB-INF/log4j.properties</param-value>
</context-param>
<listener> (3)
<listener-class>
org.springframework.web.util.Log4jConfigListener
</listener-class>
</listener>
<listener>
<listener-class> (4)
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<servlet> (5)
<servlet-name>weather</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping> (6)
<servlet-name>weather</servlet-name>
<url-pattern>*.x</url-pattern>
</servlet-mapping>
</web-app>
|
<calloutlist> Here’s a bit of magic which allows us to reuse the
applicationContext-weather.xml and applicationContext-persist.xml
in this project. The |
|
|
The |
|
|
This makes sure that the Log4J system is configured when the web
application starts. It is important to put this |
|
|
The |
|
|
We define a Spring MVC |
|
|
All requests ending in .x will be routed to the |
