package com.zillabyte.motherbrain.flow.operations.multilang.operations;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import net.sf.json.JSONObject;
import com.google.monitoring.runtime.instrumentation.common.com.google.common.collect.Lists;
import com.zillabyte.motherbrain.api.APIException;
import com.zillabyte.motherbrain.api.RestAPIHelper;
import com.zillabyte.motherbrain.flow.Fields;
import com.zillabyte.motherbrain.flow.MapTuple;
import com.zillabyte.motherbrain.flow.collectors.OutputCollector;
import com.zillabyte.motherbrain.flow.graph.Connection;
import com.zillabyte.motherbrain.flow.operations.Function;
import com.zillabyte.motherbrain.flow.operations.OperationException;
import com.zillabyte.motherbrain.flow.operations.multilang.MultiLangException;
import com.zillabyte.motherbrain.universe.Universe;
import com.zillabyte.motherbrain.utils.Utils;
public final class LocalComponent extends Function {
private static final long serialVersionUID = -6784282062699640773L;
private String _componentName;
private JSONObject _componentMeta;
private Map<String, String> _inputFields = new LinkedHashMap<String, String>();
private Map<String, String> _outputFields = new LinkedHashMap<String, String>();
public LocalComponent(JSONObject nodeSettings) {
super(MultilangHandler.getName(nodeSettings), MultilangHandler.getConfig(nodeSettings));
_componentName = nodeSettings.getString("id");
try {
_componentMeta = RestAPIHelper.get("/flows/"+_componentName, (String) Universe.instance().config().getOrException("auth.token"));
} catch (APIException e) {
throw new RuntimeException(e);
}
// Construct the input fields and output fields
Iterator<?> nodeIterator = _componentMeta.getJSONArray("nodes").iterator();
while(nodeIterator.hasNext()) {
JSONObject node = (JSONObject) nodeIterator.next();
if(node.getString("type").equalsIgnoreCase("input")) {
Iterator<?> iFieldsIterator = node.getJSONArray("fields").iterator();
while(iFieldsIterator.hasNext()) {
JSONObject fields = (JSONObject) iFieldsIterator.next();
String iField = (String) fields.keys().next();
String iType = fields.getString(iField);
_inputFields.put(iField, iType);
}
} else if(node.getString("type").equalsIgnoreCase("output")) {
Iterator<?> iFieldsIterator = node.getJSONArray("columns").iterator();
while(iFieldsIterator.hasNext()) {
JSONObject fields = (JSONObject) iFieldsIterator.next();
String iField = (String) fields.keys().next();
String iType = fields.getString(iField);
_outputFields.put(iField, iType);
}
}
}
}
@Override
public void prepare() throws MultiLangException {
}
@Override
public void cleanup() throws MultiLangException, InterruptedException {
}
@Override
public void process(final MapTuple t, final OutputCollector collector) throws OperationException, InterruptedException {
String authToken = this.getTopFlow().getFlowConfig().getAuthToken();
try {
// Translate the map tuple into a list of just the values (order is important, so we need to loop through the fields one by one).
// This will be passed to a running rpc as an argument
List<Object> tupleValues = Lists.newArrayList();
for(String field : _inputFields.keySet()) {
tupleValues.add(t.get(field));
}
List<Collection<Object>> tupleList = new ArrayList<Collection<Object>>();
tupleList.add(tupleValues);
// Send the rpc request
JSONObject body = new JSONObject();
body.put("rpc_inputs", tupleList);
JSONObject reply = RestAPIHelper.post("/components/"+_componentName+"/execute", body.toString(), authToken);
Collection<?> execId = reply.getJSONObject("execute_ids").values();
JSONObject execBody = new JSONObject();
execBody.put("execute_ids", execId);
// Continue to ping the rpc until it is done
while(true) {
JSONObject rpcStatus = RestAPIHelper.get("/components/"+_componentName+"/execute", authToken, execBody);
if(rpcStatus.containsKey("results")) {
JSONObject rpcResults = (JSONObject) rpcStatus.getJSONObject("results").values().iterator().next();
if(rpcResults.containsKey("data")) {
Iterator<?> rpcTupleIterator = rpcResults.getJSONObject("data").getJSONArray("rpc_output").iterator();
while(rpcTupleIterator.hasNext()) {
JSONObject emitTuple = (JSONObject) rpcTupleIterator.next();
collector.emit(MapTuple.create(emitTuple));
}
break;
}
}
Utils.sleep(1000L);
}
} catch (APIException e) {
throw (OperationException) new OperationException(this,e).setAllMessages("Error processing tuple: "+t.toString()+" (via remote RPC for component \""+_componentName+"\").");
}
}
@Override
public boolean isAlive() {
return true;
}
@Override
public void onFinalizeDeclare() throws OperationException, InterruptedException {
super.onFinalizeDeclare();
}
@Override
public void onSetExpectedFields() throws OperationException {
for(final Connection c : this.prevConnections()) {
for(String field : _inputFields.keySet()) {
c.source().addExpectedFields(c.streamName(), new Fields(field));
}
}
super.onSetExpectedFields();
}
}