Hi, 2016-05-03 18:20 GMT+03:00 Violeta Georgieva <miles...@gmail.com>: > > > > 2016-05-03 17:56 GMT+03:00 Konstantin Kolinko <knst.koli...@gmail.com>: > > > > 2016-05-03 17:07 GMT+03:00 Violeta Georgieva <miles...@gmail.com>: > > > Hi, > > > > > > 2016-04-27 14:27 GMT+03:00 Konstantin Kolinko <knst.koli...@gmail.com >: > > >> > > >> 2016-04-27 14:00 GMT+03:00 Violeta Georgieva <miles...@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-unsubscr...@tomcat.apache.org > > For additional commands, e-mail: dev-h...@tomcat.apache.org > >