/* * This file is part of the HyperGraphDB source distribution. This is copyrighted * software. For permitted uses, licensing options and redistribution, please see * the LicensingInformation file at the root level of the distribution. * * Copyright (c) 2005-2010 Kobrix Software, Inc. All rights reserved. */ package org.hypergraphdb.util; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; /** * * <p> * An <code>CallbackFuture</code> offers the possibility to call back a registered * listener when it is completed. To avoid taking up a thread and waiting for * completion, this is accomplished by required the creator of the future * to invoke the <code>completed</code> method when the result this * <code>Future</code> represents has been computed. * </p> * * <p> * Only one listener can be registered for call back and it is of type * Mapping<CallbackFuture<T>, T>. That is, a mapping that will receive * the <code>CallbackFuture</code> itself as an argument and must return * the result of the <code>Future</code>. Typically the mapping will * just return the <code>CallbackFuture</code>'s own result by * calling the <code>getResult</code>, but it may choose to assign a different * result. Note that blocking calls to <code>get</code> will not return * until the registered listener (if any) returns, so the listener * must rely on the provided <code>getResult</code> to obtain the * <code>Future</code>'s result value. * </p> * * <p> * This class may be extended to provide semantics for cancellation. In this * case the <code>cancel</code> method must be overridden and the * <code>canceled</code> flag must be set to <code>true</code> if cancellation * was successful. * </p> * * @author Borislav Iordanov * */ public class CallbackFuture<T> implements Future<T> { private CountDownLatch latch; private T result; private Mapping<CallbackFuture<T>, T> listener; protected volatile boolean canceled = false; public CallbackFuture() { latch = new CountDownLatch(1); } public synchronized void setCompletionListener(Mapping<CallbackFuture<T>, T> listener) { this.listener = listener; } public synchronized void complete(T result) { if (isDone()) throw new IllegalStateException("JobFuture completion attempted after it was done."); this.result = result; latch.countDown(); if (listener != null) result = listener.eval(this); } public T getResult() { return result; } public boolean cancel(boolean mayInterruptIfRunning) { return false; } public T get() throws InterruptedException, ExecutionException { latch.await(); return result; } public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { if (latch.await(timeout, unit)) return result; else return null; } public boolean isCancelled() { return canceled; } public boolean isDone() { return latch.getCount() == 0 && !canceled; } }