Load resource from class's own JAR file - java

When using Class.getResource() and supplying a rather common resource name, like "license.html", Java may load a "license.html" resource from another JAR file which is listed earlier in the CLASSPATH, but may be completely unrelated to my application. Is there a way to force the ClassLoader to first try to load the resource from the same JAR file which my class is in and only if not found, look in other JARs on the CLASSPATH?

Alternatively you could use the JarURLConnection if you know the exact jar in which your file resides:
jar:<url>!/{entry}

..common resource name, like "license.html"'
A good reason to give it an uncommon path, for instance according to the package name.
E.G. "/com/our/app/license.html" is unlikely to collide with any other license.html

Related

Whats the best way to specify a path to read from class-path even if project was a jar

I am trying to read a Properties file in a maven nature project using the Properties.load(); I am specifying a path as a string ex. "./someFolder/file.properties",
but when I try to use my project as dependency in other projects I am forced to copy those files to the other project , simply because the "." means current directory.
Is there a way to specify a path so it will always be valid despite where I am calling it from ? ,
I have tried using the MyClass.class.getClassLoader().getResourceAsStream() but I am having trouble using it , it worked sometimes and failed other times.
There are lots of misconceptions in your question.
"." means classPath
No. When used inside a filesystem path (i.e. a path passed to the constructor of a File, or FileReader, or FileInputStream), "." means the current directory.
When used in a resource path (i.e. passed to Class[Loader].getResource[AsStream]()), it's invalid.
The trick is to carefully read the documentation.
getResourceAsStream() expects a /-separated path.
When using ClassLoader.getResource[AsStream](), this path always starts from the root of the classpath. So you would pass a path looking exactly like a fully qualified class name, except the dots would be replaced by slashes. So, com/foo/bar.properties looks for a resource named bar.properties, in the package com.foo.
When using SomeClass.class.getResource[AsStream](), either the path starts with a /, and the path starts from the root of the classpath, or it doesn't, and it starts from the package of SomeClass. So, if SomeClass is in the package com.foo, using /com/foo/bar.properties is equivalent to using bar.properties.
It's hard to tell what you're doing wrong, since you're not providing any detail. But you really need to understand the difference between opening a file on the file system, and reading a resource loaded by the class loader. Sometimes, the resources just happen to be loaded by the class loader from the filesystem, because the classpath happens to contain directories, and not just jar files.
I noticed that my problem was that I had my properties files in the project path itself, and that the ClassLoader.getResource[AsStream](); looks is the target/classes folder, and that I didn't have the resources folder in my project.
I solved it my adding the resources folder to my build path and adding my files in the src/main/resources as the following src/main/resources/foo/bar.properties and loading it by SomeClass.class.getClassLoader().loadResourceAsStream("foo/bar.properties");.

how to find a local resource from dependent jar?

I have written a code that is packed to 1.jar
with this code:
return isProd? "/etc/waze/automation/devices.json":
DeviceRepositoryFromJsonFile.class.getClassLoader().getResource("devices.json").getPath().toString();
devices.json is here:
I have another project that depends on 1.jar
however the classLoader doesn't find the local devices.json file but rather one packed in the jar
anyhow it shows the file doesn't exist.
How can I fix this? just use a absolute path anyhow?
If as in your screenshot the devices.json locate in the src/main/resources and the package have successfully treat that as the package path and put in the jar file root directory, then you can just find the file via:
DeviceRepositoryFromJsonFile.class.getResource("/devices.json");
Note the "/" slash is important to indicate that to search from the root of the classpath.
It does not answer your question directly, but it may solve your problems faster.
As far as I can see you try to detect the absolute path to json file and pass it to another method so this file could be processed. Instead, it could be done simpler:
public byte[] getDevicesJsonBytes() {
return isProd
? IOUtils.toByteArray(ABSOLUTE_PATH_TO_PROD_FILE)
: IOUtils.toByteArray(DeviceRepositoryFromJsonFile.class.getResourceAsStream(RESOURCE_CLASSPATH);
}
The common way to read classpath resources it to use getResourceAsStream on class or classLoader instance. Also, many frameworks have their own resources abstractions, but I guess you don't need them now.

Resource loading in Java not working as it should

This is the well known problem of loading resources from a jar file. This is not the first time I've tried to do this, but now it doesn't work the way I expect it to.
Normally I try to load the Resources with this.getClass.getResource("foo.png"), or getResourceAsStream()and it works. Now however it does not. The Resource is always null.
If I let System.out.println(this.getClass.getResource("")) print me the path (from eclipse) it shows /path/to/eclipseproject/package/structure/. Running this from a jar it just shows rsrc:package/structure
If I recall correctly this should print the path to the jar. Furthermore I thought this would print the package structure in both cases. Am I doing something wrong?
Here is the thing...
When Extracting the file from the Jar use:
this.getClass.getResource("/foo.png")
When running from a runnable Jar use, to reference an external file in the Jar folder path:
this.getClass.getResource("foo.png")
// When running this from Eclipse, it would refer to files in project root!
I have a code in the lower level determining where I'm running from to determine the correct path.
Doe this get the path you need?
this.getClass().getClassLoader().getResource("<your class name>.class").getPath();
See also this question for more on this issue.
Unless you prepend the path to the resources with '/', Class.getResource() will search for the resource in class package. E.g.: tld.domain.Foo.class.getResource("Bar.txt") will search for tld/domain/Bar.txt
Check the URLClassLoader for all the gory details, but it really depends on whether you are trying to access a ressource in the jar,
using a class loaded inside the same jar, in this case your file 'root' is the root of the jar
using a class loaded outside the jar (your eclipse case) where the root is your 'working directory'
To access resources inside a jar from outside, you should use something like
URL url = new URL( "jar", "", "file:" + jar.getCanonicalPath( ) + "!/" + localPathResource );
url.openStream(...)
This answer provides an explanation of how to load class resources from JAR files, even when the class is not in the JAR file and not in the Class-Path specified in the JAR file's manifest. There are also links to code.

Can getResourceAsStream() find files outside the jar file?

I'm developing an application that uses a library that loads a configuration file as:
InputStream in = getClass().getResourceAsStream(resource);
My application in then packed in a .jar file. If resource is inside the .jar file, I can specify the path as "/relative/path/to/resource/resource".
I'd like to know if is it possible to find the resource if it is outside the .jar file, and in this case, how would I specify the path.
(Assuming my application's jar is in app/ and the resource is in app/config).
The program uses a 3rd party library.
The library uses the resource as a configuration file.
I also want to tweak the configuration file without having to unzip/zip the jar file all the time.
In general, yes it can. Technically, the class's ClassLoader is used to locate the resource named in the parameter (see Javadoc here). If you're not using a special ClassLoader then you'll get the bootstrap class loader, which searches the class path. So, if you have directories or other jar files on the classpath, they will be searched.
It will get any resource which is available to the appropriate classloader (the one that getClass().getClassLoader() would return). Whether the classloader includes both the app directory and the jar files depends on the rest of your application context.
There is a way to get the current directory ( How to get the path of a running JAR file?), but for configuration you can just pass a -Dconfig.location to the JVM specifying an absolute path for the configuration

Absolute Path of Project's folder in Java

Lots of confusion in this topic. Several Questions have been asked. Things still seem unclear.
ClassLoader, Absolute File Paths etc etc
Suppose I have a project directory structure as,
MyProject--
--dist
--lib
--src
--test
I have a resource say "txtfile.txt" in "lib/txt" directory. I want to access it in a system independent way. I need the absolute path of the project.
So I can code the path as abspath+"/lib/Dictionary/txtfile.txt"
Suppose I do this
java.io.File file = new java.io.File(""); //Dummy file
String abspath=file.getAbsolutePath();
I get the current working directory which is not necessarily project root.
Suppose I execute the final 'prj.jar' from the 'dist' folder which also contains "lib/txt/txtfile.txt" directory structure and resource,It should work here too. I should absolute path of dist folder.
Hope the problem is clear.
You should really be using getResource() or getResourceAsStream() using your class loader for this sort of thing. In particular, these methods use your ClassLoader to determine the search context for resources within your project.
Specify something like getClass().getResource("lib/txtfile.txt") in order to pick up the text file.
To clarify: instead of thinking about how to get the path of the resource you ought to be thinking about getting the resource -- in this case a file in a directory somewhere (possibly inside your JAR). It's not necessary to know some absolute path in this case, only some URL to get at the file, and the ClassLoader will return this URL for you. If you want to open a stream to the file you can do this directly without messing around with a URL using getResourceAsStream.
The resources you're trying to access through the ClassLoader need to be on the Class-Path (configured in the Manifest of your JAR file). This is critical! The ClassLoader uses the Class-Path to find the resources, so if you don't provide enough context in the Class-Path it won't be able to find anything. If you add . the ClassLoader should resolve anything inside or outside of the JAR depending on how you refer to the resource, though you can certainly be more specific.
Referring to the resource prefixed with a . will cause the ClassLoader to also look for files outside of the JAR, while not prefixing the resource path with a period will direct the ClassLoader to look only inside the JAR file.
That means if you have some file inside the JAR in a directory lib with name foo.txt and you want to get the resource then you'd run getResource("lib/foo.txt");
If the same resource were outside the JAR you'd run getResource("./lib/foo.txt");
First, make sure the lib directory is in your classpath. You can do this by adding the command line parameter in your startup script:
$JAVA_HOME/bin/java -classpath .:lib com.example.MyMainClass
save this as MyProject/start.sh or any os dependent script.
Then you can access the textfile.txt (as rightly mentioned by Mark) as:
// if you want this as a File
URL res = getClass().getClassLoader().getResource("text/textfile.txt");
File f = new File(res.getFile());
// As InputStream
InputStream in = getClass().getClassLoader()
.getResourceAsStream("text/textfile.txt");
#Mark is correct. That is by far the simplest and most robust approach.
However, if you really have to have a File, then your best bet is to try the following:
turn the contents of the System property "java.class.path" into a list of pathnames,
identify the JAR pathname in the list based on its filename,
figure out what "../.." is relative to the JAR pathname to give you the "project" directory, and
build your target path relative to the project directory.
Another alternative is to embed the project directory name in a wrapper script and set it as a system property using a -D option. It is also possible to have a wrapper script figure out its own absolute pathname; e.g. using whence.

Categories