Can I create a custom classpath on a per application basis in Tomcat
Asked Answered
P

4

36

For some applications I use ZK, others Hibernate, other Apache Commons, etc.

I don't want to deploy a 75MB war file, just because it uses lots of libraries.

I don't want to add the libraries to my tomcat lib folder, or nor the classpath to it's configuration as I may have an old application using library x.1 and another application using library x.2

For this reason, it would be great to have something in the web.xml or context.xml where I say something like:

<classpath>/usr/local/tomcat/custom-libs/zk-5.0.4</classpath>

Note: The above is pseudo-code

Parvenu answered 14/6, 2011 at 15:10 Comment(0)
P
36

From Tomcat 7 there is no mention of not being able to use the VirtualWebappLoader in production. I tried it and it works like a dream. Simply add the following to META-INF/context.xml:

<?xml version="1.0" encoding="UTF-8"?>

<Context antiJARLocking="true" path="/websandbox">
    <Loader className="org.apache.catalina.loader.VirtualWebappLoader"
          virtualClasspath="/usr/.../*.jar;/usr/.../*.jar"/>
</Context>

In Netbeans, under packaging, I just untick all the packages, taking the .war size down to nothing, make sure the dependencies are in the correct folders on the server and upload. Yey! No more 100 MB WAR file.

Parvenu answered 20/6, 2011 at 11:42 Comment(1)
Since I was on unix, I was trying to be smart changing semi colons to colons and was wondering what the error was spending an hourArrear
P
25

Addition @Spider answer.

Tomcat Context hold Loader element. According to docs deployment descriptor (what in <Context> tag) can be placed in:

  • $CATALINA_BASE/conf/server.xml - bad - require server restarts in order to reread config
  • $CATALINA_BASE/conf/context.xml - bad - shared across all applications
  • $CATALINA_BASE/work/$APP.war:/META-INF/context.xml - bad - require repackaging in order to change config
  • $CATALINA_BASE/work/[enginename]/[hostname]/$APP/META-INF/context.xml - nice, but see last option!!
  • $CATALINA_BASE/webapps/$APP/META-INF/context.xml - nice, but see last option!!
  • $CATALINA_BASE/conf/[enginename]/[hostname]/$APP.xml - best - completely out of application and automatically scanned for changes!!!

Here my config which demonstrate how to use development version of project files out of $CATALINA_BASE hierarchy (note that I place this file into src/test/resources dir and intruct Maven to preprocess ${basedir} placeholders through pom.xml <filtering>true</filtering> so after build in new environment I copy it to $CATALINA_BASE/conf/Catalina/localhost/$APP.xml):

<Context docBase="${basedir}/src/main/webapp"
         reloadable="true">
    <!-- http://tomcat.apache.org/tomcat-7.0-doc/config/context.html -->
    <Resources className="org.apache.naming.resources.VirtualDirContext"
               extraResourcePaths="/WEB-INF/classes=${basedir}/target/classes,/WEB-INF/lib=${basedir}/target/${project.build.finalName}/WEB-INF/lib"/>
    <Loader className="org.apache.catalina.loader.VirtualWebappLoader"
            virtualClasspath="${basedir}/target/classes;${basedir}/target/${project.build.finalName}/WEB-INF/lib"/>
    <JarScanner scanAllDirectories="true"/>

    <!-- Use development version of JS/CSS files. -->
    <Parameter name="min" value="dev"/>
    <Environment name="app.devel.ldap" value="USER" type="java.lang.String" override="true"/>
    <Environment name="app.devel.permitAll" value="true" type="java.lang.String" override="true"/>
</Context>

UPDATE Tomcat 8 change syntax for <Resources> and <Loader> elements, corresponding part now look like:

<Resources>
    <PostResources className="org.apache.catalina.webresources.DirResourceSet"
                   webAppMount="/WEB-INF/classes" base="${basedir}/target/classes" />
    <PostResources className="org.apache.catalina.webresources.DirResourceSet"
                   webAppMount="/WEB-INF/lib" base="${basedir}/target/${project.build.finalName}/WEB-INF/lib" />
</Resources>
Purpure answered 30/9, 2014 at 17:21 Comment(11)
btw, your best solution the $APP.xml file will be deleted on subsequent deployment of the war file.Carpal
@Carpal That depends on how you deploy package. When I accidentelly undeploy package through tomcat web manager - it remove that .xml file. From that time I store file backups and even add file to VCS history for each project in test/contrib leaf.Purpure
Got a nicer solution gavenkoa will post it here later on in the day.Carpal
@Chad: Did you find a better solution?Dionedionis
@Carpal do you still remember what you did?Aposiopesis
@Dionedionis see link above.Carpal
@Carpal You way to externalize configs are over-engineered and require additional training outside of official Tomcat documentation but very smart )) Our team uses one Tomcat per app and put configuration in conf/catalina.properties ))Purpure
@Purpure Nice to hear how other developers utilise tomcat. We host about on average 20 to 30 apps per tomcat. It does take 30 minutes to start it up when it crashes although no one has a problem with it. Hopefully in the future we can move to a similar approach, but will keep your technique in mind.Carpal
Awesome! This doesn't just reduce war size (and bandwidth required during deployment time etc) but if you are clever and share library jars of the same version across different app contexts then you can massively reduce the total amount of memory used by tomcat - presuming tomcat reuses existing classes already loaded in memory for the same jar versions. That, in turn, has massive performance benefits due to better use of CPUs L1, L2 cache.Kenney
@Carpal Thanks for sharing! I have forked your project and modified for Tomcat 9 github.com/zepernick/tomcat-classloaderPrecipitous
@PaulZepernick Thanks that's great to hear!Carpal
B
8

Another a bit hacky alternative.

You can write a 5-6 line custom class loader which derives from urlclassloader, and simply adds your classpath jars using addUrl() method.

Then set it as the context class loader of the thread in your application code.

Thread.setContextClassLoader(new CustomClassloader(path, parentClassLoader)

where parent class loader typically is

Thread.getContextClassloader()
Betteann answered 14/6, 2011 at 16:10 Comment(0)
T
7

This is what the META-INF/context.xml file can be used for. You defined your own WebappLoader, which loads classes for your particular webapp. This is the reference I used: http://tomcat.apache.org/tomcat-5.5-doc/config/loader.html (Edit: for Tomcat 6: http://tomcat.apache.org/tomcat-6.0-doc/config/loader.html, for Tomcat 7: http://tomcat.apache.org/tomcat-7.0-doc/config/loader.html)

Also this fellow here seems to post a solution to your exact problem (example included): http://java.dzone.com/articles/extending-tomcat-webapploader

Talithatalk answered 14/6, 2011 at 15:17 Comment(1)
No need for to make own WebAppLoader as Tomcat 7 come with VirtualWebappLoader which provide desired feature through virtualClasspath property.Purpure

© 2022 - 2024 — McMap. All rights reserved.