2

In Apache Tomcat, I have seen some posts that refer to the path $CATALINA_HOME/shared/lib.

When I download Tomcat, I see no shared folder nested in the Tomcat home folder.

I do see a $CATALINA_HOME/lib path, a lib folder nested in the Tomcat home folder. I understand this is the proper place for JAR files that should not be copied across "contexts" (web apps) in Tomcat. JDBC drivers are one important example of such.

The problem is that Tomcat populates that $CATALINA_HOME/lib folder with many JARs for its own use. So for the sake of neatness and ease of administration, it would make sense to have another folder that held nothing but our added JARs, separate from Tomcat’s own JARs. So I can understand the need for something like $CATALINA_HOME/shared/lib.

The problem is that:

  • (a) I see no shared folder,
  • (b) I cannot find any documentation about such a folder.

➥ Is this shared/lib a real feature, and should I create this pair of folders nested within the Tomcat home folder?

➥ Is $CATALINA_HOME/shared/lib an undocumented feature, perhaps just a hack that might go away in future updates?

I am asking for Tomcat 9 specifically. But Tomcat 8 is still in common usage, so an answer there too would be of use to other folks.

Mark Rotteveel
  • 100,966
  • 191
  • 140
  • 197
Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
  • The "shared" classloader was essentially removed from the default configuration in Tomcat because of the amount of confusion it caused with users not being able to understand the differences between the "shared" and the "common" classloaders. Most people want "common" semantics and failed to configure things correctly. Some advanced users still wanted the feature, so it was retained in a way that makes things simple for lay users but gives super users the ability to use the shared classloader if necessary. – Christopher Schultz Sep 30 '19 at 21:55
  • For my money, you should leave the shared classloader undefined and put your libraries into `CATALINA_BASE/lib`. Note that `CATALINA_BASE` is different than `CATALINA_HOME` and you can use that distinction to keep your "shared" libraries separate from Tomcat's bog-standard libraries it uses for itself. – Christopher Schultz Sep 30 '19 at 21:58
  • @ChristopherSchultz No, your last comment seems incorrect — base and home are *both* in the common loader. The `catalina.properties` file shipped with Tomcat 9.0.26 defines the `common.loader` property to include any `lib` folder in *both* `catalina.base` and `catalina.home`. Here is the string: `common.loader="${catalina.base}/lib","${catalina.base}/lib/*.jar","${catalina.home}/lib","${catalina.home}/lib/*.jar"` – Basil Bourque Oct 03 '19 at 21:30

1 Answers1

2

Caveat: I am not a Tomcat expert, this Answer is merely my cobbled-together understanding of how things work. I may well be wrong; please correct me.

tl;dr

Yes, this is a real feature.

You can create a folder most anywhere you want to collect the JAR files to be shared across one or more web apps within Tomcat.

diagram of class loaders in Tomcat

Tell Tomcat to use that folder by editing catalina.properties file to assign a value to the property named shared.loader.

Shared class loader

You are referring to the shared loader feature of Tomcat.

As you mentioned, some JARs such has JDBC drivers should not be replicated separately across multiple web-apps within a Tomcat instance. This topic has been discussed many times on Stack Overflow.

Such JARs should be loaded by a shared Java Class Loader, rather than the per-web-app class loaders.

Several class loaders

As you can see in the diagram above, Tomcat can use many different class loaders, as explained in the doc.

The Bootstrap and System class loaders are irrelevant to our discussion here.

The Server class loader is used by Tomcat's own needs. This may include looking up Realm related users in a database on your behalf. In such a case, Tomcat needs a JDBC driver to your database of choice.

As seen in the diagram with Webapp1 & Webapp2, each of your web apps also get a class loader. This keeps each web-app separate, preventing them from stepping on each other’s toes. For example, each web app might use a different version of a logging framework.

The trick with JDBC drivers is that they share a registry in a JVM-wide singleton DriveManager object. This design choice by the JDBC team unfortunately conflicts with the needs of an app server such as Tomcat using various class loaders. This is discussed in Question, Why must the JDBC driver be put in TOMCAT_HOME/lib folder? and in many others.

So it is best to share a single JAR file for each kind of JDBC driver across Tomcat's class loaders. That does mean all your web-apps must use the same version of each kind of JDBC driver (Postgres, H2, Oracle, etc.).

  • If you have not enabled the Realm user lookup feature in Tomcat, then Tomcat may have no use of its own for your database. So you can put your JDBC driver in the Shared class loader, for use across one or more web apps.
  • If you have enabled something in Tomcat that needs your database, then put that JDBC JAR file in the Common class loader for use across both Tomcat’s internals and one or more of your web apps.

The trick is that by default, Tomcat only defines the Common class loader specifically. The Common class loader does double-duty as the Server and Shared class loaders. To activate separate class loaders for Server and/or Shared, edit the catalina.properties file. Look for the server.loader & shared.loader properties.

catalina.properties file

As for documentation, see this main page in the manual: Class Loader How-To. Also a mention in a wiki page.

This issue is briefly discussed in comments found within the $CATALINA_HOME/conf/catalina.properties file. Excerpting, per their Apache License 2 terms:

#
# List of comma-separated paths defining the contents of the "shared"
# classloader. Prefixes should be used to define what is the repository type.
# Path may be relative to the CATALINA_BASE path or absolute. If left as blank,
# the "common" loader will be used as Catalina's "shared" loader.
# Examples:
#     "foo": Add this folder as a class repository
#     "foo/*.jar": Add all the JARs of the specified folder as class
#                  repositories
#     "foo/bar.jar": Add bar.jar as a class repository
# Please note that for single jars, e.g. bar.jar, you need the URL form
# starting with file:.
#
# Note: Values may be enclosed in double quotes ("...") in case either the
#       ${catalina.base} path or the ${catalina.home} path contains a comma.
#       Because double quotes are used for quoting, the double quote character
#       may not appear in a path.
shared.loader=

Notice how the default value for that shared.loader property is blank. As the comments explain, Tomcat will fallback to using its “common loader” which loads JARs from a lib folder in the Catalina Home holder as well as a lib folder in the Catalina Base folder (which some folks define as being a folder outside of the Tomcat folder, for ease of administration).

Create and specify your own folder of JARs

You are free to designate any folder to hold your JAR files to be accessed by Tomcat via its “shared loader” (provided your system's user account running Tomcat has file system privileges to that folder). AFAIK, creating a nested shared/lib folder is just convention.

Inside the Tomcat folder

If you want to use the Tomcat folder for something like $CATALINA_HOME/shared/lib:

  1. Create the pair of shared & lib folders (with proper file system privileges).
  2. Edit the $CATALINA_HOME/conf/catalina.properties to replace shared.loader= with:
    shared.loader="${catalina.home}/shared/lib","${catalina.home}/shared/lib/*.jar"

Notice how we used double-quotes as instructed by the comments quoted above. And we specified with just lib for class files as well as lib/*.jar for JAR files.

Outside the Tomcat folder

If you are one of those people who elect to keep your web-apps in a folder outside of the Tomcat folder, you will have defined catalina.base to be that outside folder. In such a case, you will likely want to keep your shared JAR files there too, rather than inside the Tomcat folder. So you may want the create your shared/lib there. Following the example in those quoted comments:

  1. Create the pair of shared & lib folders (with proper file system privileges) in your outside folder.
  2. Edit the $CATALINA_HOME/conf/catalina.properties to replace shared.loader= with:
    shared.loader="${catalina.base}/shared/lib","${catalina.base}/shared/lib/*.jar"

Notice how we used catalina.base rather than catalina.home.

Both inside and outside Tomcat folder.

You could specify looking at /shared/lib folders both inside your Tomcat folder ("home") and also outside your Tomcat folder ("base").

Use both .base & .home:

shared.loader="${catalina.base}/shared/lib","${catalina.base}/shared/lib/*.jar","${catalina.home}/shared/lib","${catalina.home}/shared/lib/*.jar"

If you are using Maven to drive your Java project, you will want to edit your POM file to set a dependency for your particular JDBC driver. In that <dependency> element, you will want to set the <scope> to the value provided to avoid bundling a copy of your JDBC driver within your web-app’s WAR file. See this Answer.


This old Tomcat wiki page mentions briefly this same technique of creating your shared/lib folders, then editing catalina.properties file to define a value for property named shared.loader.

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154