/* dCache - http://www.dcache.org/ * * Copyright (C) 2013 Deutsches Elektronen-Synchrotron * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.dcache.util; import com.google.common.io.Closer; import java.io.Closeable; import java.nio.channels.CompletionHandler; import org.dcache.pool.classic.Cancellable; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; /** * Implements an asynchronous variant of the try-with-resource construct of Java 7. * * The class follows the template pattern often seen in the Spring Framework. A client * is supposed to create a (possibly anonymous inner) subclass implementing either * the {@code execute} or {@code executeWithCancellable} method. * * The template implements Cancellable, which may be used to cancel any asynchronous * operations started by {@code execute}. The template implements CompletionHandler and * {@code execute} or an asynchronous operation started by {@code execute} must use this * interface to signal completion. * * Once the template completes or fails, this is signalled to an injected completion * handler. * * An asynchronous operation started by {@code execute} should be registered by calling * {@code setCancellable}. Only then will the template be able to cancel the asynchronous * operations. Alternatively a subclass may override {@code executeWithCancellable} and * return the Cancellable. * * {@code execute} may register Closeable resources by calling {@code autoclose}. * These are guaranteed to be closed once this {@code TryCatchTemplate} completes. * Failure to close any resource is propagated as a failure of the template, unless the * template has already failed (in that case the failure to close a resource is added * as a suppressed throwable). * * Any exceptions thrown by {@code execute} are caught and will result in failure * of the template. * * A cancellable registered through {@code setCancellable} will be cancelled if * {@code execute} throws an exception or calls {@code failed}, or if the template * is cancelled. * * A subclass may override {@code onSuccess} and {@code onFailure} to add additional * processing when the template completes or fails. These are only to be used for * light-weight non-blocking operations. The thread on which these are called is * unpredictable. * */ public abstract class TryCatchTemplate<V, A> implements Cancellable, CompletionHandler<V, A> { private final Closer _closer = Closer.create(); private final CompletionHandler<V, A> _completionHandler; private volatile Cancellable _cancellable; public TryCatchTemplate(CompletionHandler<V, A> completionHandler) { _completionHandler = completionHandler; try { execute(); } catch (Throwable t) { failed(t, null); } } @Override public void cancel(String explanation) { if (_cancellable != null) { _cancellable.cancel(explanation); } } @Override public final void completed(V result, A attachment) { try { _closer.close(); onSuccess(result, attachment); _completionHandler.completed(result, attachment); } catch (Throwable t) { fail(t, attachment); } } @Override public final void failed(Throwable exc, A attachment) { /* This weird looking code is to fulfill the contract of Closer. * It ensures that suppressed exceptions from the Closeables are * handled correctly. */ try { try { throw _closer.rethrow(exc, Exception.class); } finally { _closer.close(); } } catch (Throwable t) { fail(t, attachment); } } private void fail(Throwable t, A attachment) { try { onFailure(t, attachment); } catch (Exception replacement) { if (replacement.getCause() == t) { t = replacement; } else if (replacement != t) { t.addSuppressed(replacement); } } catch (Throwable suppressed) { t.addSuppressed(t); } _completionHandler.failed(t, attachment); } /** * Registers the given {@code closeable} to be closed when this {@code TryCatchTemplate} completes. * * @return the given {@code closeable} */ protected <C extends Closeable> C autoclose(C closeable) { return _closer.register(closeable); } /** * Registers the given {@code cancellable}. When this tempalte is cancelled so is {@code cancellable}. * * @throws IllegalStateException if called more than once */ protected void setCancellable(Cancellable cancellable) { checkState(_cancellable == null); _cancellable = checkNotNull(cancellable); } /** * Called by the constructor to execute the template. * * Either {@code completed}, {@code failed}, or {@code setCancellable} should be called. * * A subclass is expected to override either {@code execute} or {@code executeWithResult}. */ protected void execute() throws Exception { setCancellable(executeWithCancellable()); } /** * Like {@code execute} but allows a cancellable to be returned. * * An implementation should not call {@code completed} or {@code setCancellable} from within * {@code executeWithCancellable}. */ protected Cancellable executeWithCancellable() throws Exception { return null; } /** * Invoked with the result of the execution when it is successful. * * If an {@code Exception} is thrown, the template fails. Otherwise the template succeeds. * * Must not call {@code completed} or {@code failed}. */ protected void onSuccess(V result, A attachment) throws Exception { } /** * Invoked when the execution fails or is canceled. * * If an {@code Exception} is thrown of which {@code t} is the cause, that exception * is used to fail this template. Thus an implementation may replace the reason the * template fails by throwing a new exception with {@code t} set as the cause. * * Otherwise the template fails with {@code t}. Any other exception thrown by this * method is suppressed. * * Must not call {@code completed} or {@code failed}. */ protected void onFailure(Throwable t, A attachment) throws Exception { } }