package com.zillabyte.motherbrain.flow.rpc; import java.util.ArrayList; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.LinkedBlockingQueue; import org.apache.log4j.Logger; import com.google.common.base.Throwables; import com.zillabyte.motherbrain.flow.MapTuple; import com.zillabyte.motherbrain.flow.collectors.coordinated.BatchedTuple; import com.zillabyte.motherbrain.flow.operations.OperationException; import com.zillabyte.motherbrain.flow.operations.Sink; import com.zillabyte.motherbrain.flow.rpc.queues.OutputQueue; import com.zillabyte.motherbrain.universe.Universe; import com.zillabyte.motherbrain.utils.Utils; public class RPCSink extends Sink { private static final long serialVersionUID = 1621223735806880121L; private static Logger log = Utils.getLogger(RPCSink.class); private OutputQueue _outputQueue; private ConcurrentHashMap<Object, LinkedBlockingQueue<MapTuple>> _sinks; /*** * * @param node */ public RPCSink(String name) { super(name); _sinks = new ConcurrentHashMap<>(); } /**** * */ @Override public void prepare() throws InterruptedException { _outputQueue = Universe.instance().rpcQueueFactory().getOutputQueue(this); // appears to be necessary for tests to pass even if already done in constructor } /*** * */ @Override protected void process(MapTuple t) throws InterruptedException, OperationException { // Init log.debug("rpc sinking: " + t); if (t instanceof BatchedTuple == false) throw new IllegalStateException(); BatchedTuple bt = (BatchedTuple) t; // Save the tuple for later... final Object id = bt.batchId(); if (_sinks.containsKey(id) == false) { _sinks.put(id, new LinkedBlockingQueue<MapTuple>()); } LinkedBlockingQueue<MapTuple> sink = _sinks.get(id); sink.put(t); } /*** * */ @Override public void onThisBatchCompleted(Object batchId) { // Build the response... log.info("batch completed: " + batchId); final String id = batchId.toString(); assert (id != null); final RPCResponse response = RPCResponse.create(id); final LinkedBlockingQueue<MapTuple> sink = _sinks.get(batchId); if (sink != null) { final ArrayList<MapTuple> tuples = new ArrayList<>(); /* Atomically drain any extant responses, but leave the collector around. */ sink.drainTo(tuples); for (final MapTuple tuple : tuples) { response.addTuple(this.userGivenName(), tuple); } } try { _outputQueue.sendResponse(response); } catch (OperationException e) { Throwables.propagate(e); } } /**** * * RPC Sink PARALLELISM MUST BE 1! Change only with great caution. * */ @Override public int getMaxParallelism() { return 1; } }