Carlos B.
Carlos B.

Reputation: 107

Keystore not loading JKS file from within jar

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

Answers (3)

GPI
GPI

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 URLClassLoaders 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

Bruno Franco
Bruno Franco

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

dave_thompson_085
dave_thompson_085

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

Related Questions