/*
* Copyright 2007-2010 Sun Microsystems, Inc.
*
* This file is part of Project Darkstar Server.
*
* Project Darkstar Server is free software: you can redistribute it
* and/or modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation and
* distributed hereunder to you.
*
* Project Darkstar Server 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* --
*/
package com.sun.sgs.impl.nio;
import com.sun.sgs.nio.channels.CompletionHandler;
import com.sun.sgs.nio.channels.IoFuture;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
/**
* An abstract base class for defining a {@code CompletionHandler} to use when
* implementing methods that return an {@code IoFuture}, have a {@code
* CompletionHandler} parameter, and are implemented by making calls to similar
* methods. This class is intended to support the interaction between the
* internal (involving to the delegating function) and external (involving the
* caller) futures and completion handlers, allowing subclasses supply the
* desired behavior by customizing the three protected methods: {@link
* #implStart}, {@link #implCompleted}, and {@link #done}. <p>
*
* For example, suppose you wanted to implement a method like {@code
* AsynchronousByteChannel.read} that delegated to an existing channel, but
* printed a message before and after reading. A simple implementation might
* look like:
* <pre>
* public class PrintReader<A>
* extends DelegatingCompletionHandler<Integer, A, Integer, A>
* {
* private final AsynchronousByteChannel channel;
* private final ByteBuffer dst;
*
* public static <A> IoFuture<Integer, A> read(
* AsynchronousByteChannel channel, ByteBuffer dst,
* A attachment, CompletionHandler<Integer, A> handler)
* {
* return new PrintReader<A>(channel, dst,
* attachment, handler).start();
* }
*
* private PrintReader(AsynchronousByteChannel channel, ByteBuffer dst,
* A attachment, CompletionHandler<Integer, A> handler)
* {
* super(attachment, handler);
* this.channel = channel;
* this.dst = dst;
* }
*
* protected IoFuture<Integer, A> implStart() {
* System.err.println("Begin reading");
* return channel.read(dst, null);
* }
*
* protected IoFuture<Integer, A> implCompleted(
* IoFuture<Integer, A> result) {
* System.err.println("Done reading");
* return null;
* }
* }
* </pre>
*
* @param <OR> the result type for the outer handler
* @param <OA> the attachment type for the outer handler
* @param <IR> the result type for this handler
* @param <IA> the attachment type for this handler
*/
public abstract class DelegatingCompletionHandler<OR, OA, IR, IA>
extends IoFutureTask<OR, OA>
implements CompletionHandler<IR, IA>
{
/** The associated outer handler. */
private final CompletionHandler<OR, OA> outerHandler;
/** The lock to synchronize on when accessing innerFuture. */
private final Object lock = new Object();
/**
* The inner future associated with the current computation, or {@code
* null} if the inner computation is not underway.
*/
private IoFuture<IR, IA> innerFuture = null;
/**
* Creates an instance for the specified attachment and handler.
*
* @param outerAttachment the attachment for the outer future; may be
* {@code null}
* @param outerHandler the handler to notify or {@code null}
*/
public DelegatingCompletionHandler(
OA outerAttachment, CompletionHandler<OR, OA> outerHandler)
{
/*
* We won't be calling {@code run} on this object anyway, so the
* callable should not be called.
*/
super(new FailingCallable<OR>(), outerAttachment);
this.outerHandler = outerHandler;
}
/* -- Implement CompletionHandler -- */
/**
* Invoked when an inner computation has completed. This method calls
* {@link #implCompleted}, and calls {@link #setException} on the future if
* that method throws an exception.
*
* @param innerResult the result of the inner computation
*/
public final void completed(IoFuture<IR, IA> innerResult) {
synchronized (lock) {
if (!isDone()) {
try {
innerFuture = implCompleted(innerResult);
if (innerFuture == null) {
set(null);
}
} catch (ExecutionException e) {
setException(e.getCause());
} catch (Throwable t) {
setException(t);
}
}
}
}
/* -- Other public methods -- */
/**
* This method should not be called.
*
* @see #start
*/
@Override
public final void run() {
throw new UnsupportedOperationException(
"The run method is not supported");
}
/**
* {@inheritDoc} <p>
*
* This implementation cancels the current future, if any.
*/
@Override
public final boolean cancel(boolean mayInterruptIfRunning) {
synchronized (lock) {
if (isDone()) {
return false;
}
boolean success = (innerFuture == null)
? true : innerFuture.cancel(mayInterruptIfRunning);
if (success) {
success = super.cancel(false);
assert success;
}
return success;
}
}
/**
* Starts the computation and returns a future representing the result of
* the computation.
*
* @return a future representing the result of the computation
*/
public final IoFuture<OR, OA> start() {
synchronized (lock) {
if (!isDone()) {
try {
innerFuture = implStart();
if (innerFuture == null) {
set(null);
}
} catch (Throwable t) {
setException(t);
}
}
return this;
}
}
/* -- Protected methods -- */
/**
* Starts the computation, returning a future for managing the inner
* computation or {@code null} to indicate that the computation is
* completed. Any exception thrown will terminate the computation.
*
* @return the future or {@code null}
*/
protected abstract IoFuture<IR, IA> implStart();
/**
* Called when the delegated computation completes. The implementation
* should return a new future if there is more computation to perform, or
* else {@code null} to indicate that the operation has completed.
* Any exception thrown will terminate the computation. If an {@link
* ExecutionException} is thrown, then its cause will be used.
*
* @param innerResult the result of the delegated computation
* @return a future for managing continued compuation, or {@code null} to
* specify that the computation is done
* @throws Exception if the computation failed
*/
protected abstract IoFuture<IR, IA> implCompleted(
IoFuture<IR, IA> innerResult)
throws Exception;
/**
* Called when the computation is completed, which occurs when {@link
* #implCompleted} returns {@code null} or throws an exception, or when the
* outer future is cancelled. <p>
*
* This implementation runs the outer completion handler. Subclasses that
* override this method should make sure to call this method by calling
* {@code super.done()}.
*/
@Override
protected void done() {
synchronized (lock) {
innerFuture = null;
if (outerHandler != null) {
outerHandler.completed(this);
}
}
}
/* -- Private methods and classes -- */
/** Implements a {@code Callable} that fails if called. */
private static final class FailingCallable<V> implements Callable<V> {
FailingCallable() { }
public V call() {
throw new AssertionError();
}
}
}