Pages

Spittr - a Spring Web Application

I am going to create a Spring Web Application using as IDE the Spring Tool Suite, friendly known as STS, version 3.8.4, without the Spring Boot support. To do that, we can theoretically use the wizard named Spring Legacy Project, but the support from Pivotal is not so strong as I expected and the result needs so many adjustments that in the end I opted to use the basic Maven Project wizard instead.

I selected the archetype with artifact id maven-archetype-webapp from the group id org.apache.maven.archetypes, gave a group and artifact id to my app, and let the wizard go.

Annoyingly, I got an error and a warning:
The superclass "javax.servlet.http.HttpServlet" was not found on the Java Build Path means that the generated POM lacks that dependency. Actually, opening the POM, I see that it lacks almost any dependency, and the only one present, JUnit, refers to the old 3.8.1 version.
The warning points out to the fact that the wizard was designed to support the ancient Java 5, and we'd probably better adjust the project also in this area.

I changed the POM adding these properties and dependencies:
<properties>
 <java.version>1.8</java.version>
 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 <jsp.version>2.2</jsp.version>
 <jstl.version>1.2</jstl.version>
 <servlet.version>2.5</servlet.version>
 <spring-framework.version>4.3.7.RELEASE</spring-framework.version>
 <junit.version>4.12</junit.version>
</properties>

<dependencies>
 <dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-webmvc</artifactId>
  <version>${spring-framework.version}</version>
 </dependency>
 <dependency>
  <groupId>javax.servlet</groupId>
  <artifactId>jstl</artifactId>
  <version>${jstl.version}</version>
 </dependency>
 <dependency>
  <groupId>javax.servlet</groupId>
  <artifactId>servlet-api</artifactId>
  <version>${servlet.version}</version>
  <scope>provided</scope>
 </dependency>
 <dependency>
  <groupId>javax.servlet.jsp</groupId>
  <artifactId>jsp-api</artifactId>
  <version>${jsp.version}</version>
  <scope>provided</scope>
 </dependency>
 <dependency>
  <groupId>junit</groupId>
  <artifactId>junit</artifactId>
  <version>${junit.version}</version>
  <scope>test</scope>
 </dependency>
</dependencies>
To get rid of the warning, I opened the Project Properties and in the page Project Facets I set Java to 1.8.
The wizard created an index.jsp page in the webapp folder. I removed it and I created instead a home.jsp in the WEB-INF views subfolder. This JSP page uses the JSTL core library and a style sheet named style.css from the resources folder. All of these details are not much relevant with the job of creating the web app, so I won't say more on them. For more reference see the full source code on GitHub.

More interestingly, let see the dispatching stream of our application. We are going to use the MVC Spring structure, at the center of it there is the DispatcherServlet that decides how to manage the user requests to generate appropriate responses. From our side, we provide a Java class to configure it. The job is simplified by extending AbstractAnnotationConfigDispatcherServletInitializer and a couple of configuration classes. Our class would be automatically seen by Spring and used to configure the DispatcherServlet.

At the end of all this dispatching and configuring, we'll have a tiny web app that would have just the home.jsp as available view.
Here is the project structure:

Our initializer should override three methods from its super class:
public class SpitterWebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
    @Override
    protected String[] getServletMappings() {  // 1
        return new String[] { "/" };
    }

    @Override
    protected Class[] getRootConfigClasses() {  // 2
        return new Class[] { RootConfig.class };
    }

    @Override
    protected Class[] getServletConfigClasses() {  // 3
        return new Class[] { WebConfig.class };
    }
}
1. It returns the paths that the DispatcherServlet will be mapped to. We are mapping to the root directory, handling all the requests to our app.
2. It returns the configuration classes for the application context created by the ContextLoaderListener. You would usually find here middle and data tier components. In this trivial version of our app, there will be nothing in here. I still delegate to RootConfig, even if it won't do anything, to keep the code clean. More brutally, I could have let this method return null.
3. This is about the standard DispatcherServlet configuration. Controllers, view resolvers and handler mappings are returned by this method.

Let's see our WebConfig:
@Configuration  // 1
@EnableWebMvc  // 2
@ComponentScan("spittr.web")  // 3
public class WebConfig extends WebMvcConfigurerAdapter {  // 4
    @Bean
    public ViewResolver viewResolver() {  // 5
        InternalResourceViewResolver resolver = new InternalResourceViewResolver();
        resolver.setPrefix("/WEB-INF/views/");
        resolver.setSuffix(".jsp");
        return resolver;
    }

    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();  // 6
    }
}
1. It is a Spring configuration class.
2. This is a typical way to say that our app should enable the Spring MVC.
3. Ask Spring to enable component scanning from the passed package.
4. We extends our configuring class from this configurer adapter provided by Spring
5. We create a very simple view resolver that would set a prefix, the /WEB-INF/views/ folder, and a suffix, the jsp extension. In this way, a view named "home" would be resolved to "/WEB-INF/views/home.jsp".
6. We say to the DispatcherServlet that it should delegate to static resources the requests it gets.

Then I created a controller in the spittr.web package, so to actually provide something to do to WebConfig:
@Controller
public class HomeController {
    @RequestMapping(value = "/", method = GET)
    public String home(Model model) {
        return "home";
    }
}
Very simple, it just maps the GET requests to root returning the string "home".

Now we can Run on Server our app for the first time.
Reference: Building Spring web applications, from Spring in Action, Fourth Edition by Craig Walls. Chapter five, section one, Getting started with Spring MVC.
Full code is available on GitHub.

Go to the full post