package uk.ac.imperial.lsds.seepworker.core;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import uk.ac.imperial.lsds.seep.api.API;
import uk.ac.imperial.lsds.seep.api.RuntimeEvent;
import uk.ac.imperial.lsds.seep.api.RuntimeEventFactory;
import uk.ac.imperial.lsds.seep.api.data.OTuple;
import uk.ac.imperial.lsds.seep.core.EventBasedOBuffer;
import uk.ac.imperial.lsds.seep.core.OBuffer;
import uk.ac.imperial.lsds.seep.errors.DoYouKnowWhatYouAreDoingException;
import uk.ac.imperial.lsds.seepworker.core.output.CoreOutput;
import uk.ac.imperial.lsds.seepworker.core.output.routing.NotEnoughRoutingInformation;
import uk.ac.imperial.lsds.seepworker.core.output.routing.Router;
import uk.ac.imperial.lsds.seepworker.core.output.routing.RouterFactory;
public class Collector implements API {
// Attributes for CommAPI
private final boolean NOT_SEND_API;
private final boolean SINGLE_SEND_NOT_DEFINED;
private final boolean MULTIPLE_STREAMID;
final private int id;
private Map<Integer, List<OBuffer>> streamId_To_OBuffer;
private Map<Integer, OBuffer> buffers;
private Map<Integer, Router> streamId_To_Router;
private Router theRouter;
private OBuffer theOBuffer;
// Attributes for RuntimeEvent
private List<RuntimeEvent> rEvents;
// Current evaluation results
private RuntimeEvent re;
private Object lastEvaluationResults;
public Collector(int id, CoreOutput coreOutput) {
this.rEvents = new ArrayList<>();
this.id = id;
this.streamId_To_OBuffer = coreOutput.getStreamIdToBuffers();
this.buffers = coreOutput.getBuffers();
int numOutputBuffers = buffers.size();
int logicalDownstreams = streamId_To_OBuffer.size();
if(numOutputBuffers == 0) {
NOT_SEND_API = true;
SINGLE_SEND_NOT_DEFINED = true;
MULTIPLE_STREAMID = false;
}
else {
NOT_SEND_API = false; // we can send
if(logicalDownstreams == 1) { // only one streamId
MULTIPLE_STREAMID = false;
if(numOutputBuffers == 1) { // only one physical downstream -> 1 buffer
SINGLE_SEND_NOT_DEFINED = false; // we can send to only one
theOBuffer = buffers.values().iterator().next();
}
else { // multiple physical downstreams of the same streamId
SINGLE_SEND_NOT_DEFINED = true;
// TODO: create one router
List<Integer> oBufferIds = getBufferIds(buffers.values());
boolean stateful = buffers.values().iterator().next().getDataReference().isPartitioned();
theRouter = RouterFactory.buildRouterFor(oBufferIds, stateful);
}
}
else { // multiple streamId
MULTIPLE_STREAMID = true;
SINGLE_SEND_NOT_DEFINED = true;
streamId_To_Router = createRouters(streamId_To_OBuffer);
}
}
}
private List<Integer> getBufferIds(Collection<OBuffer> collection) {
List<Integer> ids = new ArrayList<>();
for(OBuffer buffer : collection) {
ids.add(buffer.getDataReference().getId());
}
return ids;
}
private Map<Integer, Router> createRouters(Map<Integer, List<OBuffer>> streamId_To_OBuffer) {
Map<Integer, Router> routers = new HashMap<>();
for(Entry<Integer, List<OBuffer>> entry : streamId_To_OBuffer.entrySet()) {
int streamId = entry.getKey();
List<Integer> ids = getBufferIds(entry.getValue());
boolean stateful = entry.getValue().iterator().next().getDataReference().isPartitioned();
Router r = RouterFactory.buildRouterFor(ids, stateful);
routers.put(streamId, r);
}
return routers;
}
@Override
public int id() {
return id;
}
@Override
public void send(OTuple o) {
OBuffer ob = theOBuffer;
if(NOT_SEND_API) throw new UnsupportedOperationException("Send API not defined, maybe this is a sink?");
if(MULTIPLE_STREAMID) {
throw new NotEnoughRoutingInformation("There are more than one streamId downstream; you must specify where "
+ "you are sending to");
}
if(SINGLE_SEND_NOT_DEFINED) {
int id = theRouter.route();
ob = buffers.get(id);
}
boolean completed = ob.write(o, this);
//boolean completed = ob.write(o, this);
if(completed && ob instanceof EventBasedOBuffer) {
((EventBasedOBuffer)ob).getEventAPI().readyForWrite(id);
}
}
@Override
public void send(byte[] o) {
OBuffer ob = theOBuffer;
if(NOT_SEND_API) throw new UnsupportedOperationException("Send API not defined, maybe this is a sink?");
if(MULTIPLE_STREAMID) {
throw new NotEnoughRoutingInformation("There are more than one streamId downstream; you must specify where "
+ "you are sending to");
}
if(SINGLE_SEND_NOT_DEFINED) {
int id = theRouter.route();
ob = buffers.get(id);
}
boolean completed = ob.write(o, this);
if(completed && ob instanceof EventBasedOBuffer) {
((EventBasedOBuffer)ob).getEventAPI().readyForWrite(id);
}
}
@Override
public void sendKey(byte[] o, int key) {
if(NOT_SEND_API) throw new UnsupportedOperationException("Send API not defined, maybe this is a sink?");
int id = theRouter.route(key);
OBuffer ob = buffers.get(id);
boolean completed = ob.write(o, this);
if(completed && ob instanceof EventBasedOBuffer) {
((EventBasedOBuffer)ob).getEventAPI().readyForWrite(id);
}
}
@Override
public void sendAll(byte[] o) {
if(NOT_SEND_API) throw new UnsupportedOperationException("Send API not defined, maybe this is a sink?");
if(MULTIPLE_STREAMID) {
throw new NotEnoughRoutingInformation("There are more than one streamId downstream; you must specify where "
+ "you are sending to");
}
// Regardless multiple_send or not
List<Integer> ids = new ArrayList<>();
OBuffer ob = null;
for(Entry<Integer, OBuffer> entry : buffers.entrySet()){
int id = entry.getKey();
ob = entry.getValue();
boolean completed = ob.write(o, this);
if(completed && ob instanceof EventBasedOBuffer) {
ids.add(id);
}
}
if(ids.size() > 0){
((EventBasedOBuffer)ob).getEventAPI().readyForWrite(ids);
}
}
@Override
public void sendKey(byte[] o, String key) {
if(NOT_SEND_API) throw new UnsupportedOperationException("Send API not defined, maybe this is a sink?");
int hashKey = key.hashCode();
this.sendKey(o, hashKey);
}
@Override
public void sendToStreamId(int streamId, byte[] o) {
if(NOT_SEND_API) throw new UnsupportedOperationException("Send API not defined, maybe this is a sink?");
if(! MULTIPLE_STREAMID) throw new UnsupportedOperationException("Only one streamId available!");
Router r = streamId_To_Router.get(streamId);
int id = r.route();
OBuffer ob = buffers.get(id);
boolean completed = ob.write(o, this);
if(completed && ob instanceof EventBasedOBuffer) {
((EventBasedOBuffer)ob).getEventAPI().readyForWrite(id);
}
}
@Override
public void sendToAllInStreamId(int streamId, byte[] o) {
// TODO Auto-generated method stub
}
@Override
public void sendStreamidKey(int streamId, byte[] o, int key) {
// TODO Auto-generated method stub
}
@Override
public void sendStreamidKey(int streamId, byte[] o, String key) {
// TODO Auto-generated method stub
}
@Override
public void send_index(int index, byte[] o) {
throw new DoYouKnowWhatYouAreDoingException("This is mostly a debugging method, you should not be playing with the"
+ "underlying communication directly otherwise...");
}
@Override
public void send_opid(int opId, byte[] o) {
throw new DoYouKnowWhatYouAreDoingException("You seem to know too much about the topology of this dataflow...");
}
/**
* Implement RuntimeEvent interface
*/
@Override
public List<RuntimeEvent> getRuntimeEvents() {
// Lazily add evaluation results if any
if(lastEvaluationResults != null) {
re = RuntimeEventFactory.makeEvaluateResults(lastEvaluationResults);
rEvents.add(re);
}
return rEvents;
}
@Override
public void exception(String message) {
// TODO: implement
try {
throw new Exception();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public void datasetSpilledToDisk(int datasetId) {
RuntimeEvent re = RuntimeEventFactory.makeSpillToDiskRuntimeEvent(datasetId);
this.rEvents.add(re);
}
@Override
public void failure() {
// TODO: implement
try {
throw new Exception();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public void notifyEndOfLoop() {
RuntimeEvent re = RuntimeEventFactory.makeNotifyEndOfLoop();
this.rEvents.add(re);
}
@Override
public void storeEvaluateResults(Object obj) {
// Only update state (there will be one evaluation results per Evaluator then)
// Then re will be added to the other runtimeEvents when get(rEvents).
this.lastEvaluationResults = obj;
// re = RuntimeEventFactory.makeEvaluateResults(obj);
//this.rEvents.add(re);
}
}