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.
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
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
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.
Create the file META-INF/services/javax.servlet.ServletContainerInitializer and put fully qualified class name of AppInitializer in it.
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.
Now as his project is not executable, we are just going to install it in local repository using mvn clean install