tomcat-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Violeta Georgieva <miles...@gmail.com>
Subject Re: Difference in the behaviour of WebappClassLoaderBase.getResource for jar resources
Date Fri, 06 May 2016 08:37:54 GMT
Hi,

2016-05-03 18:20 GMT+03:00 Violeta Georgieva <milesg78@gmail.com>:
>
>
>
> 2016-05-03 17:56 GMT+03:00 Konstantin Kolinko <knst.kolinko@gmail.com>:
> >
> > 2016-05-03 17:07 GMT+03:00 Violeta Georgieva <milesg78@gmail.com>:
> > > Hi,
> > >
> > > 2016-04-27 14:27 GMT+03:00 Konstantin Kolinko <knst.kolinko@gmail.com
>:
> > >>
> > >> 2016-04-27 14:00 GMT+03:00 Violeta Georgieva <milesg78@gmail.com>:
> > >> > Hi,
> > >> >
> > >> > I have a question about difference in the behaviour of
> > >> >
org.apache.catalina.loader.WebappClassLoaderBase.getResource(String).
> > >> > I'm investigating the issue reported here [1].
> > >> >
> > >> > In Tomcat 8+ when WebappClassLoaderBase.getResource is invoked
with a
> > > path
> > >> > that represents a jar resource and starts with a slash
> > >> > (classpath:/schema/shibboleth-2.0-services.xsd) this resource will
be
> > >> > served.
> > >> > In
> > >> >
> > >
org.apache.catalina.webresources.AbstractArchiveResourceSet.getResource(String)
> > >> > it is clearly stated that when the path starts with a slash then
this
> > >> > leading slash will be removed.
> > >> >
> > >> > In Tomcat 7 WebappClassLoaderBase.getResource, such resource will
not be
> > >> > found. If I remove the leading slash everything is OK.
> > >> >
> > >> > Is that difference intentional or I can apply a change for
removing the
> > >> > leading slash in Tomcat 7 WebappClassLoaderBase?
> > >> >
> > >> > Thanks a lot,
> > >> > Violeta
> > >> >
> > >> > [1] http://marc.info/?t=146170035100001&r=1&w=2
> > >>
> > >>
> > >> 1.) webresources API is a one thing. It should perform consistently
> > >> across all webresource implementation.
> > >>
> > >> (IIRC in Tomcat 7 JNDI resources API  was too forgiving of leading
> > >> slashes, leading to some inconsistencies such as duplicate entries in
> > >> resource cache.  I think that that has already been fixed in TC7 by
> > >> doing normalization before looking up into file system & caching. 
A
> > >> discussion was ~2 years ago)
> > >>
> > >> 2.) webresources API is also used by Servet API calls. Here we have
> > >> org.apache.catalina.core.ApplicationContext
GET_RESOURCE_REQUIRE_SLASH
> > >>
> > >>
> > >
http://tomcat.apache.org/tomcat-7.0-doc/config/systemprops.html#Specification
> > >>
> > >
http://tomcat.apache.org/tomcat-8.0-doc/config/systemprops.html#Specification
> > >>
> > >> 3.) WebappClassLoader.getResource() shall be consistent with Java API
> > >> of java.lang.ClassLoader.getResource()
> > >>
> > >>
> > >
http://docs.oracle.com/javase/8/docs/api/java/lang/ClassLoader.html#getResource-java.lang.String-
> > >>
> > >> It just says:
> > >> [q]
> > >> The name of a resource is a '/'-separated path name that identifies
> > >> the resource.
> > >> [/q]
> > >>
> > >> Unfortunately, this is unclear about leading slashes. (Maybe some
> > >> experimenting with classloaders provided by JRE,  or reading the Java
> > >> Lang Spec clarifies this.)  This likely was also discussed 2-3 years
> > >> ago.
> > >>
> > >> 4. Beware of similar-named method Class.getResource()
> > >>
> > >>
> > >
http://docs.oracle.com/javase/7/docs/api/java/lang/Class.html#getResource%28java.lang.String%29
> > >>
> > >> This method differentiates names starting with '/' or without it as
> > >> absolute vs relative resource names.
> > >>
> > >> [q]
> > >> If the name begins with a '/' ('\u002f'), then the absolute name of
> > >> the resource is the portion of the name following the '/'.
> > >> [/q]
> > >>
> > >> I think that the above fragment hints that the resource name in
> > >> ClassLoader,getResource() should not start with '/'.
> > >>
> > >>
> > >> I think that changing WebappClassLoader.getResource() to accept
> > >> resource names starting with '/' might be wrong.
> > >>
> > >
> > > Thanks for the input. I'll take it into account.
> > >
> > > However the problem is not a generic one but related to resources
placed
> > > into jar files.
> > > With the current implementation:
> > >
> > >
org.apache.catalina.loader.WebappClassLoaderBase.findResourceInternal(String,
> > > String, boolean)
> > >
> > > row 3309         synchronized (jarFiles) {
> > > row 3310
> > > row 3311            try {
> > > row 3312                if (!openJARs()) {
> > > row 3313                    return null;
> > > row 3314                }
> > > row 3315                for (i = 0; (entry == null) && (i <
> > > jarFilesLength); i++) {
> > > row 3316
> > > row 3317                    jarEntry = jarFiles[i].getJarEntry(path);
> > > .........
> > >
> > > The entry will never be found if it starts with "/" because of the
> > > implementation of java.util.jar.JarFile.getJarEntry(String)
> >
> >
> > Simple test:
> >
> > [[[
> > public class Sample {
> >   public static void main(String[] args) {
> >
> >     ClassLoader cld = System.class.getClassLoader();
> >
> >     if (cld == null) {
> >       System.out.println("Using System classloader");
> >       cld = ClassLoader.getSystemClassLoader();
> >     }
>
> And here is the difference between ClassLoader and URLClassLoader
> If you construct URLClassLoader with rt.jar then both statements below
will return the needed resource.


Let me summarize the findings till now.

According to [1]

1) when Class.getResource(name)
"The resource name given to a Class method may have an initial starting "/"
that identifies it as an "absolute" name. Resource names that do not start
with a "/" are "relative".
Absolute names are stripped of their starting "/" and are passed, without
any further modification, to the appropriate ClassLoader method to locate
the resource.
Relative names are modified according to the convention described
previously and then are passed to a ClassLoader method."

So when "name" is absolute the leading "/" slash will be removed and the
call will be delegated to ClassLoader.getResource(name)

2) when ClassLoader.getResource(name)
"The methods in ClassLoader use the given String as the name of the
resource without applying any absolute/relative transformation (see the
methods in Class). The name should not have a leading "/"."

As we saw above (Konstantin's example) call with
- name without leading "/" is successful
- name with leading "/" returns null

According to [2]
3) when URLClassLoader.getResource(name)
"This class loader is used to load classes and resources from a search path
of URLs referring to both JAR files and directories.
findResource(name)
Finds the resource with the specified name on the URL search path."

Now from the source code [3]
URLClassLoader.findResource delegates the call to the
sun.misc.URLClassPath.findResource
For resource in a jar file - URLClassPath.Loader is used. It just creates a
new url based on the jar file and the provided name.
When creating the URL - sun.net.www.protocol.jar.Handler (parseContextSpec)
removes the leading "/" and the URL to the resource is created successfully.

So if we construct URLClassLoader and call with:
- name without leading "/" is successful
- name with leading "/" is again successful

======
Now to the issue that the user reports. The
org.apache.naming.resources.ClasspathURLStreamHandler is implemented in a
way that
- when searching the web app loader the implementation uses
ClassLoader.getResource
- but when searches the Tomcat class loader it uses Class.getResource

Because of this when the jar files are packed in the application and the
resource has leading "/" it cannot be found, but when moving the jar files
to the Tomcat lib the resource will be found.

As WebappClassLoader extends URLClassLoader
We have to decide whether WebappClassLoader.getResource() should be
consistent with Java API of java.lang.ClassLoader.getResource() or
URLClassLoader.getResource() behavior.
A quick check to other web containers showed that they behave as Tomcat 8.

I tend to implement Tomcat 7 behaviour as Tomcat 8 and support resource
names with leading "/".
But as this behaviour is not strictly specified I prefer to get more
opinions.

With the introduction of ClasspathURLStreamHandler (7.0.66) we break user's
scenario that was working previously.

[1]
https://docs.oracle.com/javase/8/docs/technotes/guides/lang/resources.html
[2] https://docs.oracle.com/javase/8/docs/api/java/net/URLClassLoader.html
[3] http://hg.openjdk.java.net/jdk8/jdk8/jdk/


> >     testGetResource(cld, "java/lang/System.class");
> >     testGetResource(cld, "/java/lang/System.class");
> >   }
> >
> >   private static void testGetResource(ClassLoader cld, String
resourceName) {
> >     System.out.println("getResource(" + resourceName + "): " +
> > cld.getResource(resourceName));
> >   }
> > }
> > ]]]
> >
> > Running the class with JDK 8u92 produces the following: (I replaced
> > JDK path in the message)
> >
> > [[[
> > Using System classloader
> > getResource(java/lang/System.class): jar:file:/<JDK HOME
> > (jdk1.8.0_92)>/jre/lib/rt.jar!
> > /java/lang/System.class
> > getResource(/java/lang/System.class): null
> > ]]]
> >
> > Thus the expected behaviour is that calling ClassLoader,getResource()
> > with a path that starts with "/" results in non finding the resource
> > (the method returning "null").
> >
> > Best regards,
> > Konstantin Kolinko
> >
> > ---------------------------------------------------------------------
> > To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
> > For additional commands, e-mail: dev-help@tomcat.apache.org
> >

Mime
  • Unnamed multipart/alternative (inline, None, 0 bytes)
View raw message