Reputation: 107
After hours of fighting with this, I'm starting to get really frustrated.
I have a JKS server file that I want to load from within the jar. Using FileInputStream works a treat as long as the file is outside the Jar, but as soon as I try to change that to getResourceAsStream it will just not take it, saying (No such file or directory).
This is how the project is distributed
ProjectFolder
-src
--package.name
---JKS File
---Class calling the resource retrieval and loading into the keystore
I think I have tried almost every combination of this.getClass().getClassLoader().getResourceAsStream("jksFile.jks") I can think of.
I apologise in advance for not being more specific with the code, but I have literally lost count of what I have tried and what I havent.
HELP!
Thanks in advance
Upvotes: 5
Views: 10973
Reputation: 9318
There are two main ways of accessing resources inside the classpath, through the Class
object, and through the Classloader
, by getting getResourceAsStream
.
Ultimately, the precise rules for implementing the lookup of the resource is class loader dependant. But most of it is clearly specified. The resource is looked up by name.
The name of a resource is a '/'-separated path name that identifies the resource.
And, when exploring the classpath, the search order is also clear
This method will first search the parent class loader for the resource; if the parent is null the path of the class loader built-in to the virtual machine is searched. That failing, this method will invoke {@link #findResource(String)} to find the resource.
The findResource
is the part that is implementation dependent. However, most Java classloaders are URLClassLoader
s of some sort, and their implementation know how to explore the classpath entries (on the file system or inside JAR files - even remote ones) to resolve names as relative or absolute paths inside said-entries.
So, in a nutshell : you use getResourceByName
to explore your classpath looking for a file by relative or absolute path in the class path.
The difference between Class#getResourceAsStream
and ClassLoader#getResourceAsStream
is that the Class
version will interpret relative path as relative to the Class
's package, while the ClassLoader
version will start at the root of the classpath.
So... Given the following project structure :
src
name
gpi
file.txt
Test.java
This Test.java works :
public static void main(String[] args) throws IOException {
// relative path from this class
InputStream is = Test.class.getResourceAsStream("file.txt");
byte[] content = new byte[4096];
int length = is.read(content);
System.out.println(new String(content, 0, length));
// relative path from the root of the classpath
is = Test.class.getClassLoader().getResourceAsStream("name/gpi/file.txt");
content = new byte[4096];
length = is.read(content);
System.out.println(new String(content, 0, length));
}
Be careful in details in the documentation thought, as
// This works : on the class object, absolute path are treated as "root of the classpath"
InputStream is = Test.class.getResourceAsStream("/name/gpi/file.txt");
//This does not work, because this looks actually at the root of the file system
InputStream is = Test.class.getClassLoader().getResourceAsStream("/name/gpi/file.txt");
If you try the combinations listed here as working, and they actually don't, then either you have a "funky" classloader (which should definitely not happen on a simple project), either your compiler / packager settings (maven or eclipse or whatever) are setup in a way that discards your JKS file from the compiled/packaged result (JAR).
In maven projects in general, Java classes belong to src/main/java directory, whereas properties, configuration, and JKS files usually belong to the src/main/resources directory. Depending on you exact maven setting, putting a JKS file inside the java directory may result in it being discarded from the actual, runnable classpath. So you should check that too.
Upvotes: 1
Reputation: 2037
If it is about a war file:
When building and running the project, the .class will not be in this location (/src) anylonger, it will go to WEB-INF/classes/..., so, maybe you could try to put your JKS File there, or make the reference from classes to src inside the getResourceAsStream().
Useful link for folder hierarchy of war file: http://www.yolinux.com/TUTORIALS/Java-WAR-files.html
Upvotes: 2
Reputation: 38781
Assuming the jar contains the folder structure under src
and that contains the compiled .class
file(s) as well as or even instead of the source .java
file(s) (which makes the directoryname src
misleading):
this.getClass().getResourceAsStream(String)
or other ways of getting the class like MyClass.class
then .getResourceAsStream(String)
treats the resource-name as relative to the package = directory containing the class
this.getClass().getClassLoader().getResourceAsStream(String)
or other ways of finding the classloader like athread.getContextClassLoader()
then .getResourceAsStream(String)
treats the resource-name as relative to the root of the jar file, so you need "package/names/jksfile.jks"
.
Remember package paths in a jar use slash whereas in Java source they use dot.
(Directories 'src' and 'bin' are generally used and useful when you put the source files under src/package/names/MyClass.java
and the compiled files
in a mirror hierarchy bin/package/names/MyClass.class
.)
Upvotes: 0