Classpath resource within jar - java

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.

Related

Can't enumerate `class` files with ClassLoader#getResources()

I am trying to enumerate classes in the package with
Enumeration<URL> resourceUrls = myObject.getClassLoader().getResources("path/to/my/package/");
while (resourceUrls.hasMoreElements()) {
...
Unfortunately it returns nothing. Why?
Assuming path is correct. Path starts with no slash and ends with slash. There are several public classes under path.to.my.package package.
I took this code from Spring.
You cannot walk a class path like you can walk a file path. Walking a file path is done on the file system, which does not apply to a class path.
While a java class path entries are formed like file paths and usually are folders and files (either on the file system or inside a JAR archive), it does not necessarily have to be that way. In fact, the classes of one single package may originate from various locations of differing nature: one might be loaded from a local JAR file while another one might be loaded from a remote URL.
The method ClassLoader.getResources() exists to provide access to all "occurrences" of a resource if it has the same name in different JAR files (or other locations). For example you can use
ClassLoader.getSystemClassLoader().getResources("META-INF/MANIFEST.MF");
to access the manifest file of each JAR file in your class path.
Try with
Enumeration<URL> urls = ClassLoader.getSystemClassLoader().getResources("path/to/my/package");
while (urls.hasMoreElements()) {
System.out.println(urls.nextElement());
}

Relative path for library exporting in Java

I'm trying to export a .JAR to be used as library to other projects. The problem is that I need to use relative paths when referencing files inside this library, but the only solutions I found were using absolute paths like:
private static final String FILE = new File("").getAbsolutePath().concat("/src/bla/file.txt");
Obviously whenever I try to run this line of code as an exported library I'll get something like DRIVE/project/src/bla/file.txt which is not correct since this .JAR can be anywhere inside DRIVE/projects like DRIVE/projects/lib/myLib.jar.
In Nodejs we had easy functions to retrieve relative paths according to the runtime location. How can I reference files in such a way that it will capture the "runtime path" so that I can safely reference them and the path will be dynamically solved?
For those who are so eager to mark this question as duplicate, please read with attention first. I'm NOT asking how to READ files from resources!
To use the "file.txt" present in the classpath,we need to make sure the "file.txt" is present in the directory represented by classpath.
Assume you have all the class files generated in a directory named "/home/abcuser/target".
For simplicity we will place the file.txt in the target directory root level.
The main class is say TestFileAccess.class(the class with the main method)
To execute the main class present in the target directory you can use the below command
java -cp /home/abcuser/target TestFileAccess
Now, the classpath in this case is /home/abcuser/target
To access the resources on classpath,you can go with two ways.
ClassLoader.getSystemResource and ClassLoader.getSystemResourceAsStream methods.
Class.getResource and Class.getResourceAsStream
The main difference between the ClassLoader and Class versions of the methods is in the way that relative paths are interpreted.
The Class methods resolve a relative path in the "directory" that corresponds to the classes package.
The ClassLoader methods treat relative paths as if they were absolute; i.e. the resolve them in the "root directory" of the classpath
Using ClassLoader you can use the below snippet
InputStream inputStream = ClassLoader.getSystemResourceAsStream("file.txt");
To explicitly reference a resource as a classpath file you can add the resource path to the classpath while executing the java code.
Let's say your resource "file.txt" is in /home/abcuser/resources.
You can add the the resource path to the classpath during the java execution start as shown below
java -cp "/home/abcuser/target:/home/abcuser/resources" TestFileAccess

How should I use getResource() in 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.

can I load resource from classpath if set to any directory?

I want to load a resource with this:
InputStream iStream = Config.class.getResourceAsStream("autopublisherpath.cfg");
So I set the CLASSPATH to make it work. This is my dir hierarchy:
- autopublisher
.classes
.lib
.resources
If I add %AUTOPUBLISHER_HOME%\resources\config to my classpath I cannot get the resource. Otherwise if I put my .cfg file in classes and add %AUTOPUBLISHER_HOME%\classes the resource is loaded properly. The classes dir doesn't contain anything other than the autopublisherpath.cfg.
Ultimately I want to call:
java com.test.Something
Where something is loading the resource. The thing is I want user to modify this config file so I do not include it inside my jar packaging.
Am I not understanding the CLASSPATH correctly?
thank you
One thing to pay attention to when using getResourceAsStream is the format of the resource name that you are retrieving. By default if you do not specify a path, e.g., "autopublisherpath.cfg", the classloader will expect that the resource being specified is within the same package as the Class on which you executed the getResourcesAsStream method. The reason for this behavior can be found in the JVM documentation for getResourceAsStream:
If the name begins with a '/' ('\u002f'), then the absolute name of the resource is the portion of the name following the '/'.
Otherwise, the absolute name is of the following form: modified_package_name/name
In your particular example, if the Config class was located in the com.test.config package, then the resource name of "autopublisherpath.cfg" would be converted to "/com/test/config/autopublisherpath.cfg" (period in package is replaced with a '/' character). As a result, keeping with your original project hierarchy, you would need to place the file into the location:
autopublisher/resources/config/com/test/config
where autopublisher/resources/config was added as part of application's execution classpath.
If you are looking to add a specific config directory to your classpath and want the file to be located in the root of that directory, then you need to prefix the name of the file with a '/' character, which specifies that the resource should be in the root package of the classpath.
InputStream iStream = Config.class.getResourceAsStream("/autopublisherpath.cfg");
Using this code, you should be able to add the resource/config directory to your classpath and read the file as expected.
On a side note, the getResourceAsStream method loads the resource using the Classloader of the class from which it was executed (in this case Config). Unless your application uses multiple class loaders, you can perform the same function from any of your class instances using this.getClass().getResourceAsStream(...).

Class.getResourceAsStream() issue

I have a JAR-archive with java classes. One of them uses some resource that is embedded into the same JAR. In order to load that resource I use
MyClass.class.getResourceAsStream(myResourceName);
One thing that bothers me though is whether it is guaranteed that required resource will be loaded from within the same JAR. The documentation for "getResourceAsStream()" method (and corresponding ClassLoader's method) is not really clear to me.
What would happen if there's a resource with the same name located somewhere in JVM classpath before my JAR? Will that resource be loaded instead of the one embedded in my JAR? Is there any other way to substitute resource embedded in JAR?
Yes. The first matching resource found on the class path is returned, just like an executable search path. This is why resources are often "namespaced" by putting them in directories that mirror the package structure of the library or application.
This behavior may be slightly different in the presence of custom classloaders (say in OSGi), but for vanilla Java apps, it is the case.
It works much the same way as for finding class files. So first try the parent class loader (recursively) then do whatever the class loader implementation does to find files.
There is no checking of the immediate caller class loader (as ResourceBundle does - see section 6.3 of the Java Secure Coding Guidelines). However, you do need permissions to open the URL, as ClassLoader.getResourceAsStream just calls URL.openStream in the default implementation.
Specify the package. Assuming you use com.yourcompany.file it SHOULD be unique. (Unless someone WANTS to override your config file via the classpath.)
If you want to read the file only from a specific JAR you can open the JarFile and read it directly.

Categories

Resources