17dd252788645e940eada959bdde927426e2531c9Paul Duffin/*
27dd252788645e940eada959bdde927426e2531c9Paul Duffin * Copyright (C) 2012 The Guava Authors
37dd252788645e940eada959bdde927426e2531c9Paul Duffin *
47dd252788645e940eada959bdde927426e2531c9Paul Duffin * Licensed under the Apache License, Version 2.0 (the "License");
57dd252788645e940eada959bdde927426e2531c9Paul Duffin * you may not use this file except in compliance with the License.
67dd252788645e940eada959bdde927426e2531c9Paul Duffin * You may obtain a copy of the License at
77dd252788645e940eada959bdde927426e2531c9Paul Duffin *
87dd252788645e940eada959bdde927426e2531c9Paul Duffin * http://www.apache.org/licenses/LICENSE-2.0
97dd252788645e940eada959bdde927426e2531c9Paul Duffin *
107dd252788645e940eada959bdde927426e2531c9Paul Duffin * Unless required by applicable law or agreed to in writing, software
117dd252788645e940eada959bdde927426e2531c9Paul Duffin * distributed under the License is distributed on an "AS IS" BASIS,
127dd252788645e940eada959bdde927426e2531c9Paul Duffin * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
137dd252788645e940eada959bdde927426e2531c9Paul Duffin * See the License for the specific language governing permissions and
147dd252788645e940eada959bdde927426e2531c9Paul Duffin * limitations under the License.
157dd252788645e940eada959bdde927426e2531c9Paul Duffin */
167dd252788645e940eada959bdde927426e2531c9Paul Duffin
177dd252788645e940eada959bdde927426e2531c9Paul Duffinpackage com.google.common.io;
187dd252788645e940eada959bdde927426e2531c9Paul Duffin
197dd252788645e940eada959bdde927426e2531c9Paul Duffinimport static com.google.common.base.Preconditions.checkNotNull;
207dd252788645e940eada959bdde927426e2531c9Paul Duffin
217dd252788645e940eada959bdde927426e2531c9Paul Duffinimport com.google.common.annotations.Beta;
227dd252788645e940eada959bdde927426e2531c9Paul Duffinimport com.google.common.annotations.VisibleForTesting;
237dd252788645e940eada959bdde927426e2531c9Paul Duffinimport com.google.common.base.Throwables;
247dd252788645e940eada959bdde927426e2531c9Paul Duffin
257dd252788645e940eada959bdde927426e2531c9Paul Duffinimport java.io.Closeable;
267dd252788645e940eada959bdde927426e2531c9Paul Duffinimport java.io.IOException;
277dd252788645e940eada959bdde927426e2531c9Paul Duffinimport java.lang.reflect.Method;
287dd252788645e940eada959bdde927426e2531c9Paul Duffinimport java.util.LinkedList;
297dd252788645e940eada959bdde927426e2531c9Paul Duffinimport java.util.logging.Level;
307dd252788645e940eada959bdde927426e2531c9Paul Duffin
310888a09821a98ac0680fad765217302858e70fa4Paul Duffinimport javax.annotation.Nullable;
320888a09821a98ac0680fad765217302858e70fa4Paul Duffin
337dd252788645e940eada959bdde927426e2531c9Paul Duffin/**
347dd252788645e940eada959bdde927426e2531c9Paul Duffin * A {@link Closeable} that collects {@code Closeable} resources and closes them all when it is
357dd252788645e940eada959bdde927426e2531c9Paul Duffin * {@linkplain #close closed}. This is intended to approximately emulate the behavior of Java 7's
367dd252788645e940eada959bdde927426e2531c9Paul Duffin * <a href="http://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html">
377dd252788645e940eada959bdde927426e2531c9Paul Duffin * try-with-resources</a> statement in JDK6-compatible code. Running on Java 7, code using this
387dd252788645e940eada959bdde927426e2531c9Paul Duffin * should be approximately equivalent in behavior to the same code written with try-with-resources.
397dd252788645e940eada959bdde927426e2531c9Paul Duffin * Running on Java 6, exceptions that cannot be thrown must be logged rather than being added to the
407dd252788645e940eada959bdde927426e2531c9Paul Duffin * thrown exception as a suppressed exception.
417dd252788645e940eada959bdde927426e2531c9Paul Duffin *
420888a09821a98ac0680fad765217302858e70fa4Paul Duffin * <p>This class is intended to be used in the following pattern:
437dd252788645e940eada959bdde927426e2531c9Paul Duffin *
440888a09821a98ac0680fad765217302858e70fa4Paul Duffin * <pre>   {@code
450888a09821a98ac0680fad765217302858e70fa4Paul Duffin *   Closer closer = Closer.create();
460888a09821a98ac0680fad765217302858e70fa4Paul Duffin *   try {
470888a09821a98ac0680fad765217302858e70fa4Paul Duffin *     InputStream in = closer.register(openInputStream());
480888a09821a98ac0680fad765217302858e70fa4Paul Duffin *     OutputStream out = closer.register(openOutputStream());
490888a09821a98ac0680fad765217302858e70fa4Paul Duffin *     // do stuff
500888a09821a98ac0680fad765217302858e70fa4Paul Duffin *   } catch (Throwable e) {
510888a09821a98ac0680fad765217302858e70fa4Paul Duffin *     // ensure that any checked exception types other than IOException that could be thrown are
520888a09821a98ac0680fad765217302858e70fa4Paul Duffin *     // provided here, e.g. throw closer.rethrow(e, CheckedException.class);
530888a09821a98ac0680fad765217302858e70fa4Paul Duffin *     throw closer.rethrow(e);
540888a09821a98ac0680fad765217302858e70fa4Paul Duffin *   } finally {
550888a09821a98ac0680fad765217302858e70fa4Paul Duffin *     closer.close();
560888a09821a98ac0680fad765217302858e70fa4Paul Duffin *   }}</pre>
577dd252788645e940eada959bdde927426e2531c9Paul Duffin *
587dd252788645e940eada959bdde927426e2531c9Paul Duffin * <p>Note that this try-catch-finally block is not equivalent to a try-catch-finally block using
597dd252788645e940eada959bdde927426e2531c9Paul Duffin * try-with-resources. To get the equivalent of that, you must wrap the above code in <i>another</i>
607dd252788645e940eada959bdde927426e2531c9Paul Duffin * try block in order to catch any exception that may be thrown (including from the call to
617dd252788645e940eada959bdde927426e2531c9Paul Duffin * {@code close()}).
627dd252788645e940eada959bdde927426e2531c9Paul Duffin *
637dd252788645e940eada959bdde927426e2531c9Paul Duffin * <p>This pattern ensures the following:
640888a09821a98ac0680fad765217302858e70fa4Paul Duffin *
657dd252788645e940eada959bdde927426e2531c9Paul Duffin * <ul>
667dd252788645e940eada959bdde927426e2531c9Paul Duffin *   <li>Each {@code Closeable} resource that is successfully registered will be closed later.</li>
677dd252788645e940eada959bdde927426e2531c9Paul Duffin *   <li>If a {@code Throwable} is thrown in the try block, no exceptions that occur when attempting
687dd252788645e940eada959bdde927426e2531c9Paul Duffin *   to close resources will be thrown from the finally block. The throwable from the try block will
697dd252788645e940eada959bdde927426e2531c9Paul Duffin *   be thrown.</li>
707dd252788645e940eada959bdde927426e2531c9Paul Duffin *   <li>If no exceptions or errors were thrown in the try block, the <i>first</i> exception thrown
717dd252788645e940eada959bdde927426e2531c9Paul Duffin *   by an attempt to close a resource will be thrown.</li>
727dd252788645e940eada959bdde927426e2531c9Paul Duffin *   <li>Any exception caught when attempting to close a resource that is <i>not</i> thrown
737dd252788645e940eada959bdde927426e2531c9Paul Duffin *   (because another exception is already being thrown) is <i>suppressed</i>.</li>
747dd252788645e940eada959bdde927426e2531c9Paul Duffin * </ul>
757dd252788645e940eada959bdde927426e2531c9Paul Duffin *
760888a09821a98ac0680fad765217302858e70fa4Paul Duffin * <p>An exception that is suppressed is not thrown. The method of suppression used depends on the
777dd252788645e940eada959bdde927426e2531c9Paul Duffin * version of Java the code is running on:
787dd252788645e940eada959bdde927426e2531c9Paul Duffin *
797dd252788645e940eada959bdde927426e2531c9Paul Duffin * <ul>
807dd252788645e940eada959bdde927426e2531c9Paul Duffin *   <li><b>Java 7+:</b> Exceptions are suppressed by adding them to the exception that <i>will</i>
817dd252788645e940eada959bdde927426e2531c9Paul Duffin *   be thrown using {@code Throwable.addSuppressed(Throwable)}.</li>
827dd252788645e940eada959bdde927426e2531c9Paul Duffin *   <li><b>Java 6:</b> Exceptions are suppressed by logging them instead.</li>
837dd252788645e940eada959bdde927426e2531c9Paul Duffin * </ul>
847dd252788645e940eada959bdde927426e2531c9Paul Duffin *
857dd252788645e940eada959bdde927426e2531c9Paul Duffin * @author Colin Decker
867dd252788645e940eada959bdde927426e2531c9Paul Duffin * @since 14.0
877dd252788645e940eada959bdde927426e2531c9Paul Duffin */
887dd252788645e940eada959bdde927426e2531c9Paul Duffin// Coffee's for {@link Closer closers} only.
897dd252788645e940eada959bdde927426e2531c9Paul Duffin@Beta
907dd252788645e940eada959bdde927426e2531c9Paul Duffinpublic final class Closer implements Closeable {
917dd252788645e940eada959bdde927426e2531c9Paul Duffin
927dd252788645e940eada959bdde927426e2531c9Paul Duffin  /**
937dd252788645e940eada959bdde927426e2531c9Paul Duffin   * The suppressor implementation to use for the current Java version.
947dd252788645e940eada959bdde927426e2531c9Paul Duffin   */
950888a09821a98ac0680fad765217302858e70fa4Paul Duffin  private static final Suppressor SUPPRESSOR = SuppressingSuppressor.isAvailable()
960888a09821a98ac0680fad765217302858e70fa4Paul Duffin      ? SuppressingSuppressor.INSTANCE
977dd252788645e940eada959bdde927426e2531c9Paul Duffin      : LoggingSuppressor.INSTANCE;
987dd252788645e940eada959bdde927426e2531c9Paul Duffin
997dd252788645e940eada959bdde927426e2531c9Paul Duffin  /**
1007dd252788645e940eada959bdde927426e2531c9Paul Duffin   * Creates a new {@link Closer}.
1017dd252788645e940eada959bdde927426e2531c9Paul Duffin   */
1027dd252788645e940eada959bdde927426e2531c9Paul Duffin  public static Closer create() {
1037dd252788645e940eada959bdde927426e2531c9Paul Duffin    return new Closer(SUPPRESSOR);
1047dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
1057dd252788645e940eada959bdde927426e2531c9Paul Duffin
1060888a09821a98ac0680fad765217302858e70fa4Paul Duffin  @VisibleForTesting final Suppressor suppressor;
1077dd252788645e940eada959bdde927426e2531c9Paul Duffin
1087dd252788645e940eada959bdde927426e2531c9Paul Duffin  // only need space for 2 elements in most cases, so try to use the smallest array possible
1097dd252788645e940eada959bdde927426e2531c9Paul Duffin  private final LinkedList<Closeable> stack = new LinkedList<Closeable>();
1107dd252788645e940eada959bdde927426e2531c9Paul Duffin  private Throwable thrown;
1117dd252788645e940eada959bdde927426e2531c9Paul Duffin
1120888a09821a98ac0680fad765217302858e70fa4Paul Duffin  @VisibleForTesting Closer(Suppressor suppressor) {
1137dd252788645e940eada959bdde927426e2531c9Paul Duffin    this.suppressor = checkNotNull(suppressor); // checkNotNull to satisfy null tests
1147dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
1157dd252788645e940eada959bdde927426e2531c9Paul Duffin
1167dd252788645e940eada959bdde927426e2531c9Paul Duffin  /**
1177dd252788645e940eada959bdde927426e2531c9Paul Duffin   * Registers the given {@code closeable} to be closed when this {@code Closer} is
1187dd252788645e940eada959bdde927426e2531c9Paul Duffin   * {@linkplain #close closed}.
1197dd252788645e940eada959bdde927426e2531c9Paul Duffin   *
1207dd252788645e940eada959bdde927426e2531c9Paul Duffin   * @return the given {@code closeable}
1217dd252788645e940eada959bdde927426e2531c9Paul Duffin   */
1227dd252788645e940eada959bdde927426e2531c9Paul Duffin  // close. this word no longer has any meaning to me.
1230888a09821a98ac0680fad765217302858e70fa4Paul Duffin  public <C extends Closeable> C register(@Nullable C closeable) {
1240888a09821a98ac0680fad765217302858e70fa4Paul Duffin    if (closeable != null) {
1250888a09821a98ac0680fad765217302858e70fa4Paul Duffin      stack.addFirst(closeable);
1260888a09821a98ac0680fad765217302858e70fa4Paul Duffin    }
1270888a09821a98ac0680fad765217302858e70fa4Paul Duffin
1287dd252788645e940eada959bdde927426e2531c9Paul Duffin    return closeable;
1297dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
1307dd252788645e940eada959bdde927426e2531c9Paul Duffin
1317dd252788645e940eada959bdde927426e2531c9Paul Duffin  /**
1327dd252788645e940eada959bdde927426e2531c9Paul Duffin   * Stores the given throwable and rethrows it. It will be rethrown as is if it is an
1337dd252788645e940eada959bdde927426e2531c9Paul Duffin   * {@code IOException}, {@code RuntimeException} or {@code Error}. Otherwise, it will be rethrown
1347dd252788645e940eada959bdde927426e2531c9Paul Duffin   * wrapped in a {@code RuntimeException}. <b>Note:</b> Be sure to declare all of the checked
1357dd252788645e940eada959bdde927426e2531c9Paul Duffin   * exception types your try block can throw when calling an overload of this method so as to avoid
1367dd252788645e940eada959bdde927426e2531c9Paul Duffin   * losing the original exception type.
1377dd252788645e940eada959bdde927426e2531c9Paul Duffin   *
1387dd252788645e940eada959bdde927426e2531c9Paul Duffin   * <p>This method always throws, and as such should be called as
1397dd252788645e940eada959bdde927426e2531c9Paul Duffin   * {@code throw closer.rethrow(e);} to ensure the compiler knows that it will throw.
1407dd252788645e940eada959bdde927426e2531c9Paul Duffin   *
1417dd252788645e940eada959bdde927426e2531c9Paul Duffin   * @return this method does not return; it always throws
1427dd252788645e940eada959bdde927426e2531c9Paul Duffin   * @throws IOException when the given throwable is an IOException
1437dd252788645e940eada959bdde927426e2531c9Paul Duffin   */
1447dd252788645e940eada959bdde927426e2531c9Paul Duffin  public RuntimeException rethrow(Throwable e) throws IOException {
1450888a09821a98ac0680fad765217302858e70fa4Paul Duffin    checkNotNull(e);
1467dd252788645e940eada959bdde927426e2531c9Paul Duffin    thrown = e;
1477dd252788645e940eada959bdde927426e2531c9Paul Duffin    Throwables.propagateIfPossible(e, IOException.class);
1480888a09821a98ac0680fad765217302858e70fa4Paul Duffin    throw new RuntimeException(e);
1497dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
1507dd252788645e940eada959bdde927426e2531c9Paul Duffin
1517dd252788645e940eada959bdde927426e2531c9Paul Duffin  /**
1527dd252788645e940eada959bdde927426e2531c9Paul Duffin   * Stores the given throwable and rethrows it. It will be rethrown as is if it is an
1537dd252788645e940eada959bdde927426e2531c9Paul Duffin   * {@code IOException}, {@code RuntimeException}, {@code Error} or a checked exception of the
1547dd252788645e940eada959bdde927426e2531c9Paul Duffin   * given type. Otherwise, it will be rethrown wrapped in a {@code RuntimeException}. <b>Note:</b>
1557dd252788645e940eada959bdde927426e2531c9Paul Duffin   * Be sure to declare all of the checked exception types your try block can throw when calling an
1567dd252788645e940eada959bdde927426e2531c9Paul Duffin   * overload of this method so as to avoid losing the original exception type.
1577dd252788645e940eada959bdde927426e2531c9Paul Duffin   *
1587dd252788645e940eada959bdde927426e2531c9Paul Duffin   * <p>This method always throws, and as such should be called as
1597dd252788645e940eada959bdde927426e2531c9Paul Duffin   * {@code throw closer.rethrow(e, ...);} to ensure the compiler knows that it will throw.
1607dd252788645e940eada959bdde927426e2531c9Paul Duffin   *
1617dd252788645e940eada959bdde927426e2531c9Paul Duffin   * @return this method does not return; it always throws
1627dd252788645e940eada959bdde927426e2531c9Paul Duffin   * @throws IOException when the given throwable is an IOException
1637dd252788645e940eada959bdde927426e2531c9Paul Duffin   * @throws X when the given throwable is of the declared type X
1647dd252788645e940eada959bdde927426e2531c9Paul Duffin   */
1650888a09821a98ac0680fad765217302858e70fa4Paul Duffin  public <X extends Exception> RuntimeException rethrow(Throwable e,
1660888a09821a98ac0680fad765217302858e70fa4Paul Duffin      Class<X> declaredType) throws IOException, X {
1670888a09821a98ac0680fad765217302858e70fa4Paul Duffin    checkNotNull(e);
1687dd252788645e940eada959bdde927426e2531c9Paul Duffin    thrown = e;
1697dd252788645e940eada959bdde927426e2531c9Paul Duffin    Throwables.propagateIfPossible(e, IOException.class);
1707dd252788645e940eada959bdde927426e2531c9Paul Duffin    Throwables.propagateIfPossible(e, declaredType);
1710888a09821a98ac0680fad765217302858e70fa4Paul Duffin    throw new RuntimeException(e);
1727dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
1737dd252788645e940eada959bdde927426e2531c9Paul Duffin
1747dd252788645e940eada959bdde927426e2531c9Paul Duffin  /**
1757dd252788645e940eada959bdde927426e2531c9Paul Duffin   * Stores the given throwable and rethrows it. It will be rethrown as is if it is an
1767dd252788645e940eada959bdde927426e2531c9Paul Duffin   * {@code IOException}, {@code RuntimeException}, {@code Error} or a checked exception of either
1777dd252788645e940eada959bdde927426e2531c9Paul Duffin   * of the given types. Otherwise, it will be rethrown wrapped in a {@code RuntimeException}.
1787dd252788645e940eada959bdde927426e2531c9Paul Duffin   * <b>Note:</b> Be sure to declare all of the checked exception types your try block can throw
1797dd252788645e940eada959bdde927426e2531c9Paul Duffin   * when calling an overload of this method so as to avoid losing the original exception type.
1807dd252788645e940eada959bdde927426e2531c9Paul Duffin   *
1817dd252788645e940eada959bdde927426e2531c9Paul Duffin   * <p>This method always throws, and as such should be called as
1827dd252788645e940eada959bdde927426e2531c9Paul Duffin   * {@code throw closer.rethrow(e, ...);} to ensure the compiler knows that it will throw.
1837dd252788645e940eada959bdde927426e2531c9Paul Duffin   *
1847dd252788645e940eada959bdde927426e2531c9Paul Duffin   * @return this method does not return; it always throws
1857dd252788645e940eada959bdde927426e2531c9Paul Duffin   * @throws IOException when the given throwable is an IOException
1867dd252788645e940eada959bdde927426e2531c9Paul Duffin   * @throws X1 when the given throwable is of the declared type X1
1877dd252788645e940eada959bdde927426e2531c9Paul Duffin   * @throws X2 when the given throwable is of the declared type X2
1887dd252788645e940eada959bdde927426e2531c9Paul Duffin   */
1897dd252788645e940eada959bdde927426e2531c9Paul Duffin  public <X1 extends Exception, X2 extends Exception> RuntimeException rethrow(
1907dd252788645e940eada959bdde927426e2531c9Paul Duffin      Throwable e, Class<X1> declaredType1, Class<X2> declaredType2) throws IOException, X1, X2 {
1910888a09821a98ac0680fad765217302858e70fa4Paul Duffin    checkNotNull(e);
1927dd252788645e940eada959bdde927426e2531c9Paul Duffin    thrown = e;
1937dd252788645e940eada959bdde927426e2531c9Paul Duffin    Throwables.propagateIfPossible(e, IOException.class);
1947dd252788645e940eada959bdde927426e2531c9Paul Duffin    Throwables.propagateIfPossible(e, declaredType1, declaredType2);
1950888a09821a98ac0680fad765217302858e70fa4Paul Duffin    throw new RuntimeException(e);
1967dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
1977dd252788645e940eada959bdde927426e2531c9Paul Duffin
1987dd252788645e940eada959bdde927426e2531c9Paul Duffin  /**
1997dd252788645e940eada959bdde927426e2531c9Paul Duffin   * Closes all {@code Closeable} instances that have been added to this {@code Closer}. If an
2007dd252788645e940eada959bdde927426e2531c9Paul Duffin   * exception was thrown in the try block and passed to one of the {@code exceptionThrown} methods,
2017dd252788645e940eada959bdde927426e2531c9Paul Duffin   * any exceptions thrown when attempting to close a closeable will be suppressed. Otherwise, the
2027dd252788645e940eada959bdde927426e2531c9Paul Duffin   * <i>first</i> exception to be thrown from an attempt to close a closeable will be thrown and any
2037dd252788645e940eada959bdde927426e2531c9Paul Duffin   * additional exceptions that are thrown after that will be suppressed.
2047dd252788645e940eada959bdde927426e2531c9Paul Duffin   */
2050888a09821a98ac0680fad765217302858e70fa4Paul Duffin  @Override
2067dd252788645e940eada959bdde927426e2531c9Paul Duffin  public void close() throws IOException {
2077dd252788645e940eada959bdde927426e2531c9Paul Duffin    Throwable throwable = thrown;
2087dd252788645e940eada959bdde927426e2531c9Paul Duffin
2097dd252788645e940eada959bdde927426e2531c9Paul Duffin    // close closeables in LIFO order
2107dd252788645e940eada959bdde927426e2531c9Paul Duffin    while (!stack.isEmpty()) {
2117dd252788645e940eada959bdde927426e2531c9Paul Duffin      Closeable closeable = stack.removeFirst();
2127dd252788645e940eada959bdde927426e2531c9Paul Duffin      try {
2137dd252788645e940eada959bdde927426e2531c9Paul Duffin        closeable.close();
2147dd252788645e940eada959bdde927426e2531c9Paul Duffin      } catch (Throwable e) {
2157dd252788645e940eada959bdde927426e2531c9Paul Duffin        if (throwable == null) {
2167dd252788645e940eada959bdde927426e2531c9Paul Duffin          throwable = e;
2177dd252788645e940eada959bdde927426e2531c9Paul Duffin        } else {
2187dd252788645e940eada959bdde927426e2531c9Paul Duffin          suppressor.suppress(closeable, throwable, e);
2197dd252788645e940eada959bdde927426e2531c9Paul Duffin        }
2207dd252788645e940eada959bdde927426e2531c9Paul Duffin      }
2217dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
2227dd252788645e940eada959bdde927426e2531c9Paul Duffin
2237dd252788645e940eada959bdde927426e2531c9Paul Duffin    if (thrown == null && throwable != null) {
2247dd252788645e940eada959bdde927426e2531c9Paul Duffin      Throwables.propagateIfPossible(throwable, IOException.class);
2257dd252788645e940eada959bdde927426e2531c9Paul Duffin      throw new AssertionError(throwable); // not possible
2267dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
2277dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
2287dd252788645e940eada959bdde927426e2531c9Paul Duffin
2297dd252788645e940eada959bdde927426e2531c9Paul Duffin  /**
2307dd252788645e940eada959bdde927426e2531c9Paul Duffin   * Suppression strategy interface.
2317dd252788645e940eada959bdde927426e2531c9Paul Duffin   */
2320888a09821a98ac0680fad765217302858e70fa4Paul Duffin  @VisibleForTesting interface Suppressor {
2337dd252788645e940eada959bdde927426e2531c9Paul Duffin    /**
2347dd252788645e940eada959bdde927426e2531c9Paul Duffin     * Suppresses the given exception ({@code suppressed}) which was thrown when attempting to close
2357dd252788645e940eada959bdde927426e2531c9Paul Duffin     * the given closeable. {@code thrown} is the exception that is actually being thrown from the
2367dd252788645e940eada959bdde927426e2531c9Paul Duffin     * method. Implementations of this method should not throw under any circumstances.
2377dd252788645e940eada959bdde927426e2531c9Paul Duffin     */
2387dd252788645e940eada959bdde927426e2531c9Paul Duffin    void suppress(Closeable closeable, Throwable thrown, Throwable suppressed);
2397dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
2407dd252788645e940eada959bdde927426e2531c9Paul Duffin
2417dd252788645e940eada959bdde927426e2531c9Paul Duffin  /**
2427dd252788645e940eada959bdde927426e2531c9Paul Duffin   * Suppresses exceptions by logging them.
2437dd252788645e940eada959bdde927426e2531c9Paul Duffin   */
2440888a09821a98ac0680fad765217302858e70fa4Paul Duffin  @VisibleForTesting static final class LoggingSuppressor implements Suppressor {
2457dd252788645e940eada959bdde927426e2531c9Paul Duffin
2467dd252788645e940eada959bdde927426e2531c9Paul Duffin    static final LoggingSuppressor INSTANCE = new LoggingSuppressor();
2477dd252788645e940eada959bdde927426e2531c9Paul Duffin
2480888a09821a98ac0680fad765217302858e70fa4Paul Duffin    @Override
2497dd252788645e940eada959bdde927426e2531c9Paul Duffin    public void suppress(Closeable closeable, Throwable thrown, Throwable suppressed) {
2507dd252788645e940eada959bdde927426e2531c9Paul Duffin      // log to the same place as Closeables
2517dd252788645e940eada959bdde927426e2531c9Paul Duffin      Closeables.logger.log(Level.WARNING,
2527dd252788645e940eada959bdde927426e2531c9Paul Duffin          "Suppressing exception thrown when closing " + closeable, suppressed);
2537dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
2547dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
2557dd252788645e940eada959bdde927426e2531c9Paul Duffin
2567dd252788645e940eada959bdde927426e2531c9Paul Duffin  /**
2577dd252788645e940eada959bdde927426e2531c9Paul Duffin   * Suppresses exceptions by adding them to the exception that will be thrown using JDK7's
2587dd252788645e940eada959bdde927426e2531c9Paul Duffin   * addSuppressed(Throwable) mechanism.
2597dd252788645e940eada959bdde927426e2531c9Paul Duffin   */
2600888a09821a98ac0680fad765217302858e70fa4Paul Duffin  @VisibleForTesting static final class SuppressingSuppressor implements Suppressor {
2617dd252788645e940eada959bdde927426e2531c9Paul Duffin
2627dd252788645e940eada959bdde927426e2531c9Paul Duffin    static final SuppressingSuppressor INSTANCE = new SuppressingSuppressor();
2637dd252788645e940eada959bdde927426e2531c9Paul Duffin
2647dd252788645e940eada959bdde927426e2531c9Paul Duffin    static boolean isAvailable() {
2657dd252788645e940eada959bdde927426e2531c9Paul Duffin      return addSuppressed != null;
2667dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
2677dd252788645e940eada959bdde927426e2531c9Paul Duffin
2687dd252788645e940eada959bdde927426e2531c9Paul Duffin    static final Method addSuppressed = getAddSuppressed();
2697dd252788645e940eada959bdde927426e2531c9Paul Duffin
2707dd252788645e940eada959bdde927426e2531c9Paul Duffin    private static Method getAddSuppressed() {
2717dd252788645e940eada959bdde927426e2531c9Paul Duffin      try {
2727dd252788645e940eada959bdde927426e2531c9Paul Duffin        return Throwable.class.getMethod("addSuppressed", Throwable.class);
2737dd252788645e940eada959bdde927426e2531c9Paul Duffin      } catch (Throwable e) {
2747dd252788645e940eada959bdde927426e2531c9Paul Duffin        return null;
2757dd252788645e940eada959bdde927426e2531c9Paul Duffin      }
2767dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
2777dd252788645e940eada959bdde927426e2531c9Paul Duffin
2780888a09821a98ac0680fad765217302858e70fa4Paul Duffin    @Override
2797dd252788645e940eada959bdde927426e2531c9Paul Duffin    public void suppress(Closeable closeable, Throwable thrown, Throwable suppressed) {
2807dd252788645e940eada959bdde927426e2531c9Paul Duffin      // ensure no exceptions from addSuppressed
2817dd252788645e940eada959bdde927426e2531c9Paul Duffin      if (thrown == suppressed) {
2827dd252788645e940eada959bdde927426e2531c9Paul Duffin        return;
2837dd252788645e940eada959bdde927426e2531c9Paul Duffin      }
2847dd252788645e940eada959bdde927426e2531c9Paul Duffin      try {
2857dd252788645e940eada959bdde927426e2531c9Paul Duffin        addSuppressed.invoke(thrown, suppressed);
2867dd252788645e940eada959bdde927426e2531c9Paul Duffin      } catch (Throwable e) {
2877dd252788645e940eada959bdde927426e2531c9Paul Duffin        // if, somehow, IllegalAccessException or another exception is thrown, fall back to logging
2887dd252788645e940eada959bdde927426e2531c9Paul Duffin        LoggingSuppressor.INSTANCE.suppress(closeable, thrown, suppressed);
2897dd252788645e940eada959bdde927426e2531c9Paul Duffin      }
2907dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
2917dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
2927dd252788645e940eada959bdde927426e2531c9Paul Duffin}
293