IOUtils.closeSafely should use log suppressed Exceptions in stack trace of
original Exception (a new feature of Java 7)
-----------------------------------------------------------------------------------------------------------------------
Key: LUCENE-3334
URL: https://issues.apache.org/jira/browse/LUCENE-3334
Project: Lucene - Java
Issue Type: Improvement
Components: core/other
Reporter: Uwe Schindler
Assignee: Uwe Schindler
Priority: Minor
Attachments: LUCENE-3334.patch
I was always against Java 6 support, as it brings no really helpful new
features into Lucene. But there are several things that make life easier in
coming Java 7 (hopefully on July 28th, 2011). One of those is simplier
Exception handling and suppression on Closeable, called "Try-With-Resources"
(see http://docs.google.com/View?id=ddv8ts74_3fs7483dp, by the way all Lucene
classes support these semantics in Java 7 automatically, the cool try-code
below would work e.g. for IndexWriter, TokenStreams,...).
We already have this functionality in Lucene since adding the
IOUtils.closeSafely() utility (which can be removed when Java 7 is the minimum
requirement of Lucene - maybe in 10 years):
{code:java}
try (Closeable a = new ...; Closeable b = new ...) {
... use Closeables ...
} catch (Exception e) {
dosomething;
throw e;
}
{code}
This code will close a and b in an autogenerated finally block and supress any
exception. This is identical to our IOUtils.closeSafely:
{code:java}
Exception priorException = null;
Closeable a,b;
try (Closeable a = new ...; Closeable b = new ...) {
a = new ...;
b = new ...
... use Closeables ...
} catch (Exception e) {
priorException = e;
dosomething;
} finally {
IOUtils.closeSafely(priorException, a, b);
}
{code}
So this means we have the same functionality without Java 7, but there is one
thing that makes logging/debugging much nicer:
The above Java 7 code also adds maybe suppressed Exceptions in those Closeables
to the priorException, so when you print the stacktrace, it not only shows the
stacktrace of the original Exception, it also prints all Exceptions that were
suppressed to throw this Exception (all Closeable.close() failures):
{noformat}
org.apache.lucene.util.TestIOUtils$TestException: BASE-EXCEPTION
at
org.apache.lucene.util.TestIOUtils.testSuppressedExceptions(TestIOUtils.java:61)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at
org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
at
org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
at
org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
at
org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
at org.junit.rules.TestWatchman$1.evaluate(TestWatchman.java:48)
at
org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
at
org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:31)
at
org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:76)
at
org.apache.lucene.util.LuceneTestCase$LuceneTestCaseRunner.runChild(LuceneTestCase.java:1486)
at
org.apache.lucene.util.LuceneTestCase$LuceneTestCaseRunner.runChild(LuceneTestCase.java:1404)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
at
org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
at
org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:31)
at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
at junit.framework.JUnit4TestAdapter.run(JUnit4TestAdapter.java:39)
at
org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner.run(JUnitTestRunner.java:420)
at
org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner.launch(JUnitTestRunner.java:911)
at
org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner.main(JUnitTestRunner.java:768)
Suppressed: java.io.IOException: TEST-IO-EXCEPTION-1
at
org.apache.lucene.util.TestIOUtils$BrokenCloseable.close(TestIOUtils.java:36)
at org.apache.lucene.util.IOUtils.closeSafely(IOUtils.java:58)
at
org.apache.lucene.util.TestIOUtils.testSuppressedExceptions(TestIOUtils.java:62)
... 26 more
Suppressed: java.io.IOException: TEST-IO-EXCEPTION-2
at
org.apache.lucene.util.TestIOUtils$BrokenCloseable.close(TestIOUtils.java:36)
at org.apache.lucene.util.IOUtils.closeSafely(IOUtils.java:58)
at
org.apache.lucene.util.TestIOUtils.testSuppressedExceptions(TestIOUtils.java:62)
... 26 more
{noformat}
For this in Java 7 a new method was added to Throwable, that allows logging
such suppressed Exceptions (it is called automatically by the synthetic
bytecode emitted by javac). This patch simply adds this functionality
conditionally to IOUtils, so it "registers" all suppressed Exceptions, if
running on Java 7. This is done by reflection: once it looks for this method in
Throwable.class and if found, it invokes it in closeSafely, so the exceptions
thrown on Closeable.close() don't get lost.
This makes debugging much easier and logs all problems that may occur.
This patch does *not* change functionality or behaviour, it just adds more
nformation to the stack trace in a Java-7-way (similar to the way how Java 1.4
added causes). It works here locally on Java 6 and Java 7, but only Java 7 gets
the additional stack traces. For Java 6 nothing changes. Same for Java 5 (if we
backport to 3.x).
This would be our first Java 7 improvement (a minor one). Next would be NIO2...
- but thats not easy to do with reflection only, so we have to wait 10 years :-)
--
This message is automatically generated by JIRA.
For more information on JIRA, see: http://www.atlassian.com/software/jira
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]