PageRenderTime 55ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/worldguard-legacy/src/main/java/com/sk89q/worldguard/util/io/Closer.java

https://gitlab.com/igserfurtmcschulserver/CustomWorldGuard
Java | 308 lines | 155 code | 32 blank | 121 comment | 11 complexity | 7f99135da74ec6c78ec49c7ef2dd4b89 MD5 | raw file
  1. /*
  2. * WorldGuard, a suite of tools for Minecraft
  3. * Copyright (C) sk89q <http://www.sk89q.com>
  4. * Copyright (C) WorldGuard team and contributors
  5. *
  6. * This program is free software: you can redistribute it and/or modify it
  7. * under the terms of the GNU Lesser General Public License as published by the
  8. * Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful, but WITHOUT
  12. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13. * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
  14. * for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public License
  17. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. package com.sk89q.worldguard.util.io;
  20. import com.google.common.annotations.VisibleForTesting;
  21. import com.google.common.base.Throwables;
  22. import java.io.Closeable;
  23. import java.io.IOException;
  24. import java.lang.reflect.Method;
  25. import java.sql.Connection;
  26. import java.sql.ResultSet;
  27. import java.sql.SQLException;
  28. import java.sql.Statement;
  29. import java.util.ArrayDeque;
  30. import java.util.Deque;
  31. import java.util.logging.Level;
  32. import java.util.logging.Logger;
  33. import static com.google.common.base.Preconditions.checkNotNull;
  34. public final class Closer implements Closeable {
  35. private static final Logger logger = Logger.getLogger(Closer.class.getCanonicalName());
  36. /**
  37. * The suppressor implementation to use for the current Java version.
  38. */
  39. private static final Suppressor SUPPRESSOR = SuppressingSuppressor.isAvailable()
  40. ? SuppressingSuppressor.INSTANCE
  41. : LoggingSuppressor.INSTANCE;
  42. /**
  43. * Creates a new {@link Closer}.
  44. */
  45. public static Closer create() {
  46. return new Closer(SUPPRESSOR);
  47. }
  48. @VisibleForTesting
  49. final Suppressor suppressor;
  50. // only need space for 2 elements in most cases, so try to use the smallest array possible
  51. private final Deque<Closeable> stack = new ArrayDeque<Closeable>(4);
  52. private Throwable thrown;
  53. @VisibleForTesting
  54. Closer(Suppressor suppressor) {
  55. this.suppressor = checkNotNull(suppressor); // checkNotNull to satisfy null tests
  56. }
  57. /**
  58. * Registers the given {@code closeable} to be closed when this {@code Closer} is
  59. * {@linkplain #close closed}.
  60. *
  61. * @return the given {@code closeable}
  62. */
  63. // close. this word no longer has any meaning to me.
  64. public <C extends Closeable> C register(C closeable) {
  65. stack.push(closeable);
  66. return closeable;
  67. }
  68. /**
  69. * Registers the given {@code connection} to be closed when this
  70. * {@code Closer} is {@linkplain #close closed}.
  71. *
  72. * @return the given {@code connection}
  73. */
  74. public <C extends Connection> C register(final C connection) {
  75. register(new Closeable() {
  76. @Override
  77. public void close() throws IOException {
  78. try {
  79. connection.close();
  80. } catch (SQLException e) {
  81. throw new IOException("Failed to close", e);
  82. }
  83. }
  84. });
  85. return connection;
  86. }
  87. /**
  88. * Registers the given {@code statement} to be closed when this
  89. * {@code Closer} is {@linkplain #close closed}.
  90. *
  91. * @return the given {@code statement}
  92. */
  93. public <C extends Statement> C register(final C statement) {
  94. register(new Closeable() {
  95. @Override
  96. public void close() throws IOException {
  97. try {
  98. statement.close();
  99. } catch (SQLException e) {
  100. throw new IOException("Failed to close", e);
  101. }
  102. }
  103. });
  104. return statement;
  105. }
  106. /**
  107. * Registers the given {@code resultSet} to be closed when this
  108. * {@code Closer} is {@linkplain #close closed}.
  109. *
  110. * @return the given {@code resultSet}
  111. */
  112. public <C extends ResultSet> C register(final C resultSet) {
  113. register(new Closeable() {
  114. @Override
  115. public void close() throws IOException {
  116. try {
  117. resultSet.close();
  118. } catch (SQLException e) {
  119. throw new IOException("Failed to close", e);
  120. }
  121. }
  122. });
  123. return resultSet;
  124. }
  125. /**
  126. * Stores the given throwable and rethrows it. It will be rethrown as is if it is an
  127. * {@code IOException}, {@code RuntimeException} or {@code Error}. Otherwise, it will be rethrown
  128. * wrapped in a {@code RuntimeException}. <b>Note:</b> Be sure to declare all of the checked
  129. * exception types your try block can throw when calling an overload of this method so as to avoid
  130. * losing the original exception type.
  131. *
  132. * <p>This method always throws, and as such should be called as
  133. * {@code throw closer.rethrow(e);} to ensure the compiler knows that it will throw.
  134. *
  135. * @return this method does not return; it always throws
  136. * @throws IOException when the given throwable is an IOException
  137. */
  138. public RuntimeException rethrow(Throwable e) throws IOException {
  139. thrown = e;
  140. Throwables.propagateIfPossible(e, IOException.class);
  141. throw Throwables.propagate(e);
  142. }
  143. /**
  144. * Stores the given throwable and rethrows it. It will be rethrown as is if it is an
  145. * {@code IOException}, {@code RuntimeException}, {@code Error} or a checked exception of the
  146. * given type. Otherwise, it will be rethrown wrapped in a {@code RuntimeException}. <b>Note:</b>
  147. * Be sure to declare all of the checked exception types your try block can throw when calling an
  148. * overload of this method so as to avoid losing the original exception type.
  149. *
  150. * <p>This method always throws, and as such should be called as
  151. * {@code throw closer.rethrow(e, ...);} to ensure the compiler knows that it will throw.
  152. *
  153. * @return this method does not return; it always throws
  154. * @throws IOException when the given throwable is an IOException
  155. * @throws X when the given throwable is of the declared type X
  156. */
  157. public <X extends Exception> RuntimeException rethrow(Throwable e,
  158. Class<X> declaredType) throws IOException, X {
  159. thrown = e;
  160. Throwables.propagateIfPossible(e, IOException.class);
  161. Throwables.propagateIfPossible(e, declaredType);
  162. throw Throwables.propagate(e);
  163. }
  164. /**
  165. * Stores the given throwable and rethrows it. It will be rethrown as is if it is an
  166. * {@code IOException}, {@code RuntimeException}, {@code Error} or a checked exception of either
  167. * of the given types. Otherwise, it will be rethrown wrapped in a {@code RuntimeException}.
  168. * <b>Note:</b> Be sure to declare all of the checked exception types your try block can throw
  169. * when calling an overload of this method so as to avoid losing the original exception type.
  170. *
  171. * <p>This method always throws, and as such should be called as
  172. * {@code throw closer.rethrow(e, ...);} to ensure the compiler knows that it will throw.
  173. *
  174. * @return this method does not return; it always throws
  175. * @throws IOException when the given throwable is an IOException
  176. * @throws X1 when the given throwable is of the declared type X1
  177. * @throws X2 when the given throwable is of the declared type X2
  178. */
  179. public <X1 extends Exception, X2 extends Exception> RuntimeException rethrow(
  180. Throwable e, Class<X1> declaredType1, Class<X2> declaredType2) throws IOException, X1, X2 {
  181. thrown = e;
  182. Throwables.propagateIfPossible(e, IOException.class);
  183. Throwables.propagateIfPossible(e, declaredType1, declaredType2);
  184. throw Throwables.propagate(e);
  185. }
  186. /**
  187. * Closes all {@code Closeable} instances that have been added to this {@code Closer}. If an
  188. * exception was thrown in the try block and passed to one of the {@code exceptionThrown} methods,
  189. * any exceptions thrown when attempting to close a closeable will be suppressed. Otherwise, the
  190. * <i>first</i> exception to be thrown from an attempt to close a closeable will be thrown and any
  191. * additional exceptions that are thrown after that will be suppressed.
  192. */
  193. @Override
  194. public void close() throws IOException {
  195. Throwable throwable = thrown;
  196. // close closeables in LIFO order
  197. while (!stack.isEmpty()) {
  198. Closeable closeable = stack.pop();
  199. try {
  200. closeable.close();
  201. } catch (Throwable e) {
  202. if (throwable == null) {
  203. throwable = e;
  204. } else {
  205. suppressor.suppress(closeable, throwable, e);
  206. }
  207. }
  208. }
  209. if (thrown == null && throwable != null) {
  210. Throwables.propagateIfPossible(throwable, IOException.class);
  211. throw new AssertionError(throwable); // not possible
  212. }
  213. }
  214. /**
  215. * Close quietly.
  216. */
  217. public void closeQuietly() {
  218. try {
  219. close();
  220. } catch (IOException ignored) {
  221. }
  222. }
  223. /**
  224. * Suppression strategy interface.
  225. */
  226. @VisibleForTesting interface Suppressor {
  227. /**
  228. * Suppresses the given exception ({@code suppressed}) which was thrown when attempting to close
  229. * the given closeable. {@code thrown} is the exception that is actually being thrown from the
  230. * method. Implementations of this method should not throw under any circumstances.
  231. */
  232. void suppress(Closeable closeable, Throwable thrown, Throwable suppressed);
  233. }
  234. /**
  235. * Suppresses exceptions by logging them.
  236. */
  237. @VisibleForTesting static final class LoggingSuppressor implements Suppressor {
  238. static final LoggingSuppressor INSTANCE = new LoggingSuppressor();
  239. @Override
  240. public void suppress(Closeable closeable, Throwable thrown, Throwable suppressed) {
  241. // log to the same place as Closeables
  242. logger.log(Level.WARNING, "Suppressing exception thrown when closing " + closeable, suppressed);
  243. }
  244. }
  245. /**
  246. * Suppresses exceptions by adding them to the exception that will be thrown using JDK7's
  247. * addSuppressed(Throwable) mechanism.
  248. */
  249. @VisibleForTesting static final class SuppressingSuppressor implements Suppressor {
  250. static final SuppressingSuppressor INSTANCE = new SuppressingSuppressor();
  251. static boolean isAvailable() {
  252. return addSuppressed != null;
  253. }
  254. static final Method addSuppressed = getAddSuppressed();
  255. private static Method getAddSuppressed() {
  256. try {
  257. return Throwable.class.getMethod("addSuppressed", Throwable.class);
  258. } catch (Throwable e) {
  259. return null;
  260. }
  261. }
  262. @Override
  263. public void suppress(Closeable closeable, Throwable thrown, Throwable suppressed) {
  264. // ensure no exceptions from addSuppressed
  265. if (thrown == suppressed) {
  266. return;
  267. }
  268. try {
  269. addSuppressed.invoke(thrown, suppressed);
  270. } catch (Throwable e) {
  271. // if, somehow, IllegalAccessException or another exception is thrown, fall back to logging
  272. LoggingSuppressor.INSTANCE.suppress(closeable, thrown, suppressed);
  273. }
  274. }
  275. }
  276. }