getResourceAsStream not loading resource - java

The project that I am currently working on utilizes an old application contained within a .jar file. One of the responsibilities of this application is that it updates the database when changes to the configuration files are made. Every time I try to run this file (which is a simple Ant Task extension), I get an exception thrown early in the process. I decompiled the Java file responsible, and found that the exception being thrown happens here. I do not know what the issue is, as "hibernate.cfg.xml" is contained within the same .jar file as the .class throwing the exception.
ClassLoader loader = Thread.currentThread().getContextClassLoader();
InputStream in = loader.getResourceAsStream("hibernate.cfg.xml");
if (in == null) {
throw new RuntimeException("Couldn't find built in hibernate config");
}
If anyone has any ideas, even pointing me in the right direction, I would be grateful.
Of course, any solution will have to be external, as the client already has this build of the program in production use.

Are you loading it from the root dir and you need "/hibernate.cfg.xml" instead of just hibernate.cfg.xml?

getResourceAsStream() expects the file to be in the same package as the Class that was the origin of the ClassLoader. Is your file in the right package?
Doh. Didn't read the question fully. Please ignore this.

try
InputStream in = YourClass.class..getResourceAsStream("hibernate.cfg.xml");
this will work if the class is in the same jar as the cfg file.
edit:
if you can't rebuild the application, you will need to add the jar to the bootstrap classloader. this is very ugly.
you can do it by running the jvm with (play with the exact arguments, you may need to add rt.jar from your jre to it as well).
-Xbootclasspath your.jar
your problem is that the code is using the classloader that loaded the Thread class, which is most likely the bootstrap classloader. and that you are now in a more complex environment (app server?) that loads your jar using a different classloader. so the bootstrap classloader can't find your resource.

It is possible that the classloader cannot open files contained in the jar file. Try one of two things 1) try extracting the jar file and running it from the file system, or 2) if the jar manifest has a ClassPath entry, try extracting the hibernate.cfg.xml into a directory in the classpath and see if it runs.

Apparently the hibernate.cfg.xml file isn't located in the source root of the JAR file, but it is instead placed inside a package. You'll need to specify the package path in the resource name as well.

Related

Getting resource file from inside jar

I need to get a resource from inside the root of the application when its packed into jar. My project is like this:
ProjectRoot
resource.txt //want to access from here
src
main
java
package1
package2
package3
Main.java
target
app.jar
classes
resource.txt //works when here
package1
package2
package3
Main.class
I use this code:
Path path = Paths.get("resource.txt");
When run before packaging into a jar, it finds the file just fine (inside ProjectRoot). When running the jar, it can't find it, and transforms this path to target/resource.txt.
This code:
BufferedReader br = new BufferedReader(new InputStreamReader(new Main().getClass().getClassLoader().getResourceAsStream(
"resource.txt")));
when run before packaging looks for the resource inside target/classes. After packaging it claims to taking the resource from .../target/app.jar!/resource.txt.
This code:
BufferedReader br = new BufferedReader(new InputStreamReader(new Main().getClass().getClassLoader().getResourceAsStream(
"/resource.txt")));
I can't understand where's looking for the resource, but it doesn't seem to be ProjectRoot.
All I want to do is to place the resource inside ProjectRoot and be able to access it from both outside jar (when running the class files from IDE) and inside (after having packaged the files into a jar file using Maven).
EDIT: I NEED THE CODE TO WORK BOTH FOR PRE- AND POST- packaging. MEANING: If I run a Main.java FROM INSIDE IDE IT WOULD GET THE RESOURCE; IF I PACKAGE EVERYTHING INTO JAR AND RUN JAR IT WOULD GET THE RESOURCE - ALL WITH THE SAME CODE.
Use: Main.class.getResource("/resource.txt").
Note that your attempt using any call to getClassLoader is strictly worse (it's more text, and will fail more often, because that class loader can in exotic cases be null (specifically, when you're part of the bootstrap loader), whereas calling getResource directly on the class always works.
The reason your snippet does not work is because when invoking getResource on the classloader, you must NOT start the resource with a slash. When invoking on a class directly, you can (if you don't, it'll be relative to the package of the class you're calling it on, if you do, it'll be relative to the root).
TL;DR: Of the forms SomeClass.class.getClassLoader().getResource, getClass().getResource and MyClass.class.getResource, only the last one is correct, the rest are strictly inferior and therefore should not be used at all.
Maven uses something called the Standard Directory Layout. If you don't follow this layout then the plugins can't do their job correctly. Technically, you can configure Maven to use different directories but 99.999% of the time this is not necessary.
One of the features of this layout is that production files go in:
<project-dir>/src/main/java
All *.java files
<project-dir>/src/main/resources
All non-*.java files (that are meant to be resources)
When you build your project the Java source files are compiled and the *.class files are put into the target/classes directory; this is done by the maven-compiler-plugin. Meanwhile, the resource files are copied from src/main/resources into target/classes as well; the maven-resources-plugin is responsible for this.
Note: See Introduction to the Build Lifecycle for more information about phases and which plugins are executed by which phase. This Stack Overflow question may also be useful.
When you launch your application from the IDE (possibly via the exec-maven-plugin) the target/classes directory is put on the classpath. This means all the compiled classes from src/main/java and all the copied resources from src/main/resources are available to use via the classpath.
Then, when you package your application in a JAR file, all the files in target/classes are added to the resulting JAR file (handled by the maven-jar-plugin). This includes the resources copied from src/main/resources. When you launch the application using this JAR file the resources are still available to use via the classpath, because they're embedded in the JAR file.
To make resource.txt available on the classpath, just move:
<project-dir>/resource.txt
To:
<project-dir>/src/main/resources/resource.txt.
Then you can use Class#getResource with /resource.txt as the path and everything should work out for you. The URL returned by getResource will be different depending on if you're executing against target/classes or against the JAR file.
When executing against target/classes you'll get something like:
file:///.../<project-dir>/target/classes/resource.txt
When executing against the JAR file you'll get something like:
jar:file:///.../<project-dir>/target/projectname-version.jar!/resource.txt
Note: This all assumes resource.txt is actually supposed to be a resource and not an external file. Resources are typically read-only once deployed in a JAR file; if you need a writable file then it's up to you to use a designated location for the file (e.g. a folder in the user's home directory). One typically accesses external files via either java.io.File or java.nio.file.*. Remember, resources are not the same thing as normal files.
Now, if you were to put resource.txt directly under <project-dir> that would mean nothing to Maven. It would not be copied to target/classes or end up in the JAR file which means the resource is never available on the classpath. So just to reiterate, all resources go under src/main/resources.
Check out the Javadoc of java.lang.Class#getResource(String) for more information about the path, such as when to use a leading / and when not to. The link points to the Javadoc for Java 12 which includes information about resources and modules (JPMS/Jigsaw modules, not Maven modules); if you aren't using modules you can ignore that part of the documentation.

Using relative path in a maven project

I have a maven project with these standard directory structures:
src/main/java
src/main/java/pdf/Pdf.java
src/test/resources
src/test/resources/files/x.pdf
In my Pdf.java,
File file = new File("../../../test/resources/files/x.pdf");
Why does it report "No such file or dirctory"? The relative path should work. Right?
Relative paths work relative to the current working directory. Maven does not set it, so it is inherited from whatever value it had in the Java process your code is executing in.
The only reliable way is to figure it out in your own code. Depending on how you do things, there are several ways to do so. See How to get the real path of Java application at runtime? for suggestions. You are most likely looking at this.getClass().getProtectionDomain().getCodeSource().getLocation() and then you know where the class file is and can navigate relative to that.
Why does it report "No such file or dirctory"? The relative path should work. Right?
wrong.
Your classes are compiled to $PROJECT_ROOT/target/classes
and your resources are copied to the same folder keeping their relative paths below src/main/resources.
The file will be located relative to the classpath of which the root is $PROJECT_ROOT/target/classes. Therefore you have to write in your Pdf.java:
File file = new File("/files/x.pdf");
Your relative path will be evaluated from the projects current working directory which is $PROJECT_ROOT (AFAIR).
But it does not matter because you want that to work in your final application and not only in your build environment. Therefore you should access the file with getClass().getResource("/path/to/file/within/classpath") which searches the file in the class path of which the root is $PROJECT_ROOT/target/classes.
No the way you are referencing the files is according to your file system. Java knows about the classpath not the file system if you want to reference something like that you have to use the fully qualified name of the file.
Also I do not know if File constructor works with the classpath since it's an abstraction to manage the file system it will depend where the application is run from. Say it is run from the target directory at the same level as source in that case you have to go one directory up and then on src then test the resources the files and finally in x.pdf.
Since you are using a resources folder I think you want the file to be on the classpath and then you can load a resource with:
InputStream in = this.getClass().getClassLoader()
.getResourceAsStream("<path in classpath>");
Then you can create a FileInputStream or something to wrap around the file. Otherwise use the fully qualiefied name and put it somewere like /home/{user}/files/x.pdf.

Why does getResourceAsStream() work in the IDE but not the JAR?

I just want to read a file into my program. The file is located one directory above the working directory at "../f.fsh". So the following code runs correctly when I run it in the IDE
String name="../f.fsh";
InputStream is = getClass().getResourceAsStream(name);
InputStreamReader isreader=new InputStreamReader(is);//CRASHES HERE WITH NULL POINTER EXCEPTION
BufferedReader br = new BufferedReader(isreader);
but when I create a JAR file that has f.fsh zipped inside of it and run it, it crashes when creating the InputStreamReader, because the InputStream is null.
I've read a bunch of answers to questions about input streams and JAR files, and what I got out of it is that I should be using relative paths, but I am already doing that. From what I understand getResourceAsStream() can find files relative to the root of the project, that is what I want. Why does it not work in the JAR? What is going wrong, how can I fix it?
Does it have to do with the classpath? I thought that was only for including files external to the jar being run.
I have also tried, but still fail, when putting a slash in:
InputStream is = getClass().getResourceAsStream("\\"+name);
I looked at: How to get a path to a resource in a Java JAR file andfound that contents of a JAR may not necesarily be accesible as a file. So I tried it with copying the file relative to the jar (one directory up from the jar), and that still fails. In any case I'd like to leave my files in the jar and be able to read them there. I don't know what's going wrong.
You can't use .. with Class.getResourceAsStream().
To load a resource f.fsh in the same package as the class, use SomeClass.class.getResourceAsStream("f.fsh")
To load a resource f.fsh in a sub-package foo.bar of the package of the class, use SomeClass.class.getResourceAsStream("foo/bar/f.fsh")
To load a resource f.fsh in any package com.company.foo.bar, use SomeClass.class.getResourceAsStream("/com/company/foo/bar/f.fsh")
This is described in the javadoc of the getResource() method, although it lacks examples.
If .. works in Class.getResourceAsStream() while running from Eclipse, it's a bug in Eclipse. Eclipse and other IDEs implement custom class loaders to fetch resources from the project at runtime. It looks like the class loader implementation in Eclipse isn't performing all the necessary validations on input to getResourceAsStream() method. In this case the bug is in your favor, but you will still need to rethink how you structure your resources for your code to work in all cases.
it's mandatory that the name of the file is CASE SENSITIVE
it's mandatory to refresh (F5) the project explorer if the file is moved or copied outside Exclipse

Where to place a file if it is getting accessed using ClassLoader.getSystemResource in WebApplication

I am using one third party jar in my code. In the jar file , in one of the classes, when I opened the class using de-compiler, the code below is written:
java.net.URL fileURL = ClassLoader.getSystemResource("SOAPConfig.xml");
Now I am using this in my webapplication, where should I place this SOAPConfig.xml so that it will find the fileURL.
Note: I have tried putting this XML in WEB-INF/classes folder. But it is not working. Your help will be appreciated.
In Addition: In the explaination you have given, It is telling me not to use this code snippet inside the third party jar in this way...What is the exact usage of this statement
ClassLoader.getSystemResource will load the resource from the system classloader, which uses the classpath of the application as started from the command line. Any classloaders created by the application at runtime (i.e. the one that looks in WEB-INF/classes) are not on the system classpath.
You need to
Look through the script that starts your server, find out which directories are on the classpath there, and put your SOAPConfig.xml in one of those. If necessary, change the classpath in the script to look in a separate directory that's just used for your config file.
Track down the person who used ClassLoader.getSystemResource in the library, kick them squarely in the nuts, and tell them never to do that again.

How can I locate a non-Java resource in a WAR at runtime?

I need to run a shell script at runtime from a WAR that I run on Tomcat.
Thus, I've placed my script theScript.sh in my src/main/resources directory (because, yes, I use Maven). This directory is in the classpath, I've checked it.
In the code, I want to copy my script in a temp directory. So I've tried to get it via my ClassLoader:
URL myURL = ClassLoader.getSystemResource("theScript.sh");
if (myURL == null) {
LOG.error("Couldn't find the resource");
}
And guess what? Yes, the "Couldn't find the resource" keeps appearing all over the place in my logs.
Any idea what I'm doing wrong here?
My environment is the traditional Eclipse/Tomcat thing.
You don't want to use the System cloassloader - that would typically only find resources that are part of Tomcat or the JRE.
You want to load the resource from the classloader that was used to load your WAR file.
There's a couple of ways to do that, but the official way would be to get access to the ServletContext (javadoc) and call either getResource or getClassLoader().getResource on that.
If you don't have the ServletContext available, then you can "cheat" by using the classloader for a class that you know came from your WAR file. Something like
this.getClass().getClassLoader().getResource("xyz");
where this is your class that's been deployed inside your WAR file.
have you tried getClass().getClassLoader().getResource() or getResourceAsStream() ?if its in the classpath, these should get it.

Categories

Resources