JDK 1.7 Throwable `addSuppressed()` method
Asked Answered
T

2

17

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.

Tutty answered 20/1, 2012 at 19:14 Comment(5)
what didn't you understand from the javadoc of the method in question? i'd never seen the addSuppressed() method before, but a quick reading of the javadoc made it clear what its purpose is. (hint, in your example use cases it would not make sense).Claudicant
Well, javadoc wasn't enough. Why downvote the question?Tutty
why wasn't it enough, what didn't you understand?Claudicant
I found this #7860637 as very clear explanation on how try-with-resource feature is actually implemented.Tutty
@jtahlborn, let the votes decide on the usefulness of this question and answer. Just to remind you : blog.stackoverflow.com/2011/07/…Charactery
T
20

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.

Tutty answered 21/1, 2012 at 17:29 Comment(2)
I found this #7860637 as very clear explanation on how try-with-resource feature is actually implemented.Tutty
Is the catch (Exception e) { throw e; } really necessary in your first code?Stipend
K
1

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.

Kaseykasha answered 20/1, 2012 at 19:39 Comment(5)
by basically reposting what is in the javadoc, you are encouraging people to post first and think later/never.Claudicant
Well, I didn't know about try-with-resource new feature in JDK 1.7. Starting to read about it. Thanks.Tutty
@jtahlborn, how should I figure about that I supposed to read printStackTrace() method docs.oracle.com/javase/7/docs/api/java/lang/… ? I am not interesting in the stack trace stuff right now. As for me, there is no connection between issues.Tutty
@Tutty - huh? the only javadoc i read was docs.oracle.com/javase/7/docs/api/java/lang/… and that answered all my questions (and is basically what this answer contains).Claudicant
jtahlborn, stackoverflow exists because no one wants to RTFM.Kaseykasha

© 2022 - 2024 — McMap. All rights reserved.