2

I'm not sure if this is the right place to ask this, but I'll go ahead anyway! I am trying to deploy a JAR that I built with maven into the Tomcat/lib folder. The problem I'm having is that all the classpath JARs for the JAR that I built, aren't getting read. So I am having to explicitly add those JARs to Tomcat's lib folder as well. Is this the expected behavior for Tomcat? Or am I doing something wrong with the Maven build/plugins? My JAR and classpath plugins are done like:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-jar-plugin</artifactId>
    <inherited>true</inherited>
    <configuration>
        <archive>
            <manifest>
                <addClasspath>true</addClasspath>
                <addDefaultEntries>true</addDefaultEntries>
                <addBuildEnvironmentEntries>true</addBuildEnvironmentEntries>
                <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
                <addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
                <classpathPrefix>lib/</classpathPrefix>
            </manifest>
        </archive>
        <attachClasses>true</attachClasses>
        <includeEmptyDirectories>true</includeEmptyDirectories>
        <outputDirectory>${project.basedir}\target</outputDirectory>
    </configuration>
</plugin>

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-dependency-plugin</artifactId>
    <inherited>false</inherited>
    <executions>
        <execution>
            <id>pal-copy-dependencies</id>
            <phase>generate-sources</phase>
            <goals>
                <goal>copy-dependencies</goal>
            </goals>
            <inherited>false</inherited>
            <configuration>
                <excludeScope>provided</excludeScope>
                <excludeClassifiers>shared</excludeClassifiers>
                <excludeTransitive>true</excludeTransitive>
                <outputDirectory>${project.build.directory}/classes/lib</outputDirectory>
                <overWriteIfNewer>true</overWriteIfNewer>
                <silent>true</silent>
                <useBaseVersion>false</useBaseVersion>
            </configuration>
        </execution>
    </executions>
</plugin>

For example, if I have the dependency:

<dependency>
    <groupId>org.jasypt</groupId>
    <artifactId>jasypt</artifactId>
    <version>1.9.3</version>
</dependency>

I keep getting the error:

09-Jan-2020 16:33:40.347 SEVERE [main] org.apache.catalina.core.ApplicationContext.log Servlet.init() for servlet [InitialiseDAC] threw exception
java.lang.NoClassDefFoundError: org/jasypt/util/text/AES256TextEncryptor
    at com.provisioning.common.crypto.Decrypt.doDecrypt(Decrypt.java:192)

It only works when I make the dependency like:

<dependency>
    <groupId>org.jasypt</groupId>
    <artifactId>jasypt</artifactId>
    <version>1.9.3</version>
    <scope>provided</scope>
</dependency>

And put the jasypt.jar in the Tomcat's lib folder.

Is this normal for Tomcat?

2 Answers 2

4

There are two reasons, why you are getting ClassNotFoundExceptions and NoClassDefFoundErrors.

A jar inside jar project

Unless I am mistaken, you are trying to use the Maven dependency plugin to copy all dependency jars into your jar file. A jar inside jar configuration will not work unless you are using a custom class loader. The only noticeable effect you are getting is a bigger jar file.

When you are building the jar project, you don't need to worry about deploying dependencies, just list them in the POM file. The hard work of collecting them should be done in the web application WAR project.

Tomcat's classpath

Tomcat has a complex classloader hierarchy, which allows every web application to use a different version of the same library. In the standard configuration (cf. catalina.properties):

  1. Classes and jars inside the ${catalina.base}/lib and ${catalina.home}/lib folders (which by default are equal to the lib folder of your Tomcat installation) are loaded by the common classloader.
  2. Classes in the WEB-INF/classes and jars in the WEB-INF/lib folder (or WAR archive) are loaded by the web application class loader.

As a rule you should not put any new library in the common class loader's classpath, since this is administered by the Tomcat server admin, not the Java developer. As an exception you may assume the lib folder contains:

  • the JDBC drivers for the SQL servers Tomcat will connect to,
  • the implementation jars for logging frameworks like jcl, slf4j or log4j2.

You should put all the dependencies in the WEB-INF/lib folder of your WAR archive. Maven's dependency plugin can help you with that.

0

@piotr-p-karwasz do you have any thoughts on https://stackoverflow.com/questions/76024606/how-to-create-a-jar-of-jars-including-only-required-3rd-party-libraries-outside ?

Specifically, for projects where 90% of code is 3rd party dependencies and rarely changes, at least compared with the application code. In sprint/agile process we often deploy multiple times a day, which means the existing war file is quite bloated with code that hasn't changed since the last deploy. I'm looking to see if anyone has experience managing the 3rd party dependencies outside the application war file and instead publishing them to ~catalina/lib, ~catalina/third-party-lib, so they are shared across all the apps on that host and reduce the war file size considerably.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .