Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JDK 1.7 Throwable `addSuppressed()` method

Well, I get through related questions, I read the source code of JDK 1.7, but I don't find the answer.

In this question I want to completely ignore fillInStackTrace.

As of JDK 1.4 initCause() method was added. For example, when you use core reflection to invoke the method you receives InvocationTargetException with the cause that have target exception in it.

When I saw this feature I started to use it also in a scenario like this

    try {
        //contains some code that can throw new IOException();
    }
    catch(IOException e){
        throw new RuntimeException(e);
    }

So, I catch an exception, I am not ready to deal with it here and I rethrow new exception where I have original exception as the cause. In some scenarious not RuntimeException, but my custom exception is used, so sometimes I also call to e.getCause() in order to properly handle this exception in the outer block.

This is situation in pre JDK 1.7. Why and when should I use addSuppressed()? Should I change the code above to

    try {
        //contains some code that can throw new IOException();
    }
    catch(IOException e){
        RuntimeException re= new RuntimeException(e.getMessage());
        re.addSuppressed(e);
        throw re;
    }

And as a bonus question why doesn't addSuppressed() return Throwable as initCause() does to allow throw (RuntimeException)new RuntimeException().initCause(e);? For example why can't I do?:

    try {
        //contains some code that can throw new IOException();
    }
    catch(IOException e){
        throw (RuntimeException)new RuntimeException(e.getMessage()).addSuppressed(e);
    }

I extracted the answer to a separate post.

like image 642
alexsmail Avatar asked Jan 20 '12 19:01

alexsmail


People also ask

What is addSuppressed?

The addSuppressed?(Throwable exception) method of a Throwable class used to append the exception to the exceptions that were suppressed in order to deliver this exception. This method is a thread-safe method. This method is typically called by try-catch clause.

What is throwable exception in Java?

The Throwable class is the superclass of all errors and exceptions in the Java language. Only objects that are instances of this class (or one of its subclasses) are thrown by the Java Virtual Machine or can be thrown by the Java throw statement.

How do you suppress an exception in Java?

Any exception originally thrown in the try block is then suppressed. Starting with Java 7, we can now use two methods on the Throwable class to handle our suppressed exceptions: addSuppressed and getSuppressed. We should note that the try-with-resources construct was also introduced in Java 7.

What is a throwable Error?

public class Error extends Throwable. An Error is a subclass of Throwable that indicates serious problems that a reasonable application should not try to catch. Most such errors are abnormal conditions.


2 Answers

In general, Throwable addSuppressed() method should be used when in some way we have parallel execution which can produce exception, that where suppressed. I found 2 examples;

  • Try-with-resource block (try-finally block) when the calling code would see the original exception (in the try or catch block) and the exception that happened in the finally block.

  • batch jobs (bulk operations) when we should proceed to the next item regardless whether the operation on the current item succeeded or not

Before getting to the details, as @sarelbotha stated, in my case I just have to keep wrapping the original exception as the cause of my new exception.

Default behaviour in try-finally block, where we have 2 exceptions, that the original exception is suppressed and we see only exception from finally block. If we use finally block on order to close the resource than we really want to see the original exception, but optionally we want to see also exceptions from the finally block, that closed our resource and fails.

As of release 7, the platform supports the notion of suppressed exceptions (in conjunction with the try-with-resources statement). Any exceptions that were suppressed in order to deliver an exception are printed out beneath the stack trace.

http://docs.oracle.com/javase/7/docs/api/java/lang/Throwable.html#printStackTrace%28%29

First one should read about try-with-resource new feature. You can read it here http://www.baptiste-wicht.com/2010/08/java-7-try-with-resources-statement/ for example or here What is the Java 7 try-with-resources bytecode equivalent using try-catch-finally?. In short, you can have 2 Throwable in parallel in some sense, typically from you try block and from your finally block. An old try-catch semantic will return exception from the finally block whule suppressed exception from the try block (or rethrowing exception from the catch block). A new try-with-resource feature enables you to get both exception. Even more, you will receive original exception where exception from the finally block will be suppressed

Note that when one exception causes another exception, the first exception is usually caught and then the second exception is thrown in response. In other words, there is a causal connection between the two exceptions. In contrast, there are situations where two independent exceptions can be thrown in sibling code blocks, in particular in the try block of a try-with-resources statement and the compiler-generated finally block which closes the resource. In these situations, only one of the thrown exceptions can be propagated. In the try-with-resources statement, when there are two such exceptions, the exception originating from the try block is propagated and the exception from the finally block is added to the list of exceptions suppressed by the exception from the try block. As an exception unwinds the stack, it can accumulate multiple suppressed exceptions.

Example:

    public class TestClass {
static class ResourceA implements AutoCloseable{
      public void read() throws Exception{
        throw new Exception("ResourceA read exception");
      }
      @Override
      public void close() throws Exception {
        throw new Exception("ResourceA close exception");
      }
    };

static class ResourceB implements AutoCloseable{
      public void read() throws Exception{
        throw new Exception("ResourceB read exception");
      }
      @Override
      public void close() throws Exception {
        throw new Exception("ResourceB close exception");
      }
    };

    //a test method
    public static void test() throws Exception{
      try (ResourceA a = new ResourceA();
           //ResourceB b = new ResourceB()
              ) {
        a.read();
        //b.read();
      } catch (Exception e) {
        throw e;
      }
    }

    public static void main(String[] args)  throws Exception {
        test();
    }

}

Output will be the following:

Exception in thread "main" java.lang.Exception: ResourceA read exception
at TestClass$ResourceA.read(TestClass.java:6)
at TestClass.test(TestClass.java:29)
at TestClass.main(TestClass.java:39)
Suppressed: java.lang.Exception: ResourceA close exception
    at TestClass$ResourceA.close(TestClass.java:10)
    at TestClass.test(TestClass.java:31)
    ... 1 more

batch jobs (bulk operations). Well, I found some usage of this method outside try-with-resources. Below, is source code from the java.net.URLClassLoader.close

 public void close() throws IOException {
    SecurityManager security = System.getSecurityManager();
    if (security != null) {
        security.checkPermission(new RuntimePermission("closeClassLoader"));
    }
    List<IOException> errors = ucp.closeLoaders();
    // now close any remaining streams.
    synchronized (closeables) {
        Set<Closeable> keys = closeables.keySet();
        for (Closeable c : keys) {
            try {
                c.close();
            } catch (IOException ioex) {
                errors.add(ioex);
            }
        }
        closeables.clear();
    }
    if (errors.isEmpty()) {
        return;
    }
    IOException firstex = errors.remove(0);
    // Suppress any remaining exceptions
    for (IOException error: errors) {
        **firstex.addSuppressed(error);**
    }
    throw firstex;
}

In general, such approach can be used in batch jobs (bulk operations), when we should proceed to the next item (closing next open streams as is in this example) regardless whether the operation on the current item succeeded or not. In such a way, we have as I stated before, in some way parallel execution which can produce exception, that where suppressed. In such cases we should use the approach above to throw on exception with remaining suppressed exception in it.

like image 194
alexsmail Avatar answered Sep 19 '22 06:09

alexsmail


Suppressed exceptions would be saved if code executing in a finally block throws an exception. It's an exception that happened that you probably don't care about. In Java 6 such an exception in a finally block would become the only exception your calling code would see but with the new try-with-resource block your calling code would see the original exception and the exception that happened in the virtual finally block would be in getSuppressed().

In your case just keep wrapping the original exception as the cause of your new exception.

like image 37
Sarel Botha Avatar answered Sep 20 '22 06:09

Sarel Botha