/*
* Copyright 2009 Thomas Bocek
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package net.tomp2p.futures;
import java.util.ArrayList;
import java.util.List;
/**
* FutureLateJoin is similar to FutureForkJoin. The main difference is that with
* this class you don't need to specify all the futures in advance. You can just
* tell how many futures you expect and add them later on.
*
* @author Thomas Bocek
* @param <K>
*/
public class FutureLateJoin<K extends BaseFuture> extends BaseFutureImpl<FutureLateJoin<K>> implements BaseFuture {
private final int nrMaxFutures;
private final int minSuccess;
private final List<K> futuresDone;
private K lastSuceessFuture;
private int successCount = 0;
/**
* Create this future and set the minSuccess to the number of expected
* futures.
*
* @param nrMaxFutures
* The number of expected futures.
*/
public FutureLateJoin(final int nrMaxFutures) {
this(nrMaxFutures, nrMaxFutures);
}
/**
* Create this future.
*
* @param nrMaxFutures
* The number of expected futures.
* @param minSuccess
* The number of expected successful futures.
*/
public FutureLateJoin(final int nrMaxFutures, final int minSuccess) {
this.nrMaxFutures = nrMaxFutures;
this.minSuccess = minSuccess;
this.futuresDone = new ArrayList<K>(nrMaxFutures);
self(this);
}
/**
* Add a future when ready. This is why its called FutureLateJoin, since you
* can add futures later on.
*
* @param future
* The future to be added.
* @return True if the future was added to the futurelist, false if the
* latejoin future is already finished and the future was not added.
*/
public boolean add(final K future) {
synchronized (lock) {
if (completed) {
return false;
}
future.addListener(new BaseFutureAdapter<K>() {
@Override
public void operationComplete(final K future) throws Exception {
boolean done = false;
synchronized (lock) {
if (!completed) {
if (future.isSuccess()) {
successCount++;
lastSuceessFuture = future;
}
futuresDone.add(future);
done = checkDone();
}
}
if (done) {
notifyListerenrs();
}
}
});
return true;
}
}
/**
* Check if the can set this future to done.
*
* @return True if we are done.
*/
private boolean checkDone() {
if (futuresDone.size() >= nrMaxFutures || successCount >= minSuccess) {
if (!setCompletedAndNotify()) {
return false;
}
boolean isSuccess = successCount >= minSuccess;
type = isSuccess ? FutureType.OK : FutureType.FAILED;
reason = isSuccess ? "Minimal number of futures received" : "Minimal number of futures *not* received ("
+ successCount + " of " + minSuccess + " reached)";
return true;
}
return false;
}
/**
* Returns the finished futures.
*
* @return All the futures that are done.
*/
public List<K> getFuturesDone() {
synchronized (lock) {
return futuresDone;
}
}
/**
* @return the last successful finished future.
*/
public K getLastSuceessFuture() {
synchronized (lock) {
return lastSuceessFuture;
}
}
}