package com.zillabyte.motherbrain.flow.operations.multilang;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.apache.log4j.Logger;
import com.google.monitoring.runtime.instrumentation.common.com.google.common.collect.Lists;
import com.zillabyte.motherbrain.flow.MapTuple;
import com.zillabyte.motherbrain.flow.operations.Operation;
import com.zillabyte.motherbrain.flow.operations.OperationException;
import com.zillabyte.motherbrain.flow.operations.OperationLogger;
import com.zillabyte.motherbrain.utils.JSONUtil;
/***
* Handles general work loads (i.e. responding to fails, logging, etc)
* @author jake
*
*/
public class MultiLangProcessGeneralOperationObserver implements MultiLangMessageHandler, MultiLangLogHandler, MultiLangErrorHandler {
private static Logger _log = Logger.getLogger(MultiLangProcessGeneralOperationObserver.class);
private MultiLangProcess _proc;
private Operation _operation;
private LinkedBlockingQueue<Exception> _errors = new LinkedBlockingQueue<>();
public MultiLangProcessGeneralOperationObserver(MultiLangProcess proc, Operation operation) {
_proc = proc;
_proc.addMessageListener(this);
_proc.addLogListener(this);
_proc.addErrorListener(this);
_operation = operation;
}
@Override
public void onStdErr(String s, Logger fallbackLogger) {
_operation.logger().writeLog(s, OperationLogger.LogPriority.ERROR);
}
@Override
public void onStdOut(String s, Logger fallbackLogger) {
// Send stdout messages that are not part of our protocol. Kinda hacky
if (s.equalsIgnoreCase("end")) return;
if (s.startsWith("{")) return;
if (s.length() == 0) return;
_operation.logger().writeLog(s, OperationLogger.LogPriority.RUN);
}
@Override
public void onSystemError(String s, Logger fallbackLogger) {
_operation.logger().writeLog(s, OperationLogger.LogPriority.ERROR);
}
@Override
public void onSystemInfo(String s, Logger fallbackLogger) {
_operation.logger().writeLog(s, OperationLogger.LogPriority.SYSTEM);
}
/***
*
*/
public void detach() {
this._proc.removeLogListener(this);
this._proc.removeMessageListener(this);
this._proc.removeErrorListener(this);
}
/***
*
* @param obj
*/
protected void handleJsonMessage(JSONObject obj) {
// For subclasses
}
/**
* @throws InterruptedException
*
*/
@Override
public void handleMessage(String line) throws OperationException, InterruptedException {
// Sanity
if (line == null) return;
if (line.equals("end")) return;
if (line.length() == 0) return;
// Init
JSONObject obj = JSONUtil.parseObj(line);
if (obj.has("ping")) {
// The process is making sure we're alive. Respond with a 'pong'
try {
_proc.writeMessageWithEnd("{\"pong\": \"" + System.currentTimeMillis() + "\"}");
} catch (MultiLangProcessException e) {
throw new OperationException(_operation, e);
}
} else if (obj.has("command") && obj.getString("command").equalsIgnoreCase("fail")) {
// Errors!
_log.error("error: " + obj.getString("msg"));
// Tell the operation to ERROR
throw (OperationException)
new OperationException(_operation)
.setUserMessage(obj.getString("msg"))
.setInternalMessage(obj.getString("msg"));
} else if (obj.has("command") && obj.getString("command").equalsIgnoreCase("log")) {
_operation.logger().writeLog(obj.getString("msg"), OperationLogger.LogPriority.RUN);
} else {
handleJsonMessage(obj);
}
}
/***
* A helper method to send message down
* @param t
* @throws MultiLangProcessDeadException
* @throws InterruptedException
*/
public void sendTupleMessage(MapTuple t) throws MultiLangProcessException, InterruptedException {
// CLI: Make sure this is synced
JSONObject obj = new JSONObject();
obj.put("tuple", t.getValuesJSON());
// _log.info("sending tuple message: " + obj.toString());
_proc.writeMessageWithEnd(obj.toString());
}
public void sendTuplesMessage(Collection<MapTuple> col) throws MultiLangProcessException, InterruptedException {
JSONObject obj = new JSONObject();
List<JSONObject> list = Lists.newLinkedList();
for(MapTuple t : col) {
list.add(t.getValuesJSON());
}
obj.put("tuples", list);
// _log.info("sending tuple message: " + obj.toString());
_proc.writeMessageWithEnd(obj.toString());
}
/***
*
* @param t
* @throws MultiLangProcessDeadException
* @throws InterruptedException
*/
public void sendBeginGroup(MapTuple t) throws MultiLangProcessException, InterruptedException {
// CLI
JSONObject obj = new JSONObject();
obj.put("command", "begin_group");
obj.put("tuple", t.getValuesJSON());
// _log.info("sending group begin message: " + obj.toString());
_proc.writeMessage(obj.toString());
_proc.writeMessage("end");
}
/***
*
* @param t
* @param aliases
* @throws MultiLangProcessDeadException
* @throws InterruptedException
*/
public void sendAggregate(MapTuple t, JSONArray aliases) throws MultiLangProcessException, InterruptedException {
// CLI
JSONObject obj = new JSONObject();
obj.put("command", "aggregate");
obj.put("tuple", t.getValuesJSON());
if (aliases != null) {
obj.put("column_aliases", aliases);
}
//_log.debug("sending aggregate message: " + obj.toString());
_proc.writeMessage(obj.toString());
_proc.writeMessage("end");
}
/**
* @throws MultiLangProcessDeadException
* @throws InterruptedException *
*
*/
public void sendEndGroup() throws MultiLangProcessException, InterruptedException {
JSONObject obj = new JSONObject();
obj.put("command", "end_group");
//_log.info("sending end group message: " + obj.toString());
_proc.writeMessage(obj.toString());
_proc.writeMessage("end");
}
@Override
public void handleError(Exception ex) {
_log.info("error captured: " + ex);
_errors.add(ex);
}
@Override
public Exception getNextError() {
return _errors.poll();
}
@Override
public void maybeThrowNextError() throws OperationException {
final Exception ex = getNextError();
if (ex != null) {
throw new OperationException(this._operation, ex);
}
}
}