2

I was testing some code to read file available in classpath, but i ended up with some interesting thing related to printStackTrace() method of the Throwable class.

here is the code:

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URISyntaxException;
import java.net.URL;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;

public class Main {

    public static void main(String[] args) throws IOException, URISyntaxException {
        try {
            System.out.println("before-test1");
            test1(); // will throw exception
            System.out.println("after-test1");
        } catch (Exception e) {
            System.out.println("entering catch-1");
            e.printStackTrace();
            System.out.println("exiting catch-1");
        }

        try {
            System.out.println("before-test2");
            test2();
            System.out.println("after-test2");
        } catch (Exception e) {
            e.printStackTrace();
        }

        try {
            System.out.println("before-test3");
            test3();
            System.out.println("after-test3");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static void test1() throws IOException {
        String path = "classpath:/test.txt";

        File file = new File(path);

        String content = FileUtils.readFileToString(file, "UTF-8");

        System.out.println("content = " + content);
    }

    private static void test2() throws IOException {
        String path = "/test.txt";
        InputStream in = Main.class.getResourceAsStream(path);

        String content = IOUtils.toString(in);
        System.out.println("content = " + content);
    }

    private static void test3() throws IOException, URISyntaxException {
        String path = "/test.txt";

        URL url = Main.class.getResource(path);

        File file = new File(url.toURI());

        String content = FileUtils.readFileToString(file, "UTF-8");

        System.out.println("content = " + content);
    }

}

the content of the test.txt is only one line as per below:

anil bharadia

now the expected output of this program is like below:

before-test1
entering catch-1
java.io.FileNotFoundException: classpath:\test.txt (The filename, directory name, or volume label syntax is incorrect)
    at java.io.FileInputStream.open(Native Method)
    at java.io.FileInputStream.<init>(Unknown Source)
    at org.apache.commons.io.FileUtils.readFileToString(FileUtils.java:874)
    at Main.test1(Main.java:50)
    at Main.main(Main.java:16)
exiting catch-1
before-test2
content = anil bharadia
after-test2
before-test3
content = anil bharadia
after-test3

and i have got the same output as above when I ran this class for the first time.

Then after I ran the same class again and i have got the following output:

before-test1
entering catch-1
exiting catch-1
before-test2
content = anil bharadia
after-test2
before-test3
java.io.FileNotFoundException: classpath:\test.txt (The filename, directory name, or volume label syntax is incorrect)
    at java.io.FileInputStream.open(Native Method)
    at java.io.FileInputStream.<init>(Unknown Source)
    at org.apache.commons.io.FileUtils.readFileToString(FileUtils.java:874)
    at Main.test1(Main.java:50)
    at Main.main(Main.java:16)
content = anil bharadia
after-test3

in above output the stack trace is printed after the execution of the catch block from which its called and also after the test2()'s execution is over.

so that made me think that printStackTrace() method is asynchronous somehow.

i tried to look into the source of the printStackTrace() and found the following code that didn't help me :

public void printStackTrace(PrintStream s) {
        synchronized (s) {
            s.println(this);
            StackTraceElement[] trace = getOurStackTrace();
            for (int i=0; i < trace.length; i++)
                s.println("\tat " + trace[i]);

            Throwable ourCause = getCause();
            if (ourCause != null)
                ourCause.printStackTraceAsCause(s, trace);
        }
    }

I tried to google it but not found any explanation.

And when i tried to run the same class again and again, in many cases i have also found the following output :

java.io.FileNotFoundException: classpath:\test.txt (The filename, directory name, or volume label syntax is incorrect)
    at java.io.FileInputStream.open(Native Method)
    at java.io.FileInputStream.<init>(Unknown Source)
    at org.apache.commons.io.FileUtils.readFileToString(FileUtils.java:874)
    at Main.test1(Main.java:50)
    at Main.main(Main.java:16)
before-test1
entering catch-1
exiting catch-1
before-test2
content = anil bharadia
after-test2
before-test3
content = anil bharadia
after-test3

this made me think even more , like how the stack trace got printed before calling the test1() method. that should not be possible.

Anil Bharadia
  • 2,760
  • 6
  • 34
  • 46

3 Answers3

5

The stacktrace prints go to a different stream - they're printed to the error stream, rather than the standard out stream. This is why sometimes they'll be displayed in a different order.

Evan Knowles
  • 7,426
  • 2
  • 37
  • 71
1

System.out.println("after-test3"); write the input to out stream, and printStackTrace() method writes the input to System.err stream

public void printStackTrace() {
    printStackTrace(System.err);
}
Abimaran Kugathasan
  • 31,165
  • 11
  • 75
  • 105
1

Error goes to System.err stream. Normal output goes to System.out stream.

And these streams are written by different threads, so you can also expect different sequence in output if you run again.

To get desired output, you need to change as :

e.printStacktrace(System.out);

This will redirect output to System.out stream.

codingenious
  • 8,385
  • 12
  • 60
  • 90