40

I'm trying to register a servlet using servletContainerInitializer but it doesn't seem to work, Maybe it's my code (kindly review it), but I came to wonder about the difference between ServletContainerInitializer and ServletContextListener, because the follwing code runs fine when used as ServletContextListener instead.

From servlet 3.0 specification:

4.4

Configuration methods (adding servlets dynamically):

... or from the onStartup method of a ServletContainerInitializer implementation ...

The ServletContainerInitializer:

package com.marmoush.javaexamples.nullhaus.servlet;

import java.util.Set;
import javax.servlet.ServletContainerInitializer;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;

public class MyInit implements ServletContainerInitializer {
    public void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException {
        System.out.println("hello");
        ServletRegistration reg = ctx.addServlet("q31","com.marmoush.javaexamples.nullhaus.servlet.Q31");
        reg.addMapping("/q31/*");
    }
}

The servlet which I'm trying to auto-register:

package com.marmoush.javaexamples.nullhaus.servlet;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class Q31 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.getWriter().println("hello world");
    }
}

Original code from nullhaus java examples website "only class name edited" also didn't work!

package com.marmoush.javaexamples.nullhaus.servlet;

import java.util.Set;

import javax.servlet.Servlet;
import javax.servlet.ServletContainerInitializer;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;

public class MyInit implements ServletContainerInitializer {
    public void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException {
        try {
            Class klass = Class.forName("com.marmoush.javaexamples.nullhaus.servlet.Q31");
            Class<Q31> clazz = (Class<Q31>) klass;
            Servlet s = ctx.createServlet(clazz);
            ServletRegistration.Dynamic d = ctx.addServlet("q31", s);
            d.addMapping("/baz/*");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}
Community
  • 1
  • 1
Ismail Marmoush
  • 13,140
  • 25
  • 80
  • 114

2 Answers2

54

The ServletContainerInitializer implementation is intented to be bundled in a JAR file which is in turn been dropped in /WEB-INF/lib of the webapp. The JAR file itself should have a /META-INF/services/javax.servlet.ServletContainerInitializer file containing the FQN of the ServletContainerInitializer implementation in the JAR. Please note that this file should thus not be placed in the webapp itself!

This allows webapp module developers to let their JAR file hook on webapp's startup and shutdown cycle. It's true that they could also have used a ServletContextListener with @WebListener for this, but this won't work if the webapp's own web.xml file has a metadata-complete="true" attribute set in <web-app> which means that the webapp shouldn't scan JARs for annotations (which saves startup time).

That the ServletContainerInitializer doesn't work in your particular case can only mean that you're actually not developing a module JAR file, but just a part integral to your own web application. In that case, the ServletContainerInitializer is useless for you and you should be using ServletContextListener instead.

@WebListener
public class Config implements ServletContextListener {

    @Override
    public void contextInitialized(ServletContextEvent event) {
        // Do stuff during server startup.
    }

    @Override
    public void contextDestroyed(ServletContextEvent event) {
        // Do stuff during server shutdown.
    }

}

See also:

Community
  • 1
  • 1
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • thanks alot, I couldn't seem to notice the servletContainerInitializer "only for bundled jars" thing – Ismail Marmoush May 28 '12 at 20:16
  • 3
    You're welcome. It's the same concept as in JDBC 4.0 drivers. Such a driver can automatically get auto-registered without the need for `Class#forName()` if there's a `/META-INF/services/java.sql.Driver` file in the JDBC driver JAR file. See also http://docs.oracle.com/javase/7/docs/api/java/sql/DriverManager.html – BalusC May 28 '12 at 20:19
  • Does the `ServletContainerInitializer` work also for jar in the ear lib folder? – landal79 Jun 10 '13 at 13:58
  • @landal79: no, definitely not. The EAR lib serves an entirely different purpose unrelated to the WAR. WAR related libraries should absolutely not go in EAR lib, but in WAR's own `/WEB-INF/lib`. – BalusC Jun 10 '13 at 13:59
  • 1
    At least with Apache Tomcat 8.0.32, `ServletContainerInitializer`s can be registered for the web application instead of its JARs, as long as the appropriate `META-INF/services/javax.servlet.ServletContainerInitializer` is created. Another important thing to keep in mind is that `ServletContainerInitializer`s during startup load before `ServletContextListener`s, so if you need your code to load before a given third party `ServletContainerInitializer` bundled in a JAR the only option seems to implement a `ServletContainerInitializer` as part of the web application. – Jaime Hablutzel Oct 16 '17 at 14:20
  • nice explaination – zjunothing Dec 02 '22 at 01:39
5

Check if you have configured the ServletContainerInitializer propertly. The ServletContainerInitializer class name should be configured inside a file:

META-INF/services/javax.servlet.ServletContainerInitializer

The file should contain just the class name. For Ex in your case it should look like:

com.marmoush.javaexamples.nullhaus.servlet.MyInit

The file (META-INF/services/javax.servlet.ServletContainerInitializer) can be bundled in a library JAR in WEB-INF/lib.

Here is any example which explains.

Ramesh PVK
  • 15,200
  • 2
  • 46
  • 50
  • doesn't work with me, my folders structure is: /javaexamples/Webcontent/META-INF and /javaexamples/src/com/marmoush... so I created the services folder as you said in META-INF, but didn't work so I went for the link you said and found he said META-INF was in parrallel with "com" folder so I created another META-INF @ /javaexamples/src/META-INF and didn't work either – Ismail Marmoush May 28 '12 at 11:45
  • META-INF/services/javax.servlet.ServletContainerInitializer should be inside a library bundled in WEB-INF/lib – Ramesh PVK May 28 '12 at 15:29
  • @IsmailMarmoush instead of your development folder structure you should check at the folder structure in the generated WAR, there you should have `WEB-INF/classes/META-INF/services/javax.servlet.ServletContainerInitializer` and I can confirm that it worked for registering a `ServletContainerInitializer` in Apache Tomcat 8.0.32. – Jaime Hablutzel Oct 16 '17 at 14:24