package com.scaleunlimited.cascading;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import cascading.flow.FlowProcess;
import cascading.operation.BaseOperation;
import cascading.operation.Buffer;
import cascading.operation.BufferCall;
import cascading.operation.OperationCall;
import cascading.tuple.Fields;
import cascading.tuple.Tuple;
import cascading.tuple.TupleEntry;
import cascading.tuple.TupleEntryCollector;
@SuppressWarnings("serial")
public abstract class BaseBuffer extends BaseOperation<NullContext> implements Buffer<NullContext> {
private static final Logger LOGGER = LoggerFactory.getLogger(BaseBuffer.class);
@SuppressWarnings("rawtypes")
protected transient LoggingFlowProcess _flowProcess;
protected transient TupleEntryCollector _collector;
public BaseBuffer(Fields resultFields) {
super(resultFields);
}
// Classes extending BaseBuffer must implement this method
abstract public void process(BufferCall<NullContext> bufferCall) throws Exception;
// Default, do-nothing implementations of overridable methods
public void prepare() throws Exception {}
public void cleanup() throws Exception {}
public boolean handlePrepareException(Throwable t) { return false; }
public boolean handleCleanupException(Throwable t) { return false; }
@SuppressWarnings({ "rawtypes", "unchecked" })
@Override
public void prepare(FlowProcess flowProcess, OperationCall<NullContext> operationCall) {
super.prepare(flowProcess, operationCall);
_flowProcess = new LoggingFlowProcess(flowProcess);
_flowProcess.addReporter(new LoggingFlowReporter());
String bufferClassName = this.getClass().getSimpleName();
try {
LOGGER.info("Starting " + bufferClassName);
prepare();
} catch (Throwable t) {
if (!handlePrepareException(t)) {
LOGGER.error("Unhandled exception while preparing " + bufferClassName, t);
if (t instanceof RuntimeException) {
throw (RuntimeException)t;
} else {
throw new RuntimeException(t);
}
}
}
}
@SuppressWarnings("rawtypes")
@Override
public void cleanup(FlowProcess flowProcess, OperationCall<NullContext> operationCall) {
String bufferClassName = this.getClass().getSimpleName();
try {
LOGGER.info("Ending " + bufferClassName);
cleanup();
} catch (Throwable t) {
if (!handleCleanupException(t)) {
LOGGER.error("Unhandled exception while cleaning up " + bufferClassName, t);
if (t instanceof RuntimeException) {
throw (RuntimeException)t;
} else {
throw new RuntimeException(t);
}
}
}
_flowProcess.dumpCounters();
super.cleanup(flowProcess, operationCall);
}
@SuppressWarnings("rawtypes")
@Override
public void operate(FlowProcess flowProcess, BufferCall<NullContext> bufferCall) {
if (_collector == null) {
_collector = bufferCall.getOutputCollector();
}
try {
process(bufferCall);
} catch (Throwable t) {
LOGGER.error("Unhandled exception while processing group: " + safeToString(bufferCall.getGroup()), t);
if (t instanceof RuntimeException) {
throw (RuntimeException)t;
} else {
throw new RuntimeException(t);
}
}
}
protected void emit(TupleEntry out) {
_collector.add(out);
}
protected void emit(Tuple out) {
_collector.add(out);
}
@SuppressWarnings("rawtypes")
protected void incrementCounter(Enum counter, long amount) {
// Work around Cascading API bug where it only takes an int, not a long.
while (amount > Integer.MAX_VALUE) {
_flowProcess.increment(counter, Integer.MAX_VALUE);
amount -= Integer.MAX_VALUE;
}
_flowProcess.increment(counter, (int)amount);
}
private String safeToString(TupleEntry te) {
try {
return te.toString();
} catch (Throwable t) {
LOGGER.error("Exception converting TupleEntry to string", t);
return "<non-stringable object>";
}
}
}