5

We have an application which keeps loading instances of ServletContainerInitializer from our 3rd party libs.

One instance is JerseyServletContainerInitializer and the other is SpringServletContainerInitializer. These classes from Jersey and Spring seem to "take over" our servlet context messing with our mappings and filters and such.

We really need to explicitly configure our servlet container's web.xml and this auto scanning is driving us insane. By simply pulling in a dependency in our pom.xml our runtime ServletContext configurations such as Servlets/Filters/ContextListeners are mutated because the servlet container finds these libraries on the classpath.

Is there a way to use Servlet 3 but disable its annoying auto classpath scanning "feature"?

benstpierre
  • 32,833
  • 51
  • 177
  • 288
  • 2
    Did you actually try googling "[servlet 3 disable classpath scanning](https://www.google.com/#q=servlet%203%20disable%20classpath%20scanning)"? Set ``. – Andreas Apr 29 '16 at 20:00
  • @Andreas this configuration does not disables autoscan, it only disables it on the lib folder, but it still looks into /class folder. – VitorCruz Jun 20 '20 at 18:10
  • @VitorCruz Since a well-deployed production application doesn't have any files in the `WEB-INF/classes` folder, that is of little consequence. – Andreas Jun 20 '20 at 18:19
  • @Andreas What you mean? /WEB-INF/classes is where you put classes of your web application, why a well-deployed would not use this folder? – VitorCruz Jun 24 '20 at 19:41
  • @VitorCruz Because a well-deployed webapp would have packaged its own files in a jar file too. – Andreas Jun 24 '20 at 19:46
  • @Andreas why? Seems controversial to me, do you have references that explain this? To me it is completely normal to use class folder, can't see a problem with this.... I myself have never faced this pattern you described. If it is not a widespread pattern, your proposed solution will go only half way. – VitorCruz Jun 25 '20 at 02:55
  • @VitorCruz Sorry, by well-deployed I'm referring to production-quality deployment. As for pattern, example quote from [Learning Java, 4th Edition by Daniel Leuck, Patrick Niemeyer](https://www.oreilly.com/library/view/learning-java-4th/9781449372477/ch15s03.html): *During **development**, it is often easier to work with the “loose” `classes` directory and use the `lib` directory for supporting classes and third-party tools.* Implying that for production deployments, the `classes` directory should not be used. – Andreas Jun 25 '20 at 04:02
  • @Andreasint the same paragraph I quote: _You can place your classes in either location._ There is no strict recommendation on for or against use one or another folder. There are disadvantages of using WEB-INF/classes dir in production? I am not aware of any such discussion or set of recommendations in this line. This don't seem widespread at all. Anyways, some answers below take in account how to stop scanning inside WEB-INF\classes as well. – VitorCruz Jun 25 '20 at 16:59

4 Answers4

14

From https://wiki.apache.org/tomcat/HowTo/FasterStartUp

There are two options that can be specified in your WEB-INF/web.xml file:

  • Set metadata-complete="true" attribute on the <web-app> element.
  • Add an empty <absolute-ordering /> element.

Setting metadata-complete="true" disables scanning your web application and its libraries for classes that use annotations to define components of a web application (Servlets etc.). The metadata-complete option is not enough to disable all of annotation scanning. If there is a SCI with a @HandlesTypes annotation, Tomcat has to scan your application for classes that use annotations or interfaces specified in that annotation.

The <absolute-ordering> element specifies which web fragment JARs (according to the names in their WEB-INF/web-fragment.xml files) have to be scanned for SCIs, fragments and annotations. An empty element configures that none are to be scanned.

In Tomcat 7 the absolute-ordering option affects discovery both of SCIs provided by web application and ones provided by the container (i.e. by the libraries in $CATALINA_HOME/lib). In Tomcat 8 the option affects the web application ones only, while the container-provided SCIs are always discovered, regardless of absolute-ordering. In such case the absolute-ordering option alone does not prevent scanning for annotations, but the list of JARs to be scanned will be empty, and thus the scanning will complete quickly. The classes in WEB-INF/classes are always scanned regardless of absolute-ordering.

Scanning for web application resources and TLD scanning are not affected by these options.

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
Leonard Brünings
  • 12,408
  • 1
  • 46
  • 66
3

I found that neither of the given answers worked.

The only way to actually filter out 3rd-party SCIs from the classpath turned out to be the containerSciFilter attribute of the Context container (in context.xml), which is defined as

The regular expression that specifies which container provided SCIs should be filtered out and not used for this context. Matching uses java.util.regex.Matcher.find() so the regular expression only has to match a sub-string of the fully qualified class name of the container provided SCI for it to be filtered out. If not specified, no filtering will be applied.

In my case, I wanted to filter out stuff like org.eclipse.jetty.apache.jsp.JettyJasperInitializer so I used <Context ... containerSciFilter="jetty" ...>

typeracer
  • 759
  • 8
  • 11
1

The specification statement regarding absolute-ordering, per the Servlet 3.1 specification, is found in Section 8.2.2 "Ordering of web.xml and web-fragment.xml", part 1.d.:

The element may contain zero or one element. The required action for this element is described below. If the element does not contain an element, any web-fragment not specifically mentioned within elements MUST be ignored. Excluded jars are not scanned for annotated servlets, filters or listeners. However, if a servlet, filter or listener from an excluded jar is listed in web.xml or a non-excluded web-fragment.xml, then it's annotations will apply unless otherwise excluded by metadata-complete.

A key take-away is that using an empty absolute-ordering element will not just turn off scanning for servlet-container initializers. Scanning for all component defining annotations, such as @WebServlet, is turned off. Use of an empty absolute-ordering element is only advised if you really want to turn off all annotations processing of JAR files within a web application.

The scanning can be more finely tuned: Use an absolute-ordering element, and include the names of JARs which are to be scanned using name elements, and omit JARs which you wish to have skipped. DO NOT use an others element, as this will put back into the ordering all of the JARs which are not explicitly listed, and will turn scanning back on for those JARs.

As a general practice, we've found that turning off all annotation processing is dangerous, as a web application which is enabled for annotation scanning has some classes which must be scanned, which means that turning off the scanning entirely would break the web application.

Simulant
  • 19,190
  • 8
  • 63
  • 98
Thomas Bitonti
  • 1,179
  • 7
  • 14
0

JIK here is my case: I had to add another project as a dependency to my project but that project contained its own WebInit (that is a subclass of AbstractAnnotationConfigDispatcherServletInitializer) so when my war was deployed two WebApplicationInitializer was called (my and the other).

So to avoid this I had

  • add a ${projectRoot}/src/main/webapp/WEB-INF/web.xml with an empty <absolute-ordering />
  • add ${projectRoot}/src/main/resources/META-INF/services/javax.servlet.ServletContainerInitializer text file (the file name should be exact "javax.servlet.ServletContainerInitializer") with one line my.custom.package.MyCustomeServletContainerInitializer
  • add a class MyCustomeServletContainerInitializer to the package my.custom.package in my case it's a kotlin class like this
class MyCustomeServletContainerInitializer: ServletContainerInitializer {
    override fun onStartup(webAppInitializerClasses: MutableSet<Class<*>>?, servletContext: ServletContext) = ServletInitializer().onStartup(servletContext)
}

where ServletInitializer is my own implementation of WebApplicationInitializer or rather SpringBootServletInitializer

Yuko
  • 21
  • 1
  • 6