How should I use getResource() in Java? - java

This question is asked in numerous places, with myriad small variations. (Such as Java - getClassLoader().getResource() driving me bonkers among others.) I still can't make it work.
Here's a code snippet:
String clipName = "Chook.wav";
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
// URL url = classLoader.getResource(clipName);
URL url = new URL("file:///Users/chap/Documents/workspace/the1620/bin/ibm1620/" + clipName);
ais = AudioSystem.getAudioInputStream(url);
This works -- note that I've hard-coded the path to the directory containing the clip file, which is there, and is in the same directory as my .class file. Alas, the commented-out code just returns a null value for url.
Most other posts seem to deal with getResourceAsStream(). I think I'm supposed to be using getResource(). Is that making the difference?
It just can't be this hard. Any clues?

String clipName = "Chook.wav";
When using getResource, the string you pass in must be either an absolute name or be valid relative to a certain class. Since you're using ClassLoader.getResource() and not Class.getResource(), it must be an absolute path.
Without seeing your actual file hierarchy, I can only guess that "bin" is the root of your compiled classes and resources, and "ibm1260" is a package/folder within that path, and "Chook.wav" exists in that folder. If that's the case, then you need to use /ibm1260/Chook.wav (or potentially ibm1260/Chook.wav, I don't typically use the class loader for resource lookups) as the name of the file that you pass in to getResource().
Either way, you need to make sure that the file is copied into the location of your compiled code and that the root folder is on the classpath.

The getResource and getResourceAsStream methods are for accessing resources on the classpath. You seem to be trying to access some resource that is not on the classpath.
The logic that getResource and getResourceAsStream use to locate resources is essentially the same. The difference between the methods is that one returns a URL, and the other an InputStream.
It just can't be this hard. Any clues?
This is not that hard at all. You just need to understand how classpaths work ... and make sure that you use a resource name that resolves to a resource that you've put in the correct location in one of the directories or JAR files on the classpath.
Or if the resource is not "part of" your application, don't access it this way.

Related

Why my Jar doesn't run unless I extract files?

Every time I run the exported .jar file, that contains a JFrame with an image as its icon, the file doesn't run, unless I extract the file. In the compiler it is running. I dont want to make a launcher that saves both, the resources package and the jar file, in a directory.
"Why my Jar doesn't run unless I extract files?"
This seems to be the behavior of using File to your resources. Take for example
File file = new File("resources/image.png");
Image image = ImagIO.read(file);
And you project structure (Note the resources should actually be in the src, so that it builds into the jar automatically - unless you configure it differently. But for the sake of this argument, let's say you do confgigure it where resources is built to the jar)
C:\
Project
resources\image.png
Some examination:
Run from IDE - WORKS! Why? Using File looks for files on the file system. Using a relative path, the search will begin from the "working directory", which in the case of the IDE in generally the project root. So "resources/image.png" is a valid path, relative to ProjectRoot
Build jar, say it ends up in a dist dir in the project. This is what it looks like
ProjectRoot
dist
ProjectRoot.jar
Now for the sake of this argument (and is actually the correct way), let's try and print the URL of the resource in out program, so that when you run the jar, it prints out the URL of the file
URL url = Test.class.getResource("/resources/image.png");
System.out.println(url.toString());
When we run the jar C:\ProjectRoot\dist> java -jar ProjectRoot.jar We will see the print out C:\ProjectRoot\dist\ProjectRoot.jar!\resources\image.png. You can obviously see even though the current working directory is the location of the jar, the paths no longer match, with the added jar ProjectRoot.jar! location.
So why does it work when we extract it. Well when you extract it, then the path is correct
C:\ProjectRoot
dist
resources/image.png // from extracted jar
ProjectRoot.jar
When you run from the C:\ProjectRoot\dist >, the resource dir is where is should be.
Ok enough with the explanation.
For this reason, when you want to read embedded resources, they should be read from an URL as Andrew Thompson mentioned. This url should be relative to the class calling it, or the class loader. Here are a couple different ways:
As shown already
URL url = getClass().getResource("/resources/image.png");
Notice the /. This will bring us to the root of the classpath, where the resources dir will be. URL can be passed to many constructors, like ImageIcon(URL) or `ImageI.read(URL)
You can use:
InputStream is = getClass().getResourceAsStream("/resources/image.png");
Which will use an URL under the hood. You can use InputStream with many constructors also.
There's also ways to use the class loader, which will start at the root, so you don't need the /
URL url = getClass().getClassLoader().getResource("resources/image.png");
So there are a few ways you can go about it. But in general, reading File with hard coded string paths is never a good idea, when using embedded resources. It's possible to obtain the path dynamically so you can use File, but you will still need to use one of the aforementioned techniques, which unless you really need a File would be pointless, as you can do what you need with the InputStream or URL
To make a long story short
This would work
ProjectRoot
src\resources\image.png
URL url = getClass().getResource("/resources/image.png");
Image image = ImageIO.read(url);

How to use ClassLoader.getResources() in jar file

Problem statement:
I have a jar file with a set of configuration files in a package mycompany/configuration/file/.
I don't know the file names.
My intention is to load the file names at runtime from the jar file in which they are packaged and then use it in my application.
As far as I understood:
When I use the ClassLoader.getResources("mycompany/configuration/file/") I should be getting all the configuration files as URLs.
However, this is what is happening:
I get one URL object with URL like jar:file:/C:/myJarName.jar!mycompany/configuration/file/
Could you please let me know what I am doing wrong ?
For what you are trying to do I don't think it is possible.
getResource and getResources are about finding named resources within the classloader's classpath, not listing values beneath a directory or folder.
So for example ClassLoader.getResources("mycompany/configuration/file/Config.cfg") would return all the files named Config.cfg that existed in the mycompany/configuration/file path within the class loader's class path (I find this especially useful for loading version information personally).
In your case I think you might almost have half a solution. The URL you are getting back contains the source jar file (jar:file:/C:/myJarName.jar). You could use this information to crack open the jar file a read a listing of the entries, filtering those entries whose name starts with "mycompany/configuration/file/".
From there, you could then fall back on the getResource method to load a reference to each one (now that you have the name and path)

Classpath resource within jar

I have a project A, which contains some java files and a classpath resource R.txt. Within the project I use ClassLoader.getSystemResource("R.txt"); to retrieve R.txt.
Then I have a project B which includes project A's jar-file. Now getSystemResource("R.txt") wont find the textfile (and yes, it's still in the root of the jar file). Even trying "/R.txt" as was suggested on some other site didn't work. Any ideas?
Use getResource instead of getSystemResource to use a resource specific to a given classloader instead of the system. For example, try any of the following:
URL resource = getClass().getClassLoader().getResource("R.txt");
URL resource = Foo.class.getClassLoader().getResource("R.txt");
URL resource = getClass().getResource("/R.txt");
URL resource = Foo.class.getResource("/R.txt");
Note the leading slash when calling Class.getResource instead of ClassLoader.getResource; Class.getResource is relative to the package containing the class unless you have a leading slash, whereas ClassLoader.getResource is always absolute.
Apparently your JAR is not loaded by the system classloader, so getSystemResource() can't work. This should work:
ClassFromProjectA.class.getClassLoader().getResource("R.txt")
IMO more convenient is putting resources inside the same package as the classes that use them, so you can use the shorter
ClassFromProjectA.class.getResource("R.txt")
(or, inside that class just getClass().getResource("R.txt"))
Does ClassLoader.getResource() work ? At the moment you're simply specifying that the system classloader is to be used.

How to work with file path when addressing project folders content?

Let's say I have a structure
-bin/com/abc/A.class
-src/com/abc/A.java
-config/info.txt
How to address the file info.txt from A class?
Should we use "user.dir" property or "config/info.txt" so that it would work ?
I'll compile this into the jar and after that
the jar will be used from the servlet,
but I don't think that's important
cause this file is written and read from
internal jar's methods only.
Just put it in the runtime classpath and use ClassLoader#getResourceAsStream() to get an InputStream of it. Putting it in the JAR file among the classes, or adding its (JAR-relative) path to the Class-Path entry of the JAR's manifest.mf file is more than sufficient.
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
InputStream input = classLoader.getResourceAsStream("config/info.txt");
// Do your thing to read it.
Or if you actually want to get it in flavor of a java.io.File, then make use of ClassLoader#getResource(), URL#toURI() and the File constructor taking an URI:
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
URL url = classLoader.getResource("config/info.txt");
File file = new File(url.toURI());
// Do your thing with it.
Do not use relative paths in java.io stuff. It would be dependent on the current working directory which you have no control over at any way. It's simply receipt for portability trouble. Just make use of the classpath.
That said, are you aware of the java.util.Properties API? It namely look like you're trying to achieve the same thing which is more easy to be done with propertiesfiles.

class loader: java.io.FileNotFoundException: file:/home/aaa/.m2/repository/com/st/module-1.1-SNAPSHOT.jar!/com/st/resource (No such file or directory)

I have problems with class loaders.
Sometimes it works, sometimes it doesn't work.
When I started, I have tested this works but not from *.jar:
URL url = AcAnalyzer.class.getResource("../stuff/resource");
// and this works even from jar file:
URL url = Acnalyzer.class.getResource("/stuff/resource");
URL url = AcAnalyzer.class.getClassLoader().getResource("stuff/resource");
// But I got into the problem with tomcat..when I need to deploy it into the tomcat I had to do this:
URL url = Thread.currentThread().getContextClassLoader().getResource("something.xml");
where something.xml has to be in WEB-INF/classes/
... url.getFile();
The problem is that most of the time it has to work within and not within jar at the same time. Now I have test where my class is getting resource, and the jar file is used in some project deployed under the tomcat.. and somehow it doesn't want to work anymore:
I'm bit puzzled about class loaders. How to get this resource? And and the same time have working test.
URL url = Thread.currentThread().getContextClassLoader().getResource("com/st/resource");
FileInputStream inputStream = new FileInputStream(url.getFile());
java.io.FileNotFoundException: file:/home/aaa/.m2/repository/com/st/module-1.1-SNAPSHOT.jar!/com/st/resource (No such file or directory)
I'm not sure exactly what the question is in the first part, although I'd advise not using "../" in a resource path.
For the second part, if you want to load a resource you shouldn't use FileInputStream - you should use getResourceAsStream() instead of getResource().getFile(). Just load from that InputStream - there won't always be an individual file that you can load with FileInputStream.
EDIT: The two ways of referring to a resource are ClassLoader.getResource and Class.getResource (and the equivalents with the AsStream suffix). The difference between them (the only difference that I'm aware of, although there may be others) is that Class.getResource treats the given path as being relative to the package of the class that you call it on. So
ClassLoader.getResource("foo/bar/baz/test.xml")
is equivalent to
foo.bar.SomeClass.class.getResource("baz/test.xml");
As for the difference between Class.getClassLoader() and Thread.getContextClassLoader() I won't claim to have a good understanding - I suggest you ask that as a separate question.

Categories

Resources