2

Background

We have several webapps running on Tomcat 7.0.x (on Linux). For historical reasons, we have populated the "Common" lib (i.e. $CATALINA_HOME/lib) with many application jars. These jars include Spring, Jackson, and our own persistence-layer jars.

As we deploy new persistence-layer jars, we are embroiled in a classpath hell: some jars are OK in ~/webapps/foo/WEB-INF/lib, but some must be excluded and placed in Common. Recent adventures show that some may have to be in both places.

Our local (Windows) build is similarly messy: we have one folder filled with jars. We build with Eclipse (not Gradle, Ant, etc) and do not have an explicit list of jars used by the web applications. The Linux build does use Ant, but semi-selectively globs jars into the WAR files.

Question

I would like to white-board a strategy on how to extricate ourselves from this predicament. One approach is listed below, but I would appreciate any refinements, or alternative approaches.

The goal is to correct the deploy process, not the local build.

Strategy

On local machine, use Gradle to compile each web application:

  • add compile-time dependencies until compilation is OK
  • locate Gradle's local cache (or observe output) to find transitive run-time dependencies
  • add all jars to ~/WEB-INF/lib for the web app, with explicit references in the formal Ant build
  • deploy into a fresh, pristine Tomcat install
  • observe startup logs for any "ClassNotFound" exceptions, both for 3rd-party libraries and our application libraries
  • add these libraries to the formal Ant build
  • perform regression QA tests on application and monitor for "ClassNotFound"
  • One word of caution - some logging frameworks can cause classloader resource leaks when in WEB-INF/lib, because they pass a reference to their own classes to the standard java logging API, which holds onto references to them indefinitely. If they are in WEB-INF/lib, this class has a reference to the webapp classloader, to which no references are supposed to survive after the app is stopped. You may want to ensure you are using a logging system that doesn't have this bug. – Jules Nov 8 '14 at 16:10
  • Note, though the strategy may (?) be sound, we ultimately ended up putting all jars in each WAR file, with few (if any) jars in $CATALINA_HOME/lib. The disadvantage is the size of of the WAR files, but the upside is that classpath hell was mitigated. – Michael Easter Oct 21 '17 at 17:20
2

In my experience building applications using Maven for Tomcat 6 and Tomcat 7, I never needed to deploy jars explicitly to Tomcat's common lib directory.

Take a look at this post: https://stackoverflow.com/questions/267953/managing-libraries-in-tomcat

Answers there indicate that conflicts may arise between classes in the shared common lib folder, and those in each web application, as each web application may have dependencies of differing versions.

Also, according to the Tomcat page you linked to for Common class loader libraries: "Normally, application classes should NOT be placed here".

For example, if you had log4j-1.2.15.jar in the commons/lib directory, and log4j-1.2.17.jar in your application lib dir, Tomcat may try to load 1.2.15, while your application was written to use 1.2.17, which can lead to ClassNotFoundExceptions, and potentially other exceptions.

I'd recommend only placing libraries that Tomcat needs for itself into the common lib directory, and let each application context load its own dependencies.

If you use Maven (and configure the project correctly), you should not need to manually move any jars around. Gradle may have similar functionality.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.