All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.hibernate.search.util.impl.Closer Maven / Gradle / Ivy

There is a newer version: 5.11.12.Final
Show newest version
/*
 * Hibernate Search, full-text search for your domain model
 *
 * License: GNU Lesser General Public License (LGPL), version 2.1 or later
 * See the lgpl.txt file in the root directory or .
 */
package org.hibernate.search.util.impl;

/**
 * A helper for closing multiple resources and re-throwing only one exception,
 * {@link Throwable#addSuppressed(Throwable) suppressing} the others as necessary.
 * 

* This class is not thread safe. *

* This helper is mainly useful when implementing {@link AutoCloseable#close()} * or a similar closing method in your own class, to make sure that all resources are * at least given the chance to close, even if closing one of them fails. * When creating then closing resources in the scope of a single method call, * try-with-resource blocks should be favored. *

* Warning: In order not to ignore exceptions, * you should always call {@link Closer#close()} * once you closed all of your resources. The most straightforward way * to do this is to use a try-with-resource block: *


 * public void myCloseFunction() throws MyException {
 *   try ( Closer<MyException> closer = new Closer<>() ) {
 *     closer.push( this.myCloseable::close );
 *     closer.pushAll( this.myListOfCloseables, MyCloseableType::close );
 *   }
 * }
 * 
* *

Exception type

*

* Note that the closer has a generic type parameter, allowing it to * re-throw a given checked exception. * If you don't want to use this feature, you can simply use a * {@code Closer}. * *

Splitting

*

* If you need to close multiple resources throwing different checked * exceptions, and those exceptions don't have a practical common superclass, * you can "split" the closer: *


 * public void myCloseFunction() throws IOException, MyException, MyOtherException {
 *   try ( Closer<MyException> closer1 = new Closer<>();
 *       Closer<IOException> closer2 = closer1.split();
 *       Closer<MyOtherException> closer3 = closer1.split() ) {
 *     closer2.push( this.someJavaIOCloseable::close );
 *     closer1.pushAll( this.myListOfCloseables1, MyCloseableTypeThrowingMyException::close );
 *     closer3.pushAll( this.myListOfCloseables2, MyCloseableTypeThrowingMyOtherException::close );
 *   }
 * }
 * 
*

* The multiple closers will share the same state, which means the first * exception to be caught by any of the closers will be the one to be re-thrown, * and all subsequent exceptions caught by any closer will be added as suppressed * to this first exception. * Be careful though, you will have to close every single closer. * Closing just the original one will not be enough. * * @param E The supertype of exceptions this closer can catch and re-throw, * besides {@link RuntimeException} and {@link Error}. * * @author Yoann Rodiere */ public final class Closer implements AutoCloseable { private final State state; public Closer() { this( new State() ); } private Closer(State state) { this.state = state; } /** * Execute the given close {@code operator} immediately on * the given {@code objectToClose}, swallowing any throwable in order to * throw it {@link #close() later}. * @param operator An operator to close {@code objectToClose}. Accepts lambdas * such as {@code MyType::close}. * @param objectToClose An object to close */ public void push(ClosingOperator operator, T objectToClose) { try { operator.close( objectToClose ); } catch (Throwable t) { state.addThrowable( this, t ); } } /** * Close the given {@code closeable} immediately, * swallowing any throwable in order to throw it {@link #close() later}. * * @param closeable A {@link GenericCloseable} to close. Accepts lambdas * such as {@code myObject::close}. */ public void push(GenericCloseable closeable) { push( GenericCloseable::close, closeable ); } /** * Execute the given close {@code operator} immediately on * each element of the given iterable, swallowing any throwable in order to * throw them {@link #close() later}. * @param operator An operator to close each element in {@code objectsToClose}. Accepts lambdas * such as {@code MyType::close}. * @param objectsToClose An iterable of objects to close */ public void pushAll(ClosingOperator operator, Iterable objectsToClose) { for ( T objectToClose : objectsToClose ) { push( operator, objectToClose ); } } /** * Execute the given close {@code operator} immediately on * each element of the given array, swallowing any throwable in order to * throw them {@link #close() later}. * @param operator An operator to close each element in {@code objectsToClose}. Accepts lambdas * such as {@code MyType::close}. * @param objectsToClose An array of objects to close */ @SafeVarargs public final void pushAll(ClosingOperator operator, T ... objectsToClose) { for ( T objectToClose : objectsToClose ) { push( operator, objectToClose ); } } /** * @return A closer sharing the same state as {@code this}, allowing to handle * multiple exception types. * @see splitting */ public Closer split() { return new Closer<>(); } /** * @throws E The first throwable caught when executing the {@code push} methods, if any. * Any throwable caught after the first will have been * {@link Throwable#addSuppressed(Throwable) suppressed}. */ @Override public void close() throws E { state.close( this ); } /** * This is implemented in a separate class so that multiple closers * can share the same state, allowing them to elect a single "first throwable". */ private static class State { private Closer firstThrower; private Throwable firstThrowable; public void addThrowable(Closer source, Throwable throwable) { if ( firstThrowable == null ) { firstThrowable = throwable; firstThrower = source; } else { firstThrowable.addSuppressed( throwable ); } } @SuppressWarnings("unchecked") public void close(Closer source) throws E { if ( firstThrowable != null && source == firstThrower ) { try { if ( firstThrowable instanceof RuntimeException ) { throw (RuntimeException) firstThrowable; } else if ( firstThrowable instanceof Error ) { throw (Error) firstThrowable; } else if ( firstThrowable != null ) { /* * At this point we know that throwable is an instance of E, * because that's the only checked exception that the source * can catch. */ throw (E) firstThrowable; } } finally { // Ensure the next calls to Closer.close won't throw this.firstThrower = null; this.firstThrowable = null; } } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy