Thursday, May 31, 2012

Spring WebApplicationInitializer and programmatic support for web.xml file

Servlet 3.0 provides a programmatic way to specify and configure the contents of a web.xml file.

A servlet 3.0 based web application can implement a ServletContainerInitializer interface, which is explicitly passed a ServletContext which is a runtime representation of the container, to be used for configuring the servlets, filters, listeners and other contents normally specified through a typical web.xml file.

If a custom ServletContainerInitializer is provided, the implementation class name has to be declared in a META-INF/services/java.servlet.ServletContainerInitializer file in the classpath.

Spring 3.1+ makes this a process a little easier by providing an implementation of ServletContainerInitializer called the SpringServletContainerInitializer, registers this implementation through the META-INF/services/java.servlet.ServletContainerInitializer file in the spring-web module, and in turn delegates the responsibility of configuring the ServletContext to classes implementing a custom Spring interface called WebApplicationInitializer.

Essentially from a web application developers perspective the only thing that needs to be done is to implement a WebApplicationInitializer, and configure the ServletContext in the implementation. This is how a custom WebApplicationInitializer for a web application looks like for a typical Spring Application:

public class CustomWebAppInitializer implements WebApplicationInitializer {

 @Override
 public void onStartup(ServletContext container) {
  XmlWebApplicationContext rootContext = new XmlWebApplicationContext();
  rootContext.setConfigLocations(new String[]{"classpath*:META-INF/spring/applicationContext-security.xml", "classpath*:META-INF/spring/applicationContext.xml"});

  container.addListener(new ContextLoaderListener(rootContext));

  ServletRegistration.Dynamic dispatcher = container.addServlet("dispatcher", DispatcherServlet.class);
  dispatcher.setInitParameter("contextConfigLocation", "/WEB-INF/spring/webmvc-config.xml");
  dispatcher.addMapping("/");

  container.addFilter("Spring OpenEntityManagerInViewFilter", org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter.class)
   .addMappingForUrlPatterns(null, false, "/*");

  container.addFilter("HttpMethodFilter", org.springframework.web.filter.HiddenHttpMethodFilter.class)
   .addMappingForUrlPatterns(null, false, "/*");

  container.addFilter("springSecurityFilterChain", new DelegatingFilterProxy("springSecurityFilterChain"))
     .addMappingForUrlPatterns(null, false, "/*");

  FilterRegistration charEncodingfilterReg = container.addFilter("CharacterEncodingFilter", CharacterEncodingFilter.class);
  charEncodingfilterReg.setInitParameter("encoding", "UTF-8");
  charEncodingfilterReg.setInitParameter("forceEncoding", "true");
  charEncodingfilterReg.addMappingForUrlPatterns(null, false, "/*");
 }
}

A web.xml is still required though, to configure some things which cannot be completely configured using this mechanism, the stripped down web.xml looks like this:
<?xml version="1.0" encoding="ISO-8859-1" standalone="no"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
 version="3.0" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" metadata-complete="false">

    <display-name>my app</display-name>
    
    <description>my app</description>

    <context-param>
        <param-name>defaultHtmlEscape</param-name>
        <param-value>true</param-value>
    </context-param>
    
    <session-config>
        <session-timeout>30</session-timeout>
    </session-config>
    
    <error-page>
        <exception-type>java.lang.Exception</exception-type>
        <location>/uncaughtException</location>
    </error-page>
    
    <error-page>
        <error-code>404</error-code>
        <location>/resourceNotFound</location>
    </error-page>
</web-app>

Make a note of the version attribute specified as 3.0, and the schema location being specified as "http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd", these are what trigger the container to consider this as a Servlet 3.0 web application.

That is basically it, with these in place the application can be considered to be configured - mostly without a web.xml file!

References:
http://blog.springsource.org/2011/06/10/spring-3-1-m2-configuration-enhancements/
Greenhouse application at Github - https://github.com/SpringSource/greenhouse - look in the servlet3 branch

5 comments:

  1. Thankyou for sharing, helped me iron out a couple of niggles moving from XML to Java config. :)

    ReplyDelete
  2. thanks, helpful post

    ReplyDelete
  3. This is awesome! Thanks for this!

    ReplyDelete