views:

411

answers:

4

I was confronted with code which tries to read some configuration files from the same directory where the .class file for the class itself is:

File[] configFiles = new File(
    this.getClass().getResource(".").getPath()).listFiles(new FilenameFilter() {
        public boolean accept(File dir, String name) {
            return name.endsWith(".xml");
        }
});

Apparently this works in some cases (when running the code inside Resin, perhaps), but for me, running Tomcat, it simply fails with NPE, because getClass().getResource(".") returns null.

A colleague suggested creating another config file containing a list of all the ".xml" config files (which indeed would work here as it stays quite static), and that you shouldn't really try to do something like this in Java.

Still, I'm wondering if there is some nice way, which works universally, for getting the path to the directory where a given .class file is located? I guess you could get it from the path of the .class file itself like this:

new File(this.getClass().getResource("MyClass.class").getPath()).getParent()

... but is this the only / cleanest way?

Edit: To clarify, assume we know this is used in an application deployed in such a way that MyClass.class will always be read from a .class file on disk, and the resources will be there in that same directory.

+3  A: 

What makes you assume that there's a class file on disk in its own directory?

A class could be:

  • Created entirely in memory
  • Loaded from a network connection
  • Loaded from a jar file

You can get the URL used to create the class itself, and if that starts with file:// then you could get the rest... but it won't work for all classes.

Jon Skeet
Yes, I think that's pretty much what my colleague meant when proposing the alternative... But there *are* scenarios when you do know that the class will be always loaded from a file on disk - so if that is the case, is what I suggested in the question the cleanest way? Thanks for the tip about checking the start of the URL though!
Jonik
Yes, what you've got there looks about the cleanest way to me if you add in a check for the file protocol.
Jon Skeet
+1  A: 

If the resource is in the same folder as a .class file, it should be accessible via the classpath and can be loaded via getResourceAsStream directly.

this.getClass().getResourceAsStream( "filename.xml" )

As is posted earlier, classes themselves can be loaded remotely or in places where there's not a proper "path" (e.g. from a jarfile)

Dan Fleet
Yes, sure; I was looking for the simplest way of getting the *directory* path (because we're reading several resource files from there)
Jonik
Ah are you trying to read all e.g. ".xml" files from the class path at that point, without having to know their names in advance?
Dan Fleet
Yeah, that was mainly the point. (Although in this case we basically do know the names, I'd just like to read the files dynamically, to avoid listing them somewhere or some such thing.)
Jonik
+1  A: 

I agree with your colleague that Java class loading was not designed to handle this use case. Sun Facelets uses a similar strategy of assuming URLs can be mapped to Files, and it's not pretty. I agree with Jon's comment that your getResource solution is probably the cleanest given your deployment assumptions. Since you asked if it was the only way, I'll also offer getClass().getProtectionDomain().getCodeSource().getLocation(), which should be the URL that the class loader actually loaded your class from (you would need to append the subdirectories for your class' package). This strategy also has the same URL-to-File assumptions, so it's no better in that regard. I can think of no other general solutions.

Note that getResource returns an encoded URL, which means that you should not use getPath() directly. In particular, spaces will causes issues, though this might not be an issue if you have control over your environment. Consider using new File(URL.toURI()).

bkail
A: 

I think, you may be interested in PathMatchingResourcePatternResolver from the Spring Framework. You can use it directly in your code for navigating the configuration files or you can look for the implementation here.

Ivan Dubrov