Bug 41664 - ClassLoader does not return directory resources correctly
Summary: ClassLoader does not return directory resources correctly
Status: RESOLVED INVALID
Alias: None
Product: Tomcat 5
Classification: Unclassified
Component: Catalina (show other bugs)
Version: 5.5.20
Hardware: All All
: P2 normal with 1 vote (vote)
Target Milestone: ---
Assignee: Tomcat Developers Mailing List
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2007-02-20 15:54 UTC by Howard Lewis Ship
Modified: 2009-03-21 08:44 UTC (History)
0 users



Attachments
Sample code that relies on the missing functionality (6.99 KB, text/plain)
2007-02-20 15:55 UTC, Howard Lewis Ship
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Howard Lewis Ship 2007-02-20 15:54:11 UTC
It is extremely useful to be able to query the class path for the existence of
files.  ClassLoader includes a method, getResources(), for this purpose. It
returns an enumeration (it's an old API) of the URLs of all the matching resources.

In terms of Sun's ClassLoaders, including URLClassLoader, if you request a path,
and not a file, you get the contents of folder if it exists.  A typical path
might be "org/example/myapp/pages/", in order to search for resources, including
class files, within the org.example.myapp.pages package.

For a file URL, this is a sequence lines that identify the immediate contents of
the folder.

For a jar URL, you can access the java.util.jar.JarFile via the URL's connection
(it will be a JarURLConnection).

Unfortunately, the Catalina ClassLoader does not follow this example, and
requesting the contents of a directory resource returns an empty enumeration.

In addition, because of how Catalina partially unpacks WARs for deployment, the
classes are not visible via ServletContext.getResourcesPaths() either, since the
exploded WAR doesn't include a WEB-INF/classes folder.

The end result is that there's no way to scan the classpath in the way that
certain frameworks (in my case, Tapestry 5, but I've seen indications that
Facelets has the same issue) need to.

By comparison, Jetty (4, 5, 6) does provide the necessary access.
Comment 1 Howard Lewis Ship 2007-02-20 15:55:55 UTC
Created attachment 19616 [details]
Sample code that relies on the missing functionality
Comment 2 Howard Lewis Ship 2007-02-20 16:53:05 UTC
As a stop-gap, making sure that packaged classes are visible via
ServletContext.getResourcePaths() would be sufficient for my needs.
Comment 3 Yoav Shapira 2007-02-20 19:24:52 UTC
This (the getResources returning a complete enumeration) seems like a reasonable
request.  I don't have the bandwidth right now to delve into it and create a
patch, but if you care to provide one I'll try to evaluate and commit it
quickly.  You'll probably want to look at
org.apache.catalina.loader.WebappClassloader#findResources,
http://svn.apache.org/repos/asf/tomcat/container/tc5.5.x/catalina/src/share/org/apache/catalina/loader/WebappClassLoader.java,
to start with.

The getResources method itself in that class is not overridden, using the
java.net.URLClassloader default.

One other thing to consider is behavior when running inside a security manager.
 WebappClassloader already has a privileged findResource section, which is good.
findResources should also probably check for the same permissions / run in a
similar privileged action.

I'm not sure what you mean with respect to "partially" unpacking WAR files.  If
you specify that the WAR should be unpacked, it's fully unpacked.

Finally, if it's really urgent for you and you don't want to wait for one of us
to review a patch and/or a Tomcat release that includes the patch, you can
easily provide a Tapestry 5-suitable classloader by specifying the Loader
element of the Context to be your own class name, as documented at
http://tomcat.apache.org/tomcat-5.5-doc/config/loader.html
Comment 4 Howard Lewis Ship 2007-02-20 21:23:50 UTC
For the short term, I'll be focussing on a work around, as a build time
enhancement (a Maven plugin). That's necessary either way, to address the
installed base (including earlier versions of Tomcat, and likely other servlet
containers as well). I would still like to see this fixed, as it would then not
be necessary to have this extra step.  As it is, I'm going to piss off some
people who'll want an Ant task as well as a Maven plugin.
Comment 5 Remy Maucherat 2007-02-21 05:17:38 UTC
Personally, I don't find the use case convincing at this point, and I don't
understand the explanation of the problem.

The only documentation about the getResources method is the javadocs:
     Finds all the resources with the given name. A resource is some data
     (images, audio, text, etc) that can be accessed by class code in a way
     that is independent of the location of the code.
And in URLClassLoader:
     Returns an Enumeration of URLs representing all of the resources
     on the URL search path having the specified name.

As I stated earlier, I do not understand how it is assumed that this is supposed
to do a wildcard match (it would be written cleanly as
getResources("org/example/myapp/pages/*")). The wording seems to imply the
opposite, actually, and I wonder if the behavior was as intended or not.

If you have a reasonably simple patch which implements this, I would consider
it, otherwise it is not going to be addressed (and I think the bug should be
marked as WONTFIX).
Comment 6 Jacob Hookom 2007-02-21 08:01:40 UTC
Comments below:

(In reply to comment #5)
> Personally, I don't find the use case convincing at this point, and I don't
> understand the explanation of the problem.
> 
> The only documentation about the getResources method is the javadocs:
>      Finds all the resources with the given name. A resource is some data
>      (images, audio, text, etc) that can be accessed by class code in a way
>      that is independent of the location of the code.

A Directory isa File, and you find those don't you?

> 
> As I stated earlier, I do not understand how it is assumed that this is supposed
> to do a wildcard match (it would be written cleanly as
> getResources("org/example/myapp/pages/*")). The wording seems to imply the
> opposite, actually, and I wonder if the behavior was as intended or not.


There's no wildcarding going on-- it's just:

ClassLoader.findResources("/META-INF");

With the expectation that it will return an Enumeration of URLs to all META-INF
directories on the classpath.

I really need to take a look and see what Seam did to get around this with their
component loading by classpath searching.

> 
> If you have a reasonably simple patch which implements this, I would consider
> it, otherwise it is not going to be addressed (and I think the bug should be
> marked as WONTFIX).
> 

Even with the realization that there's no wildcarding going on?  just the
expectation that a directory is a valid Unified Resource *Location*?  Oracle's
AS has the same issue, but has since made the fix.
Comment 7 Remy Maucherat 2007-02-21 14:08:01 UTC
(In reply to comment #6)
> ClassLoader.findResources("/META-INF");
> 
> With the expectation that it will return an Enumeration of URLs to all META-INF
> directories on the classpath.

The current implementation does that, feel free to look at the code, it's very
explicit (WebappClassLoader.findResources).

This is not what this case requests: "if you request a path, and not a file, you
get the contents of folder if it exists" (the contents of a folder, AFAIK, is
the enumeration of the files which are in the folder).
Comment 8 Jacob Hookom 2007-02-21 14:48:50 UTC
talked more with Remy-- things work as expected-- it should just return pointers
(URL) to the directory, then it's up to the client code to walk the directories
based on if the connections returned are JarUrlConnections or should be treated
as physical file locations.

Example here:

https://facelets.dev.java.net/source/browse/facelets/src/java/com/sun/facelets/util/Classpath.java?rev=1.5&view=markup
Comment 9 Mark Thomas 2009-03-21 08:44:58 UTC
Closing based on comment 8.