Bug 54741

Summary: Add org.apache.catalina.startup.Tomcat#addWebapp(String, URL) method
Product: Tomcat 8 Reporter: Nick Williams <nicholas>
Component: CatalinaAssignee: Tomcat Developers Mailing List <dev>
Status: RESOLVED FIXED    
Severity: enhancement    
Priority: P2    
Version: 8.0.x-trunk   
Target Milestone: ----   
Hardware: All   
OS: All   

Description Nick Williams 2013-03-22 00:37:38 UTC
Currently Tomcat can only deploy from a WAR file or directory that exists on the real file system. It would be nice, in an embedded Tomcat (one JAR) situation, to be able deploy an application using a JAR file resource without having to first copy that resource to the local file system.

URL war = this.getClass().getResource("/MyApp.war")
Tomcat tomcat = new Tomcat();
...
tomcat.addWebapp("", war);

I tried doing this with the actual URL string, but that did not work:

URL war = this.getClass().getResource("/MyApp.war")
Tomcat tomcat = new Tomcat();
...
tomcat.addWebapp("", war.toString());

SEVERE: Exception fixing docBase for context []
java.io.FileNotFoundException: C:\Users\Nicholas\Desktop\Project\target\.extract\webapps\jar:file:\C:\Users\Nicholas\Desktop\Project\target\MyApp-1.0.0-SNAPSHOT.jar (The filename, directory name, or volume label syntax is incorrect)

I had to do this instead:

URL war = this.getClass().getResource("/MyApp.war")
File warFile = new File(extractDirectory, "MyApp.war");
FileUtils.copyURLToFile(war, warFile);
Tomcat tomcat = new Tomcat();
...
tomcat.addWebapp("", warFile.getAbsolutePath());

Of course, that's ignoring all the error-checking logic. It's just a nuisance to have to do this. Surely, since Tomcat unzips the WAR file anyway, it could do it on a URL resource instead of having to copy the file to the local filesystem.
Comment 1 Christopher Schultz 2013-03-27 19:54:31 UTC
FWIW, Tomcat can be configured to NOT expand WAR files, so you can't rely on a filesystem at all.

As previously discussed, StandardContext.setDocBase says it can take "an absolute pathname, a relative pathname, or a URL.". I believe you stated that trying to use a URL that points to a WAR file within a JAR file does not work.
Comment 2 Mark Thomas 2018-12-03 15:42:53 UTC
The JAR URL format does not support the concept of nested archives. This means that Tomcat has already had to implement a special URL handling scheme to support referencing a resource inside a JAR inside a WAR.

The special WAR URL handling went through several iterations as we worked to avoid various conflicts until we ended up with the current scheme.

It would not be possible to run a WAR from within a JAR file without completely replacing the JREs jar URL handling (and adding support for nesting) so URLs of the form "jar:war:jar:..." were supported.  Based on past experience with "war:...", that approach would be extremely fragile and very likely to conflict with other tools.

To be fair, I don't think this enhancement request is asking for the above. However, I wanted to set out the difficulties of a generic "deploy a WAR from within a JAR" feature.

The specific request to deploy a WAR in unpacked mode from a JAR should be less complicated. I'll take a look. Assuming deployment in unpacked mode is possible, an attempt to deploy a WAR from a JAR in packed mode will result in an error.
Comment 3 Mark Thomas 2018-12-19 16:51:09 UTC
After looking at this for a while, I opted to removed the URL reference in the getter/setter for docBase and added a new method that copies a WAR from the URL to the appBase on Tomcat start.

Fixed in:
- trunk for 9.0.15 onwards
- 8.5.x for 8.5.38 onwards
- 7.0.x for 7.0.93 onwards