Search code examples
javaspringtomcatlog4jc3p0

Is it safe to share a single Classloader for common libraries across multiple <Host> elements?


Here is a snippet from my server.xml file in Tomcat:

<Host name="client9001.example.com" appBase="/web/my-product-code" autoDeploy="true"></Host>
<Host name="client9002.example.com" appBase="/web/my-product-code" autoDeploy="true"></Host>
<Host name="client9003.example.com" appBase="/web/my-product-code" autoDeploy="true"></Host>
...
<Host name="client9254.example.com" appBase="/web/my-product-code" autoDeploy="true"></Host>

It is well-known that every Host element creates its own WebappClassloader instance, which makes every host be totally isolated and safe. With this setup I can safely use one Tomcat instance to serve X customers, each one being served under a different subdomain. This setup prevents customer A's objects to be manipulated by customer B's actions.

However, this creates a non-scalable need for non-heap space (since every class is loaded X times) and I was wondering if I could create a shared class loader for all the hosts. I know that Tomcat supports that kind of thing (using the "Common" class loader in https://tomcat.apache.org/tomcat-8.0-doc/class-loader-howto.html#Class_Loader_Definitions) but my question is whether it is safe to do so. I mean, I can make my own classes safe by eliminating all static fields but is this enough? I'm worried about the 3rd party libraries, like the Spring Framework, Log4J, Joda-time and several others that come with my product code.

I have already used reflection to identify all the static variables in those classes and I have discovered literally 252 static-and-non-final fields and 5772 static-and-final fields, and that's all in 3rd party code (my own code has only 3 fields that I will eliminate).

Should I go down this road and review all those 6027 fields? I mean, is this whole idea a bad idea? If so, then what kind of code could ever be shared in the "Common" class loader?

Is there any strong evidence that all these famous frameworks are safe to be shared like this or should I treat everything as unsafe-until-proven-otherwise?


Solution

  • I would be really cautious ... and decide if performance is worth the risk

    I was thinking of hypothetical situation where you could have some unintended consequences of using shared ClassLoader...

    I took a look at Log4J which creates loggers through LogManager which extends java.util.logging.LogManager which is used to maintain a set of shared state about Loggers and log services.

    Imagine the case where these clients do private static final Logger LOGGER = LogManager.getLogger(); to create loggers for each host

     <Host name="client9001.example.com">  
     <Host name="client9002.example.com">
     <Host name="client9003.example.com"> 
    
     <Host name="BADclient.example.com"> 
    

    Because the LogManager is statically shared between all hosts, the bad client can call LogManagers.getLoggerNames(); which would return a list of all registered loggers to that static LogManager instance and would leak information about other clients running on the same tomcat instance / be able to remove the eventlisteners for the other hosts using that same class ( **I haven't tried this **)

    Basically you want to put code that would be 100% shared between all hosts (and all future hosts) running on the the JVM. The common example would be initializing JDBC connection.

    Also in terms of code maintenance, if you were going to go down the rabbit hole, and try to review all static methods/fields .. with every update of the 3rd party imports you would have to review them again an make sure nothing was introduced ... and maybe even review the imports of the 3rd party imports

    Might find this question intresting Tomcat classloader violates delegating policy as he asks about the danger of using common class loaders in Tomcat