
net.yetamine.lang.closeables.AutoCloseableAdapter Maven / Gradle / Ivy
Show all versions of net.yetamine.lang Show documentation
package net.yetamine.lang.closeables;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import java.util.function.Function;
/**
* An {@link AutoCloseable} adapter for resources that don't implement that
* interface.
*
*
* The implementation is thread-safe, guarantees that the given closing handler
* is invoked only on the first invocation of {@link #close()}, while
* {@link #resource()} provides only the resource which is not closed yet.
*
* @param
* the type of the adapted resource
* @param
* the type of the exception that the {@link #close()} method may
* throw for this resource
*/
public final class AutoCloseableAdapter implements AutoCloseableResource {
/** Adapted object. */
private final AtomicReference resource;
/** Closing handler providing exceptions. */
private final Function super T, ? extends X> close;
/**
* Creates a new instance.
*
* @param object
* the object to adapt. It must not be {@code null}.
* @param closingHandler
* the handler to clean up and return the exception to throw (
* {@code null} if none should be thrown). It must not be
* {@code null}.
*/
private AutoCloseableAdapter(T object, Function super T, ? extends X> closingHandler) {
resource = new AtomicReference<>(Objects.requireNonNull(object));
close = closingHandler;
assert (close != null);
}
/**
* Creates a new instance.
*
* @param
* the type of the adapted resource
* @param
* the type of the exception
* @param object
* the object to adapt. It must not be {@code null}.
* @param closingHandler
* the handler to clean up and return the exception to throw (
* {@code null} if none should be thrown). It must not be
* {@code null}.
*
* @return an adapter for the object
*/
public static AutoCloseableAdapter using(T object, Function super T, ? extends X> closingHandler) {
Objects.requireNonNull(closingHandler);
return new AutoCloseableAdapter<>(object, closingHandler);
}
/**
* Creates a new instance.
*
* @param
* the type of the adapted resource
* @param
* the type of the exception
* @param object
* the object to adapt. It must not be {@code null}.
* @param closingHandler
* the handler to clean up; because it can't return any
* exception, it may signal a problem only by throwing an
* unchecked exception. It must not be {@code null}.
*
* @return an adapter for the object
*/
public static AutoCloseableAdapter using(T object, Consumer super T> closingHandler) {
Objects.requireNonNull(closingHandler);
// Using explicit definition, Eclipse is unhappy otherwise
final Function closingFunction = o -> {
closingHandler.accept(o);
return null;
};
return new AutoCloseableAdapter<>(object, closingFunction);
}
/**
* Provides the adapted object.
*
* @throws IllegalStateException
* if closed already
*
* @see net.yetamine.lang.closeables.AutoCloseableResource#resource()
*/
public T resource() {
final T result = resource.get();
if (result != null) {
return result;
}
throw new IllegalStateException();
}
/**
* @see net.yetamine.lang.closeables.AutoCloseableResource#close()
*/
public void close() throws X {
final T value = resource.getAndSet(null);
if (value == null) {
return;
}
final X toThrow = close.apply(value);
if (toThrow != null) {
throw toThrow;
}
}
/**
* Provides the adapted object if not closed yet.
*
* @return an {@link Optional} containing the adapted object, or an empty
* container if the resource has been closed
*/
public Optional available() {
return Optional.ofNullable(resource.get());
}
/**
* Indicates whether the resource is closed; note that the state may change
* by closing the resource from another thread any time.
*
* @return {@code true} if the resource is closed
*/
public boolean isClosed() {
return (resource.get() == null);
}
}