package com.scaleunlimited.cascading;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import cascading.flow.Flow;
import cascading.flow.FlowListener;
@SuppressWarnings("rawtypes")
public class FlowFuture implements Future<FlowResult> {
private static final Logger LOGGER = LoggerFactory.getLogger(FlowFuture.class);
private static final long FLOW_SLEEP_TIME = 1 * 1000L;
private Flow _flow;
private volatile Throwable _flowException;
private volatile boolean _canceled;
private volatile boolean _done;
public FlowFuture(Flow flow) {
_flow = flow;
_flowException = null;
FlowListener catchExceptions = new FlowListener() {
@Override
public void onCompleted(Flow flow) {
_done = true;
}
@Override
public void onStarting(Flow flow) { }
@Override
public void onStopping(Flow flow) {
_canceled = true;
}
@Override
public boolean onThrowable(Flow flow, Throwable t) {
_flowException = t;
return true;
}
};
_flow.addListener(catchExceptions);
LOGGER.info("Starting flow " + flow.getName());
_flow.start();
}
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
if (_canceled || _done) {
return false;
}
// We start running right away, so we have to interrupt.
if (!mayInterruptIfRunning) {
return false;
}
// Stop the flow. This will (eventually) set up the _done & _cancelled flags.
FlowUtils.safeStop(_flow);
// Wait until the onStopping AND onComplete listeners have been called.
while (!_canceled || !_done) {
try {
Thread.sleep(FLOW_SLEEP_TIME);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
return true;
}
public Flow getFlow() {
return _flow;
}
@Override
public FlowResult get() throws InterruptedException, ExecutionException {
while (!_done) {
Thread.sleep(FLOW_SLEEP_TIME);
}
if (_canceled) {
throw new CancellationException();
}
if (_flowException != null) {
throw new ExecutionException(_flowException);
}
return makeFlowResult();
}
@Override
public FlowResult get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
long remainingTimeNS = unit.toNanos(timeout);
final long defaultWaitNS = TimeUnit.MILLISECONDS.toNanos(FLOW_SLEEP_TIME);
while (!_done && (remainingTimeNS > 0)) {
long waitTimeNS = Math.min(defaultWaitNS, remainingTimeNS);
remainingTimeNS -= waitTimeNS;
long ms = TimeUnit.NANOSECONDS.toMillis(waitTimeNS);
int ns = (int)(waitTimeNS - TimeUnit.MILLISECONDS.toNanos(ms));
Thread.sleep(ms, ns);
}
if (_canceled) {
throw new CancellationException();
}
if (!_done) {
throw new TimeoutException();
}
if (_flowException != null) {
throw new ExecutionException(_flowException);
}
return makeFlowResult();
}
@Override
public boolean isCancelled() {
return _canceled;
}
@Override
public boolean isDone() {
return _done;
}
private FlowResult makeFlowResult() {
// Map<String, Long> result = new HashMap<String, Long>();
//
// FlowStats stats = _flow.getFlowStats();
// Collection<String> counterGroups = stats.getCounterGroups();
// for (String counterGroup : counterGroups) {
// Collection<String> counters = stats.getCountersFor(counterGroup);
// for (String counter : counters) {
// long counterValue = stats.getCounterValue(counterGroup, counter);
// String counterName = counterGroup + "." + counter;
// if (result.containsKey(counterName)) {
// result.put(counterName, counterValue + result.get(counterName));
// } else {
// result.put(counterName, counterValue);
// }
// }
// }
//
LOGGER.info("Flow " + _flow.getName() + " has completed");
return new FlowResult(FlowCounters.getCounters(_flow));
}
}