Search code examples
javatomcatwar

Why JAR works correcly when placed inside WAR libraries but not inside shared tomcat libraries?


I have an application (WAR) deployed on Tomcat, containing multiple jar files. For reasons, I need to extend a class inside one of these jar files and override one of his methods. The new method will be different for every customer based on their necessities.

What I did is creating a small jar file that has a dependency on the main jar, added the class that extends the original one, and compiled it.

Now I want to deploy it on Tomcat so that the new class is loaded by Spring. This works fine if I put the new jar inside WEB-INF/lib of the deployed WAR, but it doesn't work if I put it inside shared/lib (or just lib) of Tomcat. The error is ClassNotFoundException for the one that I'm extending (basically every class that comes from the main jar can't be found). I already checked catalina.properties and it correctly loads the .jar in both locations.

My question is: why this JAR works only if I put it inside the WAR libraries but not if I put it inside the shared lib of Tomcat? Tomcat version is 7.0.86

A little scheme to give a better idea of what works and what doesn't work:

    - tomcat folder
      - lib
        - jar-that-extends-main-jar.jar (ClassNotFoundException for the one I'm extending)
      - shared
        - lib
          - jar-that-extends-main-jar.jar (ClassNotFoundException for the one I'm extending)
      - webapps
        - my-application
          - WEB/INF
            - lib
              - jar-that-extends-main-jar.jar (this works correctly and I can use my custom class)

Solution

  • Tomcat uses different ClassLoaders in kind of a tree hierarchy (you can see a picture as well as some explanations here: https://tomcat.apache.org/tomcat-8.0-doc/class-loader-howto.html).

    Basically, you have a Classloader per webapp, one for shared libraries, one for system and one for bootstrap, where the webapp classloaders are at the bottom of the tree, and then it goes up. A Classloader however can only access its parent classloaders, but not the other way round, so when looking for a class, a classloader will look at its own classes, and then ask its parent classloaders for that specific class.

    That's why you can access classes in the shared classloader from your webapp, but not the otherway round.