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.
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
*.java
files<project-dir>/src/main/resources
*.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.