11

Simple Java program:

public static String loadText(String file) {
    StringBuilder finalString = new StringBuilder();

    InputStream in = null;
    BufferedReader reader = null;
    InputStreamReader isr = null;

    try{
        System.out.println("Text File: " + file);

        // Version 1
        //URL url = Thread.currentThread().getClass().getResource(file); 
        //in = url.openStream();

        // Version 2
        in = Class.class.getResourceAsStream(file);

        isr = new InputStreamReader(in);
        reader = new BufferedReader(isr);
        String line;
        while((line = reader.readLine()) != null) {
            finalString.append(line).append("//\n");
        }
    }
    catch(IOException e) {
        e.printStackTrace();
        System.exit(-1);
    }
    finally {
        try {
            if (isr != null) { isr.close(); }
            if (reader != null) { reader.close(); }
            if (in != null) { in.close(); }
            } catch (IOException e) { e.printStackTrace(); }
        }
    return finalString.toString();
}

The getResource and getResourceAsStream methods works fine in JDK 8 (java-8-openjdk-amd64) but they always return null in JDK 11.

Questions: Why? And how can I fix this?

  • Operation System: Linux Mint 19 Tara x64
  • IDE: Eclipse 2018-12 (4.10.0)
Lii
  • 11,553
  • 8
  • 64
  • 88
Alexey
  • 119
  • 1
  • 2
  • 8
  • 1
    What's a typical value for the 'file' argument you are using? – DodgyCodeException Jan 03 '19 at 12:04
  • Please check [this](https://stackoverflow.com/questions/48673769/java-9-module-system-resources-files-location) question out – Denis Zavedeev Jan 03 '19 at 12:05
  • 5
    Class.class.getResourceAsStream(...) will attempt to locate the resource in the java.base module. Replace "Class.class" with the name of your class so that it finds it in your module (or on the class path). – Alan Bateman Jan 03 '19 at 12:48
  • 2
    Further, it’s worth learning about the [try-with-resources statement](https://docs.oracle.com/javase/8/docs/technotes/guides/language/try-with-resources.html)… – Holger Jan 10 '19 at 15:29
  • Possible duplicate of [Accessing resource files from external modules](https://stackoverflow.com/questions/46861589/accessing-resource-files-from-external-modules) – smac89 Oct 05 '19 at 17:57
  • @Holger I agree, its good, for super-sonic coding of simple modules, on small programs. Bad for: a) self explanatory code; b) backwards-compatibility; c) debug using stack trace. – Alexey Apr 29 '20 at 10:16
  • 3
    @Alexey **a)** try-with-resource is part of the language for a decade and should be understood by Java developers without further explanation. It’s not different to any other high level language construct, e.g. for-each loop or string concatenation, or using OOP concepts. Verbosity doesn’t imply “self explanatory”. **b)** Backward compatibility to what? You are asking a question about Java 11 but insist on compatibility to Java 6 or even older? **c)** in which regard does using try-with-resource hinder debugging? – Holger Apr 29 '20 at 10:36

3 Answers3

20

I've tried your application with both openjdk 8 and 11 on MacOS and it does not work with both. I think you need to look at [1] and [2] in order to understand how getResourceAsStream works.

TLDR:

  1. If the path is absolute (i.e. starts with a slash - /), then class.getResourceAsStream() searches in the provided path

  2. If the path is NOT absolute (i.e. does not start with a slash) , then class.getResourceAsStream() searches in a constructed path that corresponds to the package name, where the dots are replaced with slashes

So whether it works or not depends on 2 things:

  1. Is your path absolute or not ?
  2. Is the file located in the same package as the class or not ?

Basically in your exaple as is provided, it can never work if the path is not absolute, because Class.class.getResourceAsStream() will always resolve the path to java/lang/<file>, so your file must be in a system package. So instead you must use <MyClass>.class.getResourceAsStream() or alternatively use an absolute path

[1] https://docs.oracle.com/javase/7/docs/api/java/lang/ClassLoader.html#getResource(java.lang.String)

[2] https://docs.oracle.com/javase/7/docs/api/java/lang/Class.html#getResource%28java.lang.String%29


Update

Since Java SE 9, invoking getResourceXXX on a class in a named module will only locate the resource in that module, it will not search the class path as it did in previous release. So when you use Class.class.getResourceAsStream() it will attempt to locate the resource in module containing java.lang.Class, which is the java.base module. Obviously your resource is not in that module, so it returns null.

You have to make java 9+ search for the file in your module, which most probably is an "unnamed module". You can do that by changing Class to any class defined in your module in order to make java use the proper class loader.

Svetlin Zarev
  • 14,713
  • 4
  • 53
  • 82
  • I'm sorry if I am confused you. Yes, the path is absolute. TXT file in the package folder with Java class files and included in the Build Path. File Path: /configuration/options.txt – Alexey Jan 03 '19 at 12:39
  • Ok, then it's because of the java modules. I'll update my answer – Svetlin Zarev Jan 03 '19 at 12:41
  • Thanks for explanation. Changing from Class.class.getResourceAsStream(file) to TextTools.class.getResourceAsStream(file) solve Error. – Alexey Jan 03 '19 at 12:59
  • 1
    Ugh, this is a strange behavior with modules in Java 9 and later. Changing to `getClass().getResourceXXX` worked for me. – Christopher Jul 26 '19 at 23:22
  • The update here is the correct answer. We were using Classloader.class.getResourceAsStream() and it didn't work. We changed it to a class we wrote in our packages and it worked fine. Bingo! – markthegrea Oct 04 '19 at 20:12
  • 1
    @markthegrea both are correct, it depends on which java version you are using – Svetlin Zarev Oct 05 '19 at 08:52
7

There is another solution using ClassLoader

ClassLoader.getSystemResourceAsStream(file)

loads from all locations. I'm using it to load resources from JAR file which holds just resources and no executable code so the previous trick can't work because there is no SomeClass in the JAR to do SomeClass.class.getResourceAsStream()

I just don't understand the internals and I must change also the path of the file.

I have JAR file with file "my-file.txt" in the root of the JAR archive.

For java 8, this worked

Class.class.getResourceAsStream("/my-file.txt");

For java 11, I must remove the leading / even when the file is in the root path

ClassLoader.getSystemResourceAsStream("my-file.txt");
j123b567
  • 3,110
  • 1
  • 23
  • 32
-1

I ran into that same problem. The solution is to use the ClassPathResource in Java versions later than 8.

ClassPathResource resource = new ClassPathResource("/path/to/file.txt");
  • v1 as file:
URL url = resource.getURL();
  • v2 as Stream:
in = resource.getInputStream();
Gaël J
  • 11,274
  • 4
  • 17
  • 32