1

I have a method where I need to read a resource file and pass it to another method as an InputStream. The obvious approach

InputStream is = classLoader.getResourceAsStream("filename.pem");

works fine when actually running the application, but in testing it returns an empty InputStream (filled with zeros). I don't think its a problem with the resource path or anything, because when I use a nonsense path like "filex" (filex does not exist), I get an actual null pointer exception and not an empty stream. Also in debugger the complete file path of the empty Stream points to the correct path, where the file actually is stored (default class root).

Furthermore, with the following workaround it works:

            File file = new File(classLoader.getResource("filename.pem").getFile());
            String fileS= new String(Files.readAllBytes(file.toPath()), Charset.defaultCharset());
            InputStream is = classLoader.getResourceAsStream("filename.pem");
            InputStream is2 = new ByteArrayInputStream(fileS.getBytes(StandardCharsets.UTF_8));

In this example is2 has the actual content of the file in the InputStream, while is has an Stream filled with zeros. I can't quite explain that behaviour. I double checked with 'getClass().getClassLoader().getClass()' if we use some modified ClassLoader in the Application, but it is the original one from sun.misc.Launcher$AppClassLoader.

So my questions are:

  • Why does the workaround work but not the classic approach?
  • Why does it fail only in test class?
  • Is there a way to make it work? The workaround is more lines of code and also need to catch IOException because of the Files.readAllBytes() call.

The only idea I had left: the encoding or charset has something to do with it. But to my knowledge there is no parameter in getResourceAsStream() like Charset or StandardCharsets.

Retros_x
  • 11
  • 3

1 Answers1

0

If you open a resource file as a stream, you end up with a BufferedInputStream around a FileInputStream.

The call chain is as follows:

  • java.lang.ClassLoader#getResource returns an URL
  • url.openStream() is called
  • this first opens the Stream; sun.net.www.protocol.file.Handler#createFileURLConnection
  • then the Stream is connected: is = new BufferedInputStream(new FileInputStream(filename)); in sun.net.www.protocol.file.FileURLConnection#connect
  • finally you get this is back as InputStream

What you're seeing is the empty internal buffer of the BufferedInputStream, which will be used as soon as you start reading from the InputStream. See is FileInputStream not buffered and why BufferedInputStream is faster?

If you for example read from the InputStream with all zero's, you will see it does actually contain data:

Scanner scanner = new Scanner(inputStream, StandardCharsets.UTF_8.name());
String firstLine = scanner.next();

From https://www.baeldung.com/convert-input-stream-to-string

Your workaround works, because after you've located the file from the resource URL you actually start reading it directly.

So what might be failing in your test; are you not trying to read from the stream in your testcase? How are you using/validating if this inputstream is correct in your test vs the real application? There might be the problem.

slindenau
  • 1,091
  • 2
  • 11
  • 18