Coverage Summary for Class: Closer (com.google.common.io)

Class Method, % Line, %
Closer 0% (0/8) 0% (0/36)
Closer$LoggingSuppressor 0% (0/3) 0% (0/3)
Closer$SuppressingSuppressor 0% (0/3) 0% (0/12)
Total 0% (0/14) 0% (0/51)


1 /* 2  * Copyright (C) 2012 The Guava Authors 3  * 4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5  * in compliance with the License. You may obtain a copy of the License at 6  * 7  * http://www.apache.org/licenses/LICENSE-2.0 8  * 9  * Unless required by applicable law or agreed to in writing, software distributed under the License 10  * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11  * or implied. See the License for the specific language governing permissions and limitations under 12  * the License. 13  */ 14  15 package com.google.common.io; 16  17 import static com.google.common.base.Preconditions.checkNotNull; 18  19 import com.google.common.annotations.Beta; 20 import com.google.common.annotations.GwtIncompatible; 21 import com.google.common.annotations.VisibleForTesting; 22 import com.google.common.base.Throwables; 23 import com.google.errorprone.annotations.CanIgnoreReturnValue; 24 import java.io.Closeable; 25 import java.io.IOException; 26 import java.lang.reflect.Method; 27 import java.util.ArrayDeque; 28 import java.util.Deque; 29 import java.util.logging.Level; 30 import javax.annotation.CheckForNull; 31 import org.checkerframework.checker.nullness.qual.Nullable; 32  33 /** 34  * A {@link Closeable} that collects {@code Closeable} resources and closes them all when it is 35  * {@linkplain #close closed}. This is intended to approximately emulate the behavior of Java 7's <a 36  * href="http://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html" 37  * >try-with-resources</a> statement in JDK6-compatible code. Running on Java 7, code using this 38  * should be approximately equivalent in behavior to the same code written with try-with-resources. 39  * Running on Java 6, exceptions that cannot be thrown must be logged rather than being added to the 40  * thrown exception as a suppressed exception. 41  * 42  * <p>This class is intended to be used in the following pattern: 43  * 44  * <pre>{@code 45  * Closer closer = Closer.create(); 46  * try { 47  * InputStream in = closer.register(openInputStream()); 48  * OutputStream out = closer.register(openOutputStream()); 49  * // do stuff 50  * } catch (Throwable e) { 51  * // ensure that any checked exception types other than IOException that could be thrown are 52  * // provided here, e.g. throw closer.rethrow(e, CheckedException.class); 53  * throw closer.rethrow(e); 54  * } finally { 55  * closer.close(); 56  * } 57  * }</pre> 58  * 59  * <p>Note that this try-catch-finally block is not equivalent to a try-catch-finally block using 60  * try-with-resources. To get the equivalent of that, you must wrap the above code in <i>another</i> 61  * try block in order to catch any exception that may be thrown (including from the call to {@code 62  * close()}). 63  * 64  * <p>This pattern ensures the following: 65  * 66  * <ul> 67  * <li>Each {@code Closeable} resource that is successfully registered will be closed later. 68  * <li>If a {@code Throwable} is thrown in the try block, no exceptions that occur when attempting 69  * to close resources will be thrown from the finally block. The throwable from the try block 70  * will be thrown. 71  * <li>If no exceptions or errors were thrown in the try block, the <i>first</i> exception thrown 72  * by an attempt to close a resource will be thrown. 73  * <li>Any exception caught when attempting to close a resource that is <i>not</i> thrown (because 74  * another exception is already being thrown) is <i>suppressed</i>. 75  * </ul> 76  * 77  * <p>An exception that is suppressed is not thrown. The method of suppression used depends on the 78  * version of Java the code is running on: 79  * 80  * <ul> 81  * <li><b>Java 7+:</b> Exceptions are suppressed by adding them to the exception that <i>will</i> 82  * be thrown using {@code Throwable.addSuppressed(Throwable)}. 83  * <li><b>Java 6:</b> Exceptions are suppressed by logging them instead. 84  * </ul> 85  * 86  * @author Colin Decker 87  * @since 14.0 88  */ 89 // Coffee's for {@link Closer closers} only. 90 @Beta 91 @GwtIncompatible 92 @ElementTypesAreNonnullByDefault 93 public final class Closer implements Closeable { 94  95  /** The suppressor implementation to use for the current Java version. */ 96  private static final Suppressor SUPPRESSOR; 97  98  static { 99  SuppressingSuppressor suppressingSuppressor = SuppressingSuppressor.tryCreate(); 100  SUPPRESSOR = suppressingSuppressor == null ? LoggingSuppressor.INSTANCE : suppressingSuppressor; 101  } 102  103  /** Creates a new {@link Closer}. */ 104  public static Closer create() { 105  return new Closer(SUPPRESSOR); 106  } 107  108  @VisibleForTesting final Suppressor suppressor; 109  110  // only need space for 2 elements in most cases, so try to use the smallest array possible 111  private final Deque<Closeable> stack = new ArrayDeque<>(4); 112  @CheckForNull private Throwable thrown; 113  114  @VisibleForTesting 115  Closer(Suppressor suppressor) { 116  this.suppressor = checkNotNull(suppressor); // checkNotNull to satisfy null tests 117  } 118  119  /** 120  * Registers the given {@code closeable} to be closed when this {@code Closer} is {@linkplain 121  * #close closed}. 122  * 123  * @return the given {@code closeable} 124  */ 125  // close. this word no longer has any meaning to me. 126  @CanIgnoreReturnValue 127  @ParametricNullness 128  @SuppressWarnings("nullness") 129  public <C extends @Nullable Closeable> C register( 130  // TODO(b/147136275): Replace @CheckForNull with @ParametricNullness, and remove suppression. 131  @CheckForNull C closeable) { 132  if (closeable != null) { 133  stack.addFirst(closeable); 134  } 135  136  return closeable; 137  } 138  139  /** 140  * Stores the given throwable and rethrows it. It will be rethrown as is if it is an {@code 141  * IOException}, {@code RuntimeException} or {@code Error}. Otherwise, it will be rethrown wrapped 142  * in a {@code RuntimeException}. <b>Note:</b> Be sure to declare all of the checked exception 143  * types your try block can throw when calling an overload of this method so as to avoid losing 144  * the original exception type. 145  * 146  * <p>This method always throws, and as such should be called as {@code throw closer.rethrow(e);} 147  * to ensure the compiler knows that it will throw. 148  * 149  * @return this method does not return; it always throws 150  * @throws IOException when the given throwable is an IOException 151  */ 152  public RuntimeException rethrow(Throwable e) throws IOException { 153  checkNotNull(e); 154  thrown = e; 155  Throwables.propagateIfPossible(e, IOException.class); 156  throw new RuntimeException(e); 157  } 158  159  /** 160  * Stores the given throwable and rethrows it. It will be rethrown as is if it is an {@code 161  * IOException}, {@code RuntimeException}, {@code Error} or a checked exception of the given type. 162  * Otherwise, it will be rethrown wrapped in a {@code RuntimeException}. <b>Note:</b> Be sure to 163  * declare all of the checked exception types your try block can throw when calling an overload of 164  * this method so as to avoid losing the original exception type. 165  * 166  * <p>This method always throws, and as such should be called as {@code throw closer.rethrow(e, 167  * ...);} to ensure the compiler knows that it will throw. 168  * 169  * @return this method does not return; it always throws 170  * @throws IOException when the given throwable is an IOException 171  * @throws X when the given throwable is of the declared type X 172  */ 173  public <X extends Exception> RuntimeException rethrow(Throwable e, Class<X> declaredType) 174  throws IOException, X { 175  checkNotNull(e); 176  thrown = e; 177  Throwables.propagateIfPossible(e, IOException.class); 178  Throwables.propagateIfPossible(e, declaredType); 179  throw new RuntimeException(e); 180  } 181  182  /** 183  * Stores the given throwable and rethrows it. It will be rethrown as is if it is an {@code 184  * IOException}, {@code RuntimeException}, {@code Error} or a checked exception of either of the 185  * given types. Otherwise, it will be rethrown wrapped in a {@code RuntimeException}. <b>Note:</b> 186  * Be sure to declare all of the checked exception types your try block can throw when calling an 187  * overload of this method so as to avoid losing the original exception type. 188  * 189  * <p>This method always throws, and as such should be called as {@code throw closer.rethrow(e, 190  * ...);} to ensure the compiler knows that it will throw. 191  * 192  * @return this method does not return; it always throws 193  * @throws IOException when the given throwable is an IOException 194  * @throws X1 when the given throwable is of the declared type X1 195  * @throws X2 when the given throwable is of the declared type X2 196  */ 197  public <X1 extends Exception, X2 extends Exception> RuntimeException rethrow( 198  Throwable e, Class<X1> declaredType1, Class<X2> declaredType2) throws IOException, X1, X2 { 199  checkNotNull(e); 200  thrown = e; 201  Throwables.propagateIfPossible(e, IOException.class); 202  Throwables.propagateIfPossible(e, declaredType1, declaredType2); 203  throw new RuntimeException(e); 204  } 205  206  /** 207  * Closes all {@code Closeable} instances that have been added to this {@code Closer}. If an 208  * exception was thrown in the try block and passed to one of the {@code exceptionThrown} methods, 209  * any exceptions thrown when attempting to close a closeable will be suppressed. Otherwise, the 210  * <i>first</i> exception to be thrown from an attempt to close a closeable will be thrown and any 211  * additional exceptions that are thrown after that will be suppressed. 212  */ 213  @Override 214  public void close() throws IOException { 215  Throwable throwable = thrown; 216  217  // close closeables in LIFO order 218  while (!stack.isEmpty()) { 219  Closeable closeable = stack.removeFirst(); 220  try { 221  closeable.close(); 222  } catch (Throwable e) { 223  if (throwable == null) { 224  throwable = e; 225  } else { 226  suppressor.suppress(closeable, throwable, e); 227  } 228  } 229  } 230  231  if (thrown == null && throwable != null) { 232  Throwables.propagateIfPossible(throwable, IOException.class); 233  throw new AssertionError(throwable); // not possible 234  } 235  } 236  237  /** Suppression strategy interface. */ 238  @VisibleForTesting 239  interface Suppressor { 240  /** 241  * Suppresses the given exception ({@code suppressed}) which was thrown when attempting to close 242  * the given closeable. {@code thrown} is the exception that is actually being thrown from the 243  * method. Implementations of this method should not throw under any circumstances. 244  */ 245  void suppress(Closeable closeable, Throwable thrown, Throwable suppressed); 246  } 247  248  /** Suppresses exceptions by logging them. */ 249  @VisibleForTesting 250  static final class LoggingSuppressor implements Suppressor { 251  252  static final LoggingSuppressor INSTANCE = new LoggingSuppressor(); 253  254  @Override 255  public void suppress(Closeable closeable, Throwable thrown, Throwable suppressed) { 256  // log to the same place as Closeables 257  Closeables.logger.log( 258  Level.WARNING, "Suppressing exception thrown when closing " + closeable, suppressed); 259  } 260  } 261  262  /** 263  * Suppresses exceptions by adding them to the exception that will be thrown using JDK7's 264  * addSuppressed(Throwable) mechanism. 265  */ 266  @VisibleForTesting 267  static final class SuppressingSuppressor implements Suppressor { 268  @CheckForNull 269  static SuppressingSuppressor tryCreate() { 270  Method addSuppressed; 271  try { 272  addSuppressed = Throwable.class.getMethod("addSuppressed", Throwable.class); 273  } catch (Throwable e) { 274  return null; 275  } 276  return new SuppressingSuppressor(addSuppressed); 277  } 278  279  private final Method addSuppressed; 280  281  private SuppressingSuppressor(Method addSuppressed) { 282  this.addSuppressed = addSuppressed; 283  } 284  285  @Override 286  public void suppress(Closeable closeable, Throwable thrown, Throwable suppressed) { 287  // ensure no exceptions from addSuppressed 288  if (thrown == suppressed) { 289  return; 290  } 291  try { 292  addSuppressed.invoke(thrown, suppressed); 293  } catch (Throwable e) { 294  // if, somehow, IllegalAccessException or another exception is thrown, fall back to logging 295  LoggingSuppressor.INSTANCE.suppress(closeable, thrown, suppressed); 296  } 297  } 298  } 299 }