--- /dev/null
+//
+// ========================================================================
+// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.util;
+
+import java.io.IOException;
+import java.util.concurrent.CancellationException;
+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;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+public class FutureCallback implements Future<Void>,Callback
+{
+ private static Throwable COMPLETED=new Throwable();
+ private final AtomicBoolean _done=new AtomicBoolean(false);
+ private final CountDownLatch _latch=new CountDownLatch(1);
+ private Throwable _cause;
+
+ public FutureCallback()
+ {}
+
+ public FutureCallback(boolean completed)
+ {
+ if (completed)
+ {
+ _cause=COMPLETED;
+ _done.set(true);
+ _latch.countDown();
+ }
+ }
+
+ public FutureCallback(Throwable failed)
+ {
+ _cause=failed;
+ _done.set(true);
+ _latch.countDown();
+ }
+
+ @Override
+ public void succeeded()
+ {
+ if (_done.compareAndSet(false,true))
+ {
+ _cause=COMPLETED;
+ _latch.countDown();
+ }
+ }
+
+ @Override
+ public void failed(Throwable cause)
+ {
+ if (_done.compareAndSet(false,true))
+ {
+ _cause=cause;
+ _latch.countDown();
+ }
+ }
+
+ @Override
+ public boolean cancel(boolean mayInterruptIfRunning)
+ {
+ if (_done.compareAndSet(false,true))
+ {
+ _cause=new CancellationException();
+ _latch.countDown();
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean isCancelled()
+ {
+ if (_done.get())
+ {
+ try
+ {
+ _latch.await();
+ }
+ catch (InterruptedException e)
+ {
+ throw new RuntimeException(e);
+ }
+ return _cause instanceof CancellationException;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean isDone()
+ {
+ return _done.get() && _latch.getCount()==0;
+ }
+
+ @Override
+ public Void get() throws InterruptedException, ExecutionException
+ {
+ _latch.await();
+ if (_cause==COMPLETED)
+ return null;
+ if (_cause instanceof CancellationException)
+ throw (CancellationException) new CancellationException().initCause(_cause);
+ throw new ExecutionException(_cause);
+ }
+
+ @Override
+ public Void get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException
+ {
+ if (!_latch.await(timeout,unit))
+ throw new TimeoutException();
+
+ if (_cause==COMPLETED)
+ return null;
+ if (_cause instanceof TimeoutException)
+ throw (TimeoutException)_cause;
+ if (_cause instanceof CancellationException)
+ throw (CancellationException) new CancellationException().initCause(_cause);
+ throw new ExecutionException(_cause);
+ }
+
+ public static void rethrow(ExecutionException e) throws IOException
+ {
+ Throwable cause=e.getCause();
+ if (cause instanceof IOException)
+ throw (IOException)cause;
+ if (cause instanceof Error)
+ throw (Error)cause;
+ if (cause instanceof RuntimeException)
+ throw (RuntimeException)cause;
+ throw new RuntimeException(cause);
+ }
+
+ @Override
+ public String toString()
+ {
+ return String.format("FutureCallback@%x{%b,%b}",hashCode(),_done,_cause==COMPLETED);
+ }
+
+}