001/*
002 * ====================================================================
003 * Licensed to the Apache Software Foundation (ASF) under one
004 * or more contributor license agreements.  See the NOTICE file
005 * distributed with this work for additional information
006 * regarding copyright ownership.  The ASF licenses this file
007 * to you under the Apache License, Version 2.0 (the
008 * "License"); you may not use this file except in compliance
009 * with the License.  You may obtain a copy of the License at
010 *
011 *   http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing,
014 * software distributed under the License is distributed on an
015 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
016 * KIND, either express or implied.  See the License for the
017 * specific language governing permissions and limitations
018 * under the License.
019 * ====================================================================
020 *
021 * This software consists of voluntary contributions made by many
022 * individuals on behalf of the Apache Software Foundation.  For more
023 * information on the Apache Software Foundation, please see
024 * <http://www.apache.org/>.
025 *
026 */
027package org.apache.http.concurrent;
028
029import org.apache.http.util.Args;
030
031import java.util.concurrent.CancellationException;
032import java.util.concurrent.ExecutionException;
033import java.util.concurrent.Future;
034import java.util.concurrent.TimeUnit;
035import java.util.concurrent.TimeoutException;
036
037/**
038 * Basic implementation of the {@link Future} interface. {@code BasicFuture}
039 * can be put into a completed state by invoking any of the following methods:
040 * {@link #cancel()}, {@link #failed(Exception)}, or {@link #completed(Object)}.
041 *
042 * @param <T> the future result type of an asynchronous operation.
043 * @since 4.2
044 */
045public class BasicFuture<T> implements Future<T>, Cancellable {
046
047    private final FutureCallback<T> callback;
048
049    private volatile boolean completed;
050    private volatile boolean cancelled;
051    private volatile T result;
052    private volatile Exception ex;
053
054    public BasicFuture(final FutureCallback<T> callback) {
055        super();
056        this.callback = callback;
057    }
058
059    @Override
060    public boolean isCancelled() {
061        return this.cancelled;
062    }
063
064    @Override
065    public boolean isDone() {
066        return this.completed;
067    }
068
069    private T getResult() throws ExecutionException {
070        if (this.ex != null) {
071            throw new ExecutionException(this.ex);
072        }
073        if (cancelled) {
074            throw new CancellationException();
075        }
076        return this.result;
077    }
078
079    @Override
080    public synchronized T get() throws InterruptedException, ExecutionException {
081        while (!this.completed) {
082            wait();
083        }
084        return getResult();
085    }
086
087    @Override
088    public synchronized T get(final long timeout, final TimeUnit unit)
089            throws InterruptedException, ExecutionException, TimeoutException {
090        Args.notNull(unit, "Time unit");
091        final long msecs = unit.toMillis(timeout);
092        final long startTime = (msecs <= 0) ? 0 : System.currentTimeMillis();
093        long waitTime = msecs;
094        if (this.completed) {
095            return getResult();
096        } else if (waitTime <= 0) {
097            throw new TimeoutException();
098        } else {
099            for (;;) {
100                wait(waitTime);
101                if (this.completed) {
102                    return getResult();
103                } else {
104                    waitTime = msecs - (System.currentTimeMillis() - startTime);
105                    if (waitTime <= 0) {
106                        throw new TimeoutException();
107                    }
108                }
109            }
110        }
111    }
112
113    public boolean completed(final T result) {
114        synchronized(this) {
115            if (this.completed) {
116                return false;
117            }
118            this.completed = true;
119            this.result = result;
120            notifyAll();
121        }
122        if (this.callback != null) {
123            this.callback.completed(result);
124        }
125        return true;
126    }
127
128    public boolean failed(final Exception exception) {
129        synchronized(this) {
130            if (this.completed) {
131                return false;
132            }
133            this.completed = true;
134            this.ex = exception;
135            notifyAll();
136        }
137        if (this.callback != null) {
138            this.callback.failed(exception);
139        }
140        return true;
141    }
142
143    @Override
144    public boolean cancel(final boolean mayInterruptIfRunning) {
145        synchronized(this) {
146            if (this.completed) {
147                return false;
148            }
149            this.completed = true;
150            this.cancelled = true;
151            notifyAll();
152        }
153        if (this.callback != null) {
154            this.callback.cancelled();
155        }
156        return true;
157    }
158
159    @Override
160    public boolean cancel() {
161        return cancel(true);
162    }
163
164}