/**
* Copyright 2010 TransPac Software, Inc.
*
* 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 com.scaleunlimited.cascading;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import cascading.flow.FlowProcess;
import cascading.operation.BaseOperation;
import cascading.operation.Function;
import cascading.operation.FunctionCall;
import cascading.operation.OperationCall;
import cascading.tuple.TupleEntryCollector;
@SuppressWarnings({"serial", "rawtypes"})
public abstract class BaseFunction<INDATUM extends BaseDatum, OUTDATUM extends BaseDatum> extends BaseOperation<NullContext> implements Function<NullContext> {
private static final Logger LOGGER = LoggerFactory.getLogger(BaseFunction.class);
private INDATUM _inDatum;
private OUTDATUM _outDatum;
private TupleEntryCollector _collector;
private LoggingFlowProcess _flowProcess;
public BaseFunction(Class<INDATUM> inClass, Class<OUTDATUM> outClass) throws Exception {
super(outClass.newInstance().getFields());
_inDatum = inClass.newInstance();
_outDatum = outClass.newInstance();
}
@SuppressWarnings("unchecked")
@Override
public final void prepare(FlowProcess process, OperationCall<NullContext> opCall) {
super.prepare(process, opCall);
_flowProcess = new LoggingFlowProcess(process);
_collector = ((FunctionCall)opCall).getOutputCollector();
try {
prepare();
} catch (Throwable t) {
if (!handlePrepareException(t)) {
LOGGER.error("Unhandled exception while preparing", t);
if (t instanceof RuntimeException) {
throw (RuntimeException)t;
} else {
throw new RuntimeException(t);
}
}
}
}
@Override
public final void cleanup(FlowProcess flowProcess, OperationCall<NullContext> opCall) {
super.cleanup(flowProcess, opCall);
try {
cleanup();
} catch (Throwable t) {
if (!handleCleanupException(t)) {
LOGGER.error("Unhandled exception while cleaning up", t);
if (t instanceof RuntimeException) {
throw (RuntimeException)t;
} else {
throw new RuntimeException(t);
}
}
}
}
@Override
public final void operate(FlowProcess process, FunctionCall<NullContext> funcCall) {
_inDatum.setTupleEntry(funcCall.getArguments());
try {
process(_inDatum);
} catch (Throwable t) {
if (!handleProcessException(_inDatum, t)) {
LOGGER.error("Unhandled exception while processing datum: " + safeToString(_inDatum), t);
if (t instanceof RuntimeException) {
throw (RuntimeException)t;
} else {
throw new RuntimeException(t);
}
}
}
}
private String safeToString(Object o) {
try {
return o.toString();
} catch (Throwable t) {
LOGGER.error("Exception converting object to string", t);
return "<non-stringable object>";
}
}
public final void emit(OUTDATUM out) {
_collector.add(out.getTuple());
}
public OUTDATUM getOutDatum() {
return _outDatum;
}
public LoggingFlowProcess getFlowProcess() {
return _flowProcess;
}
public void prepare() throws Exception {}
abstract void process(final INDATUM in) throws Exception;
public void cleanup() throws Exception {}
public boolean handlePrepareException(Throwable t) { return false; }
public boolean handleProcessException(final INDATUM in, Throwable t) { return false; }
public boolean handleCleanupException(Throwable t) { return false; }
}