views:

58

answers:

3

I'm trying to have my application load a resource (binary file) transparently:

If the file exists under the current directory, open it.
If not, try looking in the current JAR file if applicable.
If not, try looking in other JAR files. (This is optional and I don't mind explicitly specifying which JAR files.)

So far I know of File which opens a local file and ClassLoader which has getResource* for JAR contents.

Is there a class which combines the two? If not, how should I go about writing it myself? Should I write a ClassLoader which also checks the local filesystem? Using File? (I'm very unfamiliar with Java and don't even know what's a good type to return. InputStream?)

Thanks

P.S. By "file" I mean "path", e.g. "data/texture1.png".

+2  A: 

A URLClassLoader should be able to load both and try the file path first if the file path is on the class path ahead of the jar.


Regarding your comments:

seanizer
Ah, great. This might be exactly what I'm looking for.
aib
And indeed it is! One question, though: Why can't I seem to use `new URL("file:foo.jar!/data")`? I cannot seem to use `!/` at all!
aib
Err and one more thing, it seems to prefer the file in the JAR regardless of what order I give the paths in.
aib
see my updated answer
seanizer
Thanks for the pointers. I can work around the JAR-relative path problem, but what about the JAR being scanned first? I cannot get to the file on the filesystem unless I delete the one inside the JAR. It seems like the URL search order is to always search the JAR first, regardless of whether it was the first or the second element of the URL array I passed to the constructor.
aib
I'd say skip the explicit classloader call and just configure tha jar manifest as mentioned in the other question
seanizer
I think Java's own classes are too specialized for this. I guess I'll make my own abstraction.
aib
...or not, after reading the other question another time. Hmm, I'll need to try both.
aib
+1  A: 

Doing #1 and #3 is pretty easy. Doing #2 (just looking in the current JAR only) is much harder as it requires you figuring out what JAR you

If you wanted to check the filesystem first, otherwise load from classpath, it would be something like:

public java.io.InputStream loadByName(String name) {
  java.io.File f = new java.io.File(name);
  if (f.isFile()) {
    return new FileInputStream(f);
  } else {
    return getClass().getResource(name);
  }
}

If you want to prefer loading from the same JAR file first, you will need to figure out where it is. Check out http://stackoverflow.com/questions/1983839/determine-which-jar-file-a-class-is-from for more info on figuring out the JAR file you want to load the resource from.

Rob Di Marco
I was afraid I might need a function like that :( - Thanks for the pointer to the other question btw.
aib
+1  A: 

Current jar file and current directory are not concepts in the JVM like they are when you're running a shell script. You would need to specify a directory to be used for loading the files that you're interested in, such as with a system property while executing the JVM:

java -Ddirectory.to.scan=/home/aib

Then retrieve this property:

String dir = System.getProperty("directory.to.scan");

Now when talking about JAR files, all JAR files specified explicitly on the classpath when you start the JVM are loaded by the ClassLoader. You can get the ClassLoader of a specific class by:

InputStream is = <Your class>.class.getClassLoader().getResourceAsStream("binary file");

Note that any jar file loaded by the current class loader is searched.

purecharger