Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
569 views
in Technique[技术] by (71.8m points)

exception - 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.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

2.1m questions

2.1m answers

60 comments

57.0k users

...