Thursday, July 31, 2014

Tips on Migrating to Tomat 8 - Resources

One of the major changes from Tomcat 7 to Tomcat 8 was a refactoring with how Tomcat handles web application resources.  With Tomcat 7, there are features like aliases, VirtualLoader and VirtualDirContext that provide admins with a way to pull external resources into an app.  Unfortunately, each one of these features was implemented separately so to consolidate things and make them easier to maintain in Tomcat 8, all of these different implementations were combined into one resources framework which now handles all of that functionality.

Because of the consolidation and refactoring, several attributes from the Context configuration were removed and replaced with the Resources configuration.  This migration tip discusses how to switch from using common variations of the old Tomcat 7 configuration to the new Tomcat 8 Resources configuration.

Aliases

In Tomcat 7, the Context configuration tag has an attribute called aliases which can be used to provide a list of external locations from which Tomcat will load resources for the context.  There were a few reasons to use this, but one common reason was to use it to include static resources or configuration that an administrator wanted to place outside of a WAR file.

Here's an example configuration using aliases.
<Context aliases="/images=/var/www/images" />

Here's an example of the same configuration using Tomcat 8's Resources.
<Context>
  <Resources>
    <PostResources className="org.apache.catalina.webresources.DirResourceSet"
                   base="/var/www/images" webAppMount="/images" />
  </Resources>
</Context>


VirtualDirContext

The VirtualDirContext feature is similar to aliases but was targeted as a development feature.  The suggestion for using it is so that you do not have to deploy an entire WAR file.  Instead, you can point to the location of your static resources in your project and only copy, zip and deploy the code itself.

Here is an example of how this was used under Tomcat 7.  In this case, I'm using it to point to picture and movie assets that need to be included with the application.
<Context>
    <Resources className="org.apache.naming.resources.VirtualDirContext"
               extraResourcePaths="/pictures=/Users/theuser/mypictures,/movies=/Users/theuser/mymovies" />
</Context>
The same configuration under Tomcat 8 would look like this.
<Context>
  <Resources>
    <PostResources className="org.apache.catalina.webresources.DirResourceSet"
                   base="/Users/theuser/mypictures" webAppMount="/pictures" />
    <PostResources className="org.apache.catalina.webresources.DirResourceSet"
                   base="/Users/theuser/mymovies" webAppMount="/movies" />
  </Resources>
</Context>

It's also possible to use a PreResources tag, instead of the PostResources.  The difference would be that resources listed with a PostResources tag are checked after resources bundled in the web applications and resources with the PreResources tag are checked prior to resources bundled in the web application.  In other words, PreResources can be used to override a resource that is included with an application.

VirtualWebappLoader

The VirtualWebappLoader feature is used to add additional directories or JAR files onto the class path.  This is different from the previous two options because it allows you to add JAR files, classes or configuration to the class path and not just the file system.  Again, there are a few reasons why you might want to do this, one common reason was so that you could pull JAR files out from the WAR file to a centralized location.

Here's an example of how this was configured with Tomcat 7.
<Context>
  <Loader className="org.apache.catalina.loader.VirtualWebappLoader"
          virtualClasspath="/apps/shared/lib/*.jar,/apps/shared/classes" />
</Context>
Here's an example of how this is configured with Tomcat 8.
<Context>
  <Resources>
    <PostResources className="org.apache.catalina.webresources.DirResourceSet"
                   base="/apps/shared/lib" webAppMount="/WEB-INF/lib" />
    <PostResources className="org.apache.catalina.webresources.DirResourceSet"
                   base="/apps/shared/classes" webAppMount="/WEB-INF/classes" />
  </Resources>
</Context>
If you were using the searchExternalFirst attribute of the VirtualWebappLoader class, switching from using a PostResource tag to a PreResource tag should allow you to achieve the same behavior.

Additional Information

That covers some of the common configuration scenarios that have been changed due to the new Resources API.  For more information on this change, I would suggest the following resources.

Feel free to drop me a comment if I missed anything.

6 comments:

Ed Rouse said...

Nice explanation but it leaves out how to accomplish this dynamically. I had a custom class loader that would read all of the subdirs in an extensions directory and add all of the jars, class files, jsp pages, css, etc... This esentially allowed us to create a base war file and use external war files (expanded and read as Files) to customize it for specific needs. We used the FileDirContext for this. I have been looking for information on doing this with the DirResourceSet. There may be a better way, but so far the information is slim at best.

Can you shed any light on using the new Resources framework to dynamically add external war file files to the existing war file structure? Thanks.

Dan's Blog said...

The situation you're describing sounds very close to "overlays", which is what the new resource api was designed to accommodate.

I don't think you're going to get anything "dynamic" with the current resources implementation though. It expects you to set XML to configure individual resources. Let say you have your "extensions" directory and 10 sub directories then you'd need to create 10 DirResourceSet's to include all of those.

Off the top of my head, my suggestion would be to write a custom ResourceSet (i.e. implement org.apache.catalina.WebResourceSet). You could make a directory of directories ResourceSet. It would just take the original directory, find the sub directories and create a DirResourceSet for each one.

drumsticks said...

Is there a way of allowing a directory outside of the webapps folder altogether? I noticed you are using a relative path, but I need to use a fixed path.

For example, I was able to do so in Tomcat 7 with the following configuration within server.xml successfully, but I cannot find a way to do this in Tomcat 8.



Would you possibly have any ideas on how to get this to operate?

Thank you for your time.

Thusitha said...

Is itr possible to map more than a one directory to /WEB-INF/lib?
e.g.


Dan's Blog said...

>Is there a way of allowing a directory outside of the webapps folder altogether?

I'm not sure I follow you here and it looks like blogger ate your config example. If you're still looking for a solution, post your Tomcat 7 config sample on the tomcat-users mailing list. Myself or someone else on the list can help you get this figured out.

>Is itr possible to map more than a one directory to /WEB-INF/lib?

It would probably help to have more context about what you're trying to do here. I'd like to think theres a way to make this happen, but it probably depends on your setup and why you need to do this. Again, if you're still looking for an answer I'd suggest posting to the tomcat-users mailing list. With more detail, myself or someone else on the list can help you get this figured out.

Dan

whatsapp dp said...

Thanks for the inspiration .