Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generating fatal error in Java

Suppose we are writing a Java library, which provides some I/O ulitity functions, for example, a convenient method to read text files as Strings:

public class StringReader {

private static final Logger log = LoggerFactory.getLog(StringReader.class);

/**
 * Returns the contents of file <b>fileName</b> as String.
 * @param fileName file name to read
 * @return null on IO error
 */
public static String readString(String fileName) {
    FileInputStream fis = null;
    try {
        fis = new FileInputStream(fileName);
        byte[] data = new byte[fis.available()];
        fis.read(data);
        return new String(data, "ISO-8859-1"); // may throw UnsupportedEncodingException!
    } catch (IOException e) {
        log.error("unable to read file", e);
    } catch (UnsupportedEncodingException e) {
        log.fatal("JRE does not support ISO-8859-1!", e);
        // ???
    } finally {
        closeQuiet(fis);
    }

    return null;
}
}

This code reads a text file into a String using ISO-8859-1 encoding and returns the String to user.

The String(byte[], String) constructor throws an UnsupportedEncodingException when specified encoding is not supported. But, as we know, ISO-8859-1 must be supported by JRE, as said here (see the Standard charsets section).

Hence, we expect the block

catch (UnsupportedEncodingException e) {
    log.fatal("encoding is unsupported", e);
    // ???
}

is never reached if JRE distribution conforms the standard.

But what if it doesn't? How to handle this exception in the most correct way? The question is, how to alert properly about such error?

The suggestions are:

  1. Throw some kind of RuntimeException.
  2. Do not disable the logger in production code, write an exception details in log and ignore it.
  3. Put the assert false here, so it produce AssertionError if user launched VM with -ea.
  4. Throw an AssertionError manually.
  5. Add an UnsupportedEncodingException in method declaration and allow user to choose. Not very convenient, I think.
  6. Call System.exit(1).

Thanks.

like image 586
Stanislav Mamontov Avatar asked Oct 29 '13 07:10

Stanislav Mamontov


People also ask

What are fatal errors in Java?

Fatal errors are errors such as native memory exhaustion, memory access errors, or explicit signals directed to the process.

How do I create a fatal error?

As I've noted in another place, you can create a fatal error via: if($isTestMode==1){ eval("noSuchFunction();"); } This method will always work.

How do you create errors in Java?

Throwing an exception is as simple as using the "throw" statement. You then specify the Exception object you wish to throw. Every Exception includes a message which is a human-readable error description. It can often be related to problems with user input, server, backend, etc.

How do you handle a fatal exception in Java?

You can just specify throws Exception for the main method to let the console receive the stack traces. So in the end: use throws in the signature of your methods instead of catching exceptions before you want to handle them. Save this answer.


1 Answers

But what if it doesn't?

Then you're in a really bad situation, and you should probably get out of it as quickly as possible. When a JRE is violating its own promises, what would you want to depend on?

I'd feel happy using AssertionError in this case.

It's important to note that not all unchecked exceptions are treated equally - it's not unusual for code to catch Exception at the top level of the stack, log an error and then keep going... if you just throw RuntimeException, that will be caught by such a scheme. AssertionError would only be caught if the catch block specified Throwable (or specifically Error or AssertionError, but that's much rarer to see). Given how impossible this should be, I think it's reasonable to abort really hard.

Also note that in Java 7, you can use StandardCharsets.ISO_8859_1 instead of the string name, which is cleaner and removes the problem.

There are other things I'd change about your code, by the way:

  • I would avoid using available() as far as possible. That tells you how many bytes are available right now - it doesn't tell you how long the file is, necessarily.
  • I would definitely not assume that read() will read the whole file in one go. Call read() in a loop, ideally until it says there's no more data.
  • I would personally accept a Charset as a parameter, rather than hard-coding ISO-8859-1. - I would let IOException bubble up from the method rather than just returning null. After all, unless you're really going to check the return value of every call for nullity, you're just going to end up with a NullPointerException instead, which is harder to diagnose than the original IOException.

Alternatively, just use Guava's Files.toString(File, Charset) to start with :) (If you're not already using Guava, now is a good time to start...)

like image 77
Jon Skeet Avatar answered Sep 19 '22 09:09

Jon Skeet