Controlling the classpath in a servlet

14,720

Solution 1

As far as I understand the resource selection from the classpath is non-deterministic (from the point of view of the app developer). Even if the same file is loaded consistently the behaviour could change: 1. When you upgrade the version of your current container. 2. If you switch containers.

The simplest solution will be to remove embedded log4j config files from library jars. It is almost never a good idea to embed log4j config's as it leads to the problem you are seeing here...

Are they third party jars or jars you developed?

Solution 2

Tomcat 8.5

Ditto Tomcat 8.0.

See documentation: Class Loader HOW-TO.

Tomcat 8.0

The answer is simple, taken from the Tomcat documentation page, Class Loader HOW-TO. In particular notice the use of the /WEB-INF/ directory/folder.

Therefore, from the perspective of a web application, class or resource loading looks in the following repositories, in this order:

  • Bootstrap classes of your JVM
  • /WEB-INF/classes of your web application
  • /WEB-INF/lib/*.jar of your web application
  • System class loader classes (described above)
  • Common class loader classes (described above)

If the web application class loader is configured with <Loader delegate="true"/> then the order becomes:

  • Bootstrap classes of your JVM
  • System class loader classes (described above)
  • Common class loader classes (described above)
  • /WEB-INF/classes of your web application
  • /WEB-INF/lib/*.jar of your web application

Tomcat 6

Excerpted from Tomcat 6 page, Class Loader HOW-TO.

Therefore, from the perspective of a web application, class or resource loading looks in the following repositories, in this order:

  • Bootstrap classes of your JVM
  • System class loader classes (described above)
  • /WEB-INF/classes of your web application
  • /WEB-INF/lib/*.jar of your web application
  • $CATALINA_HOME/lib
  • $CATALINA_HOME/lib/*.jar

Solution 3

In my experience, WEB-INF/classes typically takes precedence over jars in WEB-INF/lib, however, that also depends on the servlet container you use (I could never figure out the behavior of JRun, for instance). It would help immensely if you could tell me which container you're using.

Also, are you certain that the offending log4j configuration is in a jar in WEB-INF/lib? Typically, when I've run into classpath problems in a servlet container situation, it's because of libraries that reside outside of the web app.

The servlet specs recommend that web app classloaders load their own classes before delegating to the container's classloader (SRV.9.7.2), but since this is counter to the Java spec, not all vendors do this by default (in fact Tomcat is the only container I've used that does this by default). With that said, it's always possible to configure your container's web app classloading behavior. If you tell me which container you're using, I may be able to help you (specifically, I have done this successfully before on WebLogic, WebSphere, Glassfish and JRun)).

Solution 4

If you're unable to control the classpath, since Tomcat is setting it for you, are you at least able to set a system property for log4j.configuration? I believe that location pointed to by that property can be set outside of the classpath.

If not, another approach, although an ugly one, would be to explicitly run one of the configurators yourself in your application code.

Share:
14,720
Ian Dickinson
Author by

Ian Dickinson

Semantic Web, Linked Data, AI, NLP, user experience design, Ruby, Rails, Python, JavaScript, VueJS, HTML+CSS, RDF, OWL, SPARQL. Co-founder of Epimorphics Ltd. Interested in full-stack development for linked-data systems, dataviz, and graph-based systems.

Updated on June 19, 2022

Comments

  • Ian Dickinson
    Ian Dickinson over 1 year

    My servlet application includes a number of library .jars, some of which contain embedded log4j.xml or log4j.properties files. I'd like to ensure that log4j finds my log4j.xml first! I've tried searching for some specification of the priorities of the various classpath elements in a servlet (e.g. does WEB-INF/classes always precede WEB-INF/lib?), or some way to configure or tweak the servlet's classloader so that a given resource directory appears early in the classpath. So far, I've drawn a blank. Any suggestions on ensuring that a servlet .war file loads the correct log4j.xml via the classloader?

  • Ian Dickinson
    Ian Dickinson about 15 years
    That doesn't really answer the question I asked. Is WEB-INF/classes guaranteed to be on the classpath before WEB-INF/lib? What if I want my log4j.xml not to be in WEB-INF/classes but another directory - it's not a class after all.
  • Ian Dickinson
    Ian Dickinson about 15 years
    Third party, hence the problem.
  • Marko
    Marko about 15 years
    But it has to be in classpath
  • johnstok
    johnstok about 15 years
    At the risk of sounding ruthless perhaps you should name and shame them in the question, and file a bug too...
  • johnstok
    johnstok about 15 years
    I would suggest removing the log4j config file manually from the 3rd party jar. This will be a pain though if you are using maven, ivy, etc. to manage dependencies.
  • Ian Dickinson
    Ian Dickinson about 15 years
    Hi Jack, I'm currently using Tomcat 6 (tomcat6-6.0.18-1.1.fc9.noarch) and Jetty 6.1.11. And no, I'm not wholly sure that the clash is coming from log4j configs in my .jars ... we have some weird logging behaviour, and conflicting configs seems like a good hypothesis atm.
  • Ian Dickinson
    Ian Dickinson about 15 years
    Hi johnstok, Normally I do use maven, but I'm working with an intern on this project and it's not mavenized yet. So manually editing the .jars will allow me to see if that's the root cause. Good suggestion, thanks.
  • matt b
    matt b about 15 years
    Is removing the log4j config from that JAR an option? Also let me guess, is it Axis related?
  • Jack Leow
    Jack Leow about 15 years
    I have not used Jetty, but in Tomcat, you can probably rule out classloader configuration as the problem. Is log4j.jar in your WEB-INF/lib directory?