• Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other Pie Elite all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Jeanne Boyarsky
  • Ron McLeod
  • Paul Clapham
  • Liutauras Vilda
Sheriffs:
  • paul wheaton
  • Rob Spoor
  • Devaka Cooray
Saloon Keepers:
  • Stephan van Hulst
  • Tim Holloway
  • Carey Brown
  • Frits Walraven
  • Tim Moores
Bartenders:
  • Mikalai Zaikin

Tomcat 8 Resource loading problem

 
Greenhorn
Posts: 6
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Hello everyone,

I have encountered a strange problem while migrating from Tomcat version 7 to 8. The problem is with resources.

(From what i understand there are no resources that need additional configuration as mentioned in the migration guide here http://tomcat.apache.org/migration-8.html so the problem should be not in the Context)

The problem led me to a source file named AbstractFileResourceSet. Please see the source code here: http://grepcode.com/file/repo1.maven.org/maven2/org.apache.tomcat/tomcat-catalina/8.0.9/org/apache/catalina/webresources/AbstractFileResourceSet.java

There is a method File file() which makes trouble.

1. You can see the a File object is created.
2. Then a file canonical path is being calculated. (lest say it's C:\MyExample.xml)
3. Then a file absolute path is being calculated (the same C:\MyExample.xml)
4. Then suddenly the absolute path is being normalized adding a leading '\' (so the absolute path is now \C:\MyExample.xml)
5. Then parts of the string are being compared and though they seem equal as Strings, they have a different offset because of the leading slash so
instead of returning the resource a null is being returned.

I can't even understand if there is a way that this method works. Can someone help me out please?

Thank you!

 
Saloon Keeper
Posts: 27763
196
Android Eclipse IDE Tomcat Server Redhat Java Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
What I don't understand is why you care. What is it doing to your webapp or server configuration that is a problem to you?

The addition of a leading slash is simply to convert the canonical path to URL format. The canonical path is the "official" absolute path of the file, in case it has alternative names (for example, because of aliases/softlinks).

URL format for absolute resource paths always begins with a slash. That's the official signal that it's an absolute path and not a relative one. So a URL in the form:

"file:///var/log/tomcat/catalina.out"

translates to /var/log/tomcat/catalina.out. I've used a Unix/Linux path as an example because Java's path "normalization" is based on Unix filesystem syntax. And in Unix, there's always a single filesystem root ("/"), unlike DOS, which can have 26 of them. The DOS/Windows normalized equivalent would be "/C:/var/log/tomcat/catalina.out". Or, in your case, "/C:/MyExample.xml". Note also that as part of the Unix convention, Java expects upper-lower case to be precise. So even though Windows considers "C:\MyExample.xml" and "C:\myexample.xml" to be the same file, Java does not.

The 2 extra slashes at the beginning of the path signal the hostname, but for a file, the hostname is implicit, which is why the file URL starts with 3 slashes. Two for host, one for absolute path. I think of of the URL convention's creators expressed regret about having done it that way, but that's what we're stuck with.
 
Ivan Skorohodov
Greenhorn
Posts: 6
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I care because the resource is not returned when it's found(it exists on the path, but as two Strings that are compared in the file method are not equal, null is returned instead of the file itself).

The resource is requested by spring's WebUtils
(see src: http://grepcode.com/file/repo1.maven.org/maven2/org.springframework/spring-web/3.0.5.RELEASE/org/springframework/web/util/WebUtils.java#WebUtils.getRealPath%28javax.servlet.ServletContext%2Cjava.lang.String%29 )

the call is - servletContext.getRealPath(path);
and as null is returned the application can't get the resource.

The problem could be in somekind of incompatibility between Tomcat 8 and spring 2.5. But the file() method still remains unclear to me.
 
Ivan Skorohodov
Greenhorn
Posts: 6
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Just wanted to point out these lines of code:


canPath = file.getCanonicalPath(); // C:/MyExample.xml
...
String fileAbsPath = file.getAbsolutePath(); // C:/MyExample.xml
...
String absPath = normalize(fileAbsPath); // /C:/MyExample.xml
...
if (!canPath.equals(absPath)) // C:/MyExmample.xml != /C:/MyExample.xml
return null;

So I can't immagine a way how this works.
 
Tim Holloway
Saloon Keeper
Posts: 27763
196
Android Eclipse IDE Tomcat Server Redhat Java Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
getRealPath is a dangerous function.

Properly speaking, a J2EE webapp must be packaged into a WAR or EAR file. In this case, getRealPath will return null, because the "files" inside a file (WAR or EAR) have no filesystem-accessible path.

In order for getRealPath to work, the WAR/EAR must be "exploded" (unzipped) into a set of actual files. In which case getRealPath will return the path of the directory that the requested resource was exploded into (servlet context path), concatenated with the relative path of the exploded file.

Tomcat historically has automatically exploded WARs unless instructed not to, but that's a setting that can easily be overridden.
 
Ivan Skorohodov
Greenhorn
Posts: 6
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
but my app is exploded.... I don't even know if it has something todo with tomcat configuration because the File object being created is perfectly normal, not null and leads to an existing file.
 
Ivan Skorohodov
Greenhorn
Posts: 6
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Resolved by changing the way i get the resources. Now I provide the full path for spring resource loader by executing getRealPath("/"), appending it with a "file:". Dont know if this is a correct way to solving this, but it was surele the less invasive way. Thanks to Tim Holloway for support!

 
Tim Holloway
Saloon Keeper
Posts: 27763
196
Android Eclipse IDE Tomcat Server Redhat Java Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Spring's resource location services are a bit different than Tomcat's. Yes, you can provide a file URL, but if the resource is in the webapp's classpath, there's also the "classpath:" option, where you would supply the classpath-relative resource path (for example, "com/javaranch/saloon/messages/DBMessages.properties"). For a webapp, "classpath:" covers the WEB-INF/classes directory, the objects in the WEB-INF/lib jars and the classes the webapp inherits from Tomcat. This is a more portable alternative to brute filesystem access.

Another option is the getResource and getResourceAsStream J2EE methods, which can locate a resource anywhere within a WAR, whether the WAR has been exploded or not. The syntax of the resource path begins with "/", which signifies the base of the WAR, so "/images/happyface.gif" would be in the WAR's "images" subdirectory.

I'm sending you a private ("purple moosage") message, BTW. Just something about the Ranch protocols.
 
Ivan Skorohodov
Greenhorn
Posts: 6
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
So, after some additional digging with my collegue, we found that some resources are working as they did before migration. The reason turned out to be pretty simple.

In tomcat 8 resource check is case sensitive.

So the fix we applied is just rename the resource names. All hail legacy code. Thanks
 
Tim Holloway
Saloon Keeper
Posts: 27763
196
Android Eclipse IDE Tomcat Server Redhat Java Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
In Java, everything is case sensitive. Not something that I'm very fond of, but that's how it is and always has been.

The problem is that in Windows, Windows filenames are NOT case sensitive. This makes it possible to code badly and not even realize what you've done. At least until you migrate to an OS with case-sensitive filenames such as Unix, Linux or OSX.
 
reply
    Bookmark Topic Watch Topic
  • New Topic