3

I am new to JAVA and could not find yet a way to write a "hello world"-application that works correctly without any change on all platforms and also detects and handles output errors.

In particular, I would like to port the following portable C program to JAVA:

"Makefile":

.POSIX:

TARGETS = hello

.PHONY: all clean

all: $(TARGETS)

clean:
    -rm $(TARGETS)

"hello.c":

#include <stdio.h>
#include <stdlib.h>

int main(void) {
   if (puts("Hello, world!") >= 0 && fflush(0) == 0) return EXIT_SUCCESS;
   (void)fputs("An output error occurred.\n", stderr);
   return EXIT_FAILURE;
}

This program is intended to work like this:

$ make
c99 -O    hello.c   -o hello

$ ./hello
Hello, world!

$ ./hello > /dev/full
An output error occurred.

Unfortunately, I was not yet able to implement the same in JAVA.

Neither System.out.println() nor System.out.flush() seem to check for I/O errors, or maybe I was just not able to catch them.

I already tried to catch exceptions of type IOException, but the compiler tells me that

error: exception IOException is never thrown in body of corresponding try statement

so this is obviously not the right way to do it.

But even if I could find a way to catch output errors, I still had no clue what exit code to use portably, because JAVA does not seem to define something corresponding to C's EXIT_FAILURE.

I find that hard to believe, considering that JAVA is always praised as a highly portable system for application development.

It would be quite shocking if the correct and portable implementation of even a simple "hello world" already exceeded the capabilities of this "write once, run everywhere" platform.

So please help me creating a JAVA implementation that behaves exactly like the C version and is still portable.

Thanks to khelwood and others, I managed to create the following JAVA implementation which works best so far:

public class HelloWorld {
   private static class OutputError extends java.io.IOException { }

   private static void println(String s) throws OutputError {
      System.out.println(s);
      if (System.out.checkError()) throw new OutputError();
   }

   private static void flush() throws OutputError {
      System.out.flush();
      if (System.out.checkError()) throw new OutputError();
   }

   private static int getExitFailureCode() {
      try {
         return (new java.lang.ProcessBuilder("/bin/false")).start().waitFor();
      } catch (Exception dummy) {
         return 99;
      }
   }

   public static void main(String[] args) {
      try {
         println("Hello, world!");
         flush();
      } catch (OutputError dummy) {
         System.err.println("An output error occurred.");
         System.exit(getExitFailureCode());
      }
   }
}

However, this is not truly portable because it will only work reliably on POSIX systems which provide a "false" utility.

Otherwise it will revert to the arbitrary return code of 99, which is obviously dangerous because some platform could, for instance, define all negative values as failure codes and all other values as success codes.

  • 3
    Did you try [`System.out.checkError()`](https://docs.oracle.com/javase/7/docs/api/java/io/PrintStream.html#checkError())? – khelwood Mar 04 '19 at 12:42
  • @khelwood Not yet. But as the documentation says "The internal error state is set to true when the underlying output stream throws an IOException" and the JAVA compiler insists no such exception is thrown, it seems doubtful that this could help. I will try it, though. – Guenther Brunthaler Mar 04 '19 at 12:51
  • 1
    No, the compiler objects because IOException is never thrown _out of_ the methods you are calling. That doesn't mean it can't happen _inside_ the method (directly or indirectly) and be handled internally. – khelwood Mar 04 '19 at 12:52

2 Answers2

3

System.out is a PrintStream.

The docs for PrintStream say:

Unlike other output streams, a PrintStream never throws an IOException; instead, exceptional situations merely set an internal flag that can be tested via the checkError method.

You can check System.out.checkError() to see if an error has occurred.

If you want your program to exit with a nonzero status, you can use System.exit.

E.g.

if (System.out.checkError()) {
    System.err.println("An output error occurred.");
    System.exit(-1);
}

See also Is there a replacement for EXIT_SUCCESS and EXIT_FAILURE in Java?

khelwood
  • 55,782
  • 14
  • 81
  • 108
  • This means PrintStream is an exception to the documented way checkError() is supposed to work, and would therefore solve the first half of my question. Thanks for that! But I still need a way to portably get the return code for System.exit(). Just using "1" as a constant does not help, because not all platforms consider a return code of 1 as an error. I need something symbolic. – Guenther Brunthaler Mar 04 '19 at 12:55
  • @GuentherBrunthaler I don't think PrintStream is an exception the documented way checkError() is supposed to work. I think you misunderstood what the checkError() documentation says. – khelwood Mar 04 '19 at 13:03
  • You might be right. It says "[...] error state is set to true when the underlying output stream throws an IOException [...] and when the setError method is invoked". So it is possible that PrintStream.println() does invoke setError() explicitly without throwing an exception. However, the documentation of println() does not mention this. – Guenther Brunthaler Mar 04 '19 at 13:09
  • @GuentherBrunthaler It says that when the _underlying_ stream throws an exception, that exception is caught, setError is invoked, and the exception is (otherwise) suppressed. So the PrintStream method doesn't throw an IOException, even when the _underlying_ stream does. – khelwood Mar 04 '19 at 13:35
  • Accepting your answer. Summing up, the answer to my question is "no". There is no portable way to obtain EXIT_FAILURE in the current JAVA framework. Checking for output errors is possible but required a lot of work, because essentially most PrintStream methods need to be wrapped in substitute functions which use checkError() and throw exceptions (or deal with the errors directly somehow). Combining this, there is NO easy way to write a correct and portable "Hello world" in JAVA. – Guenther Brunthaler Mar 05 '19 at 14:29
2

The object System.out is an instance of java.io.PrintStream. You can use checkError() to check for possible errors.

Pablo
  • 3,655
  • 2
  • 30
  • 44
  • This means that I would need to write a wrapper around System.out.println() and all similar functions, unless I want to call checkError() after every call to any of those functions. This is quite annoying! What does JAVA have exceptions for, if it is not using them in the most widely used console output functions? Also, I still need to know where to get a proper platform-specific error exit code for System.exit(). – Guenther Brunthaler Mar 04 '19 at 13:04