Index: lucene/src/java/org/apache/lucene/util/IOUtils.java =================================================================== --- lucene/src/java/org/apache/lucene/util/IOUtils.java (revision 1150091) +++ lucene/src/java/org/apache/lucene/util/IOUtils.java (working copy) @@ -19,8 +19,11 @@ import java.io.Closeable; import java.io.IOException; +import java.lang.reflect.Method; -/** @lucene.internal */ +/** This class emulates the new Java 7 "Try-With-Resources" statement. + * Remove once Lucene is on Java 7. + * @lucene.internal */ public final class IOUtils { private IOUtils() {} // no instance @@ -55,6 +58,7 @@ object.close(); } } catch (Throwable t) { + addSuppressed((priorException == null) ? th : priorException, t); if (th == null) { th = t; } @@ -81,6 +85,7 @@ object.close(); } } catch (Throwable t) { + addSuppressed((priorException == null) ? th : priorException, t); if (th == null) { th = t; } @@ -118,6 +123,7 @@ object.close(); } } catch (Throwable t) { + addSuppressed(th, t); if (th == null) th = t; } @@ -143,6 +149,7 @@ object.close(); } } catch (Throwable t) { + addSuppressed(th, t); if (th == null) th = t; } @@ -155,5 +162,31 @@ throw new RuntimeException(th); } } + + /** This reflected {@link Method} is {@code null} before Java 7 */ + private static final Method SUPPRESS_METHOD; + static { + Method m; + try { + m = Throwable.class.getMethod("addSuppressed", Throwable.class); + } catch (Exception e) { + m = null; + } + SUPPRESS_METHOD = m; + } + /** adds a Throwable to the list of suppressed Exceptions of the first Throwable (if Java 7 is detected) + * @param exception this exception should get the suppressed one added + * @param suppressed the suppressed exception + */ + private static final void addSuppressed(Throwable exception, Throwable suppressed) { + if (SUPPRESS_METHOD != null && exception != null && suppressed != null) { + try { + SUPPRESS_METHOD.invoke(exception, suppressed); + } catch (Exception e) { + // ignore any exceptions caused by invoking (e.g. security constraints) + } + } + } + } Index: lucene/src/test/org/apache/lucene/util/TestIOUtils.java =================================================================== --- lucene/src/test/org/apache/lucene/util/TestIOUtils.java (revision 0) +++ lucene/src/test/org/apache/lucene/util/TestIOUtils.java (revision 0) @@ -0,0 +1,107 @@ +package org.apache.lucene.util; + +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.io.Closeable; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; + +public class TestIOUtils extends LuceneTestCase { + + static final class BrokenCloseable implements Closeable { + final int i; + + public BrokenCloseable(int i) { + this.i = i; + } + + @Override + public void close() throws IOException { + throw new IOException("TEST-IO-EXCEPTION-" + i); + } + } + + static final class TestException extends Exception { + public TestException() { + super("BASE-EXCEPTION"); + } + } + + public void testSuppressedExceptions() { + boolean isJava7 = true; + try { + // this class only exists in Java 7: + Class.forName("java.lang.AutoCloseable"); + } catch (ClassNotFoundException cnfe) { + isJava7 = false; + } + + if (!isJava7) { + System.err.println("WARNING: TestIOUtils.testSuppressedExceptions: Full test coverage only with Java 7, as suppressed exception recording is not supported before."); + } + + // test with prior exception + try { + final TestException t = new TestException(); + IOUtils.closeSafely(t, new BrokenCloseable(1), new BrokenCloseable(2)); + } catch (TestException e1) { + assertEquals("BASE-EXCEPTION", e1.getMessage()); + final StringWriter sw = new StringWriter(); + final PrintWriter pw = new PrintWriter(sw); + e1.printStackTrace(pw); + pw.flush(); + final String trace = sw.toString(); + if (VERBOSE) { + System.out.println("TestIOUtils.testSuppressedExceptions: Thrown Exception stack trace:"); + System.out.println(trace); + } + if (isJava7) { + assertTrue("Stack trace does not contain first suppressed Exception: " + trace, + trace.contains("java.io.IOException: TEST-IO-EXCEPTION-1")); + assertTrue("Stack trace does not contain second suppressed Exception: " + trace, + trace.contains("java.io.IOException: TEST-IO-EXCEPTION-2")); + } + } catch (IOException e2) { + fail("IOException should not be thrown here"); + } + + // test without prior exception + try { + IOUtils.closeSafely((TestException) null, new BrokenCloseable(1), new BrokenCloseable(2)); + } catch (TestException e1) { + fail("TestException should not be thrown here"); + } catch (IOException e2) { + assertEquals("TEST-IO-EXCEPTION-1", e2.getMessage()); + final StringWriter sw = new StringWriter(); + final PrintWriter pw = new PrintWriter(sw); + e2.printStackTrace(pw); + pw.flush(); + final String trace = sw.toString(); + if (VERBOSE) { + System.out.println("TestIOUtils.testSuppressedExceptions: Thrown Exception stack trace:"); + System.out.println(trace); + } + if (isJava7) { + assertTrue("Stack trace does not contain suppressed Exception: " + trace, + trace.contains("java.io.IOException: TEST-IO-EXCEPTION-2")); + } + } + } + +} Property changes on: lucene\src\test\org\apache\lucene\util\TestIOUtils.java ___________________________________________________________________ Added: svn:keywords + Date Author Id Revision HeadURL Added: svn:eol-style + native