Search code examples
javamavencompilationfilenotfoundexception

Maven Issue With Accessing Resource File


I am trying to use Maven to build a JAR file that will load files from the file system during runtime. I am having an issue with loading the file after compiling it with

 mvn clean package

pom.xml:

<build>
    <resources>
        <resource>
            <filtering>true</filtering>
            <directory>src/main/resources</directory>
        </resource>
    </resources>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-jar-plugin</artifactId>
            <version>3.1.0</version>
            <configuration>
                <archive>
                    <manifest>
                        <addClasspath>true</addClasspath>
                        <classpathPrefix>lib/</classpathPrefix>
                        <mainClass>App</mainClass>
                    </manifest>
                </archive>
            </configuration>
        </plugin>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <configuration>
                <source>7</source>
                <target>7</target>
            </configuration>
        </plugin>
    </plugins>
</build>

My main Class is:

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;

public class App {
    public static void main(String[] args) throws IOException {
        App app = new App();
        app.printTestFile();
    }

    public void printTestFile() throws IOException {
        File file = new File(getClass().getClassLoader().getResource("template.txt").getFile());
        System.out.println("File is in " + file.getAbsolutePath());
        printFile(file);
    }

    private static void printFile(File file) throws IOException {
        try (FileReader reader = new FileReader(file);
             BufferedReader br = new BufferedReader(reader)) {
            String line;
            while ((line = br.readLine()) != null) {
                System.out.println(line);
            }
        }
    }
}

The file is loaded with:

File file = new File(getClass().getClassLoader().getResource("template.txt").getFile());

The project structure is:

│   pom.xml
└───src
    ├───main
    │   ├───java
    │   │       App.java
    │   └───resources
    │           template.txt
    └───test
        └───java

When run with IntelliJ the result is:

File is in C:\Users\user\Documents\Git\maven-scratch\target\classes\template.txt
This is the test file's content.

However, the issue comes when run purely with Maven like:

mvn clean package
java -jar target\maven-scratch-1.0-SNAPSHOT.jar

Outputs the error:

File is in C:\Users\user\Documents\Git\maven-scratch\file:\C:\Users\user\Documents\Git\maven-scratch\target\maven-scratch-1.0-SNAPSHOT.jar!\template.txt
Exception in thread "main" java.io.FileNotFoundException: file:\C:\Users\user\Documents\Git\maven-scratch\target\maven-scratch-1.0-SNAPSHOT.jar!\template.txt (The filena
me, directory name, or volume label syntax is incorrect)
        at java.io.FileInputStream.open0(Native Method)
        at java.io.FileInputStream.open(Unknown Source)
        at java.io.FileInputStream.<init>(Unknown Source)
        at java.io.FileReader.<init>(Unknown Source)
        at App.printFile(App.java:19)
        at App.printTestFile(App.java:15)
        at App.main(App.java:9)

The thing that puzzles me the most is that the file that's trying to be loaded has the path:

C:\Users\user\Documents\Git\maven-scratch\file:\C:\Users\user\Documents\Git\maven-scratch\target\maven-scratch-1.0-SNAPSHOT.jar!\template.txt

... whether with IntelliJ the path is:

C:\Users\user\Documents\Git\maven-scratch\target\classes\template.txt

Is there an easy way to use a file from Maven's resources folder and after packaging into a JAR with Maven to also be able to read the file from the target directory?


Solution

  • The JAR construction is fine (easily verified by loading it into a zip file viewer).

    IntelliJ's classpath is the local filesystem - it is loading a real file ./target/classes/template.txt

    Commandline classpath is looking for the same thing inside the JAR file (maven-scratch-1.0-SNAPSHOT.jar!\template.txt). This is not an actual file itself, it is some bytes inside the JAR file. Basically it can't be read by FileReader which is looking for a file literally named maven-scratch-1.0-SNAPSHOT.jar!\template.txt

        // getResourceAsStream doesn't care if it comes from a real file or a jar
        InputStream is = getClass().getClassLoader().getResourceAsStream("fix.cfg");
        InputStreamReader reader = new InputStreamReader(is);