Close

Understanding ServletContainerInitializer with an Example Project

[Last Updated: Nov 4, 2018]

In previous topic, we talked about how we can add servlet components programmatically. We also talked about Servlet 3.0 new interface ServletContainerInitializer briefly . Here we are going to give an example. Followings are the key points first.

  • ServletContainerInitializer is based on Service Provider Interface (SPI) concept. See an example on SPI here.

  • Implementation of ServletContainerInitializer interface must be configured in META-INF/services directory.

  • ServletContainerInitializer has only one method:

    package javax.servlet;
    ...
    public interface ServletContainerInitializer {
    
        public void onStartup(Set<Class<?>> c, ServletContext ctx)
            throws ServletException;
    }

    ServletContainerInitializer#onStartup method is called by the servlet container (must be supporting at least Servlet 3.0 version). In that method we can do some programmatic servlet/filters/listeners additions, just like we saw a ServletContextListener example in the last topic.

  • The implementation will still get called even if it's packaged in a jar file which the current project is using. This jar must be in WEB-INF/lib. There's no way for client code to turn off that behavior, like we saw by setting metadata-complete="true".

  • The method onStartup(Set<java.lang.Class<?>> c, ServletContext ctx), is passed with class types specified by javax.servlet.annotation.HandlesTypes. Those types could be abstract types or concrete type or most importantly could be on client side.

    @HandlesTypes({MyType.class})
      public class AppInitializer implements ServletContainerInitializer{
    ..
    } 

    In above example, MyType.class and it's specialized subclasses will be passed in Set<Class<?>> via onStartup method.

  • By using a generic type 'A' with javax.servlet.annotation.HandlesTypes, a framework can ask client side to provide application specific information via more specialized implementations of 'A'. This is opposite to what we do by calling framework directly or filling/providing some framework specific information in web.xml. Hence use of ServletContainerInitializer enable frameworks to apply IOC pattern.

  • The implementations of ServletContainerInitializer can also register their servlets/filters/listeners programmatically via ServletContext object (the second argument of the method).



The Example Project

In this example we will create two projects. First one will be on framework level and will be packaged in a jar (not war). The second one will be the client war project. This is a typical MVC example where controller (AppController) is on framework level and hidden from client. Client has to provide their view and model information by implementing a framework specific interface Page

Creating an example Servlet Framework

  1. Prepare project

    • Create a simple maven project (name it servlet-framework-example) using your IDE or maven-archetype-quickstart. See an example here.
    • In pom.xml add dependency of javax.servlet-api:3.0.1
  2. Create implementation of ServletContainerInitializer as AppInitializer. Annotate this class with @HandlesTypes({Page.class}), where Page is the interface must be implemented on the client side. This interface is just returning strings from it's methods. In real framework we will be doing more than that.

  3. Create the file META-INF/services/javax.servlet.ServletContainerInitializer and put fully qualified class name of AppInitializer in it.

  4. Create a servlet class AppController without any annotations and of course there will be no web.xml configuration as it's not a web project. This servlet will get registered during runtime as coded in AppInitializer#onStartup method.

  5. Now as his project is not executable, we are just going to install it in local repository using mvn clean install

Dependencies and Technologies Used:

  • Java Servlet API 3.0.1
  • JDK 1.8
  • Maven 3.0.4

Servlet Container Initializer Project Select All Download
  • servlet-framework-example
    • src
      • main
        • java
          • com
            • logicbig
              • servlet
                • AppInitializer.java
          • resources
            • META-INF
              • services

    Client Project

    Now we are going to create a war project as a client of the above example framework.

    1. Preparing the project
      • Create web application using maven-archetype-webapp, steps here.
      • Delete web.xml, we don't need it at all.
      • Add dependency of above project servlet-framework-example
      • In this example we don't need dependency of javax.servlet-api
      • In pom.xml add tomcat7-maven-plugin to run it as embedded server.
    2. Create two implementations of Page as PageOne and PageTwo
    3. That's it. Now we are going to run our web application from root folder:
      mvn clean tomcat7:run-war
    4. Put following urls one by one in your browser:
      http://localhost:8080/servlet-framework-client/pageOne
      http://localhost:8080/servlet-framework-client/pageTwo
      We should see the expected output as generated by the servlet AppController.

    Dependencies and Technologies Used:

    • com.logicbig.servlet:servlet-framework-example 1.0-SNAPSHOT
    • JDK 1.8
    • Maven 3.0.4

    Servlet Framework Client Select All Download
    • servlet-framework-client
      • src
        • main
          • java
            • com
              • logicbig
                • servlet
                  • PageOne.java

      See Also