/******************************************************************************* * Copyright (c) 2013 Imperial College London. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Raul Castro Fernandez - initial design and implementation ******************************************************************************/ package uk.ac.imperial.lsds.seep.processingunit; import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Socket; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.SocketChannel; import java.nio.channels.spi.SelectorProvider; import java.util.ArrayList; import java.util.HashMap; import java.util.Vector; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import uk.ac.imperial.lsds.seep.GLOBALS; import uk.ac.imperial.lsds.seep.buffer.Buffer; import uk.ac.imperial.lsds.seep.buffer.OutputBuffer; import uk.ac.imperial.lsds.seep.infrastructure.WorkerNodeDescription; import uk.ac.imperial.lsds.seep.operator.EndPoint; import uk.ac.imperial.lsds.seep.operator.Operator; import uk.ac.imperial.lsds.seep.operator.OperatorStaticInformation; import uk.ac.imperial.lsds.seep.operator.OperatorContext.PlacedOperator; import uk.ac.imperial.lsds.seep.runtimeengine.AsynchronousCommunicationChannel; import uk.ac.imperial.lsds.seep.runtimeengine.DisposableCommunicationChannel; import uk.ac.imperial.lsds.seep.runtimeengine.SynchronousCommunicationChannel; import com.esotericsoftware.kryo.io.ByteBufferOutputStream; import com.esotericsoftware.kryo.io.Output; public class PUContext { final private Logger LOG = LoggerFactory.getLogger(PUContext.class); // private WorkerNodeDescription nodeDescr = null; private final int CONTROL_SOCKET; private ArrayList<EndPoint> remoteUpstream = new ArrayList<EndPoint>(); private ArrayList<EndPoint> remoteDownstream = new ArrayList<EndPoint>(); //These structures are Vector because they are potentially accessed from more than one point at a time /// \todo {refactor this to a synchronized map??} private Vector<EndPoint> downstreamTypeConnection = null; private Vector<EndPoint> upstreamTypeConnection = null; //The structure just stores the ip adresses of those nodes in the topology ready to receive state chunks private ArrayList<EndPoint> starTopology = null; // Selector for asynchrony in downstream connections private Selector selector; //map in charge of storing the buffers that this operator is using private HashMap<Integer, Buffer> downstreamBuffers = new HashMap<Integer, Buffer>(); public PUContext(WorkerNodeDescription nodeDescr, ArrayList<EndPoint> starTopology){ this.CONTROL_SOCKET = new Integer(GLOBALS.valueFor("controlSocket")); // this.nodeDescr = nodeDescr; this.starTopology = starTopology; try { this.selector = SelectorProvider.provider().openSelector(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public boolean isScalingOpDirectDownstream(int opId){ for(EndPoint ep : downstreamTypeConnection){ if(ep.getOperatorId() == opId){ return true; } } return false; } public int getStarTopologySize(){ return starTopology.size(); } public void filterStarTopology(int opId){ for(int i = 0; i < starTopology.size(); i++){ EndPoint ep = starTopology.get(i); if(ep.getOperatorId() == opId){ starTopology.remove(i); } } } public void updateStarTopology(ArrayList<EndPoint> starTopology){ this.starTopology = starTopology; } public DisposableCommunicationChannel getDCCfromOpIdInStarTopology(int opId){ for(EndPoint dcc : starTopology){ if(dcc.getOperatorId() == opId){ return (DisposableCommunicationChannel)dcc; } } return null; } public ArrayList<EndPoint> getStarTopology(){ return starTopology; } public ArrayList<OutputBuffer> getOutputBuffers(){ ArrayList<OutputBuffer> outputBuffers = new ArrayList<OutputBuffer>(); for(EndPoint ep : this.getDownstreamTypeConnection()){ if(ep instanceof SynchronousCommunicationChannel){ outputBuffers.add(new OutputBuffer(((SynchronousCommunicationChannel) ep).getBatch(), ep.getOperatorId())); } } return outputBuffers; } public Vector<EndPoint> getDownstreamTypeConnection() { return downstreamTypeConnection; } public Vector<EndPoint> getUpstreamTypeConnection() { return upstreamTypeConnection; } public Selector getConfiguredSelector(){ return selector; } private void configureDownstreamAndUpstreamConnections(Operator op){ //Gather nature of downstream operators, i.e. local or remote for(PlacedOperator down: op.getOpContext().downstreams){ LOG.debug("-> configuring downstream of {}", down.opID()); configureNewDownstreamCommunication(down.opID(), down.location()); } for(PlacedOperator up: op.getOpContext().upstreams){ configureNewUpstreamCommunication(up.opID(), up.location()); } } public void configureOperatorConnections(Operator op) { downstreamTypeConnection = new Vector<EndPoint>(); upstreamTypeConnection = new Vector<EndPoint>(); configureDownstreamAndUpstreamConnections(op); } /** * This function creates a (always) synchronous communication channel with the specified upstream operator * @param opID * @param loc */ public void configureNewUpstreamCommunication(int opID, OperatorStaticInformation loc) { createRemoteSynchronousCommunication(opID, loc.getMyNode().getIp(), 0, loc.getInC(), "up"); LOG.debug("-> PUContext. New remote upstream (sync) conn to OP: {}", opID); } /** * This function creates a synchronous communication channel with the specified downstream operator * @param opID * @param loc */ public void configureNewDownstreamCommunication(int opID, OperatorStaticInformation loc) { //If remote, create communication with other point if (GLOBALS.valueFor("synchronousOutput").equals("true")){ createRemoteSynchronousCommunication(opID, loc.getMyNode().getIp(), loc.getInD(), loc.getInC(), "down"); LOG.debug("-> New remote downstream (SYNC) conn to OP: ", opID); } else{ createRemoteAsynchronousCommunication(opID, loc.getMyNode().getIp(), loc.getInD()); LOG.debug("-> New remote downstream (ASYNC) conn to OP: ", opID); } } private void createRemoteAsynchronousCommunication(int opId, InetAddress ip, int port){ LOG.debug("-> Trying remote downstream conn to: {}/{}", ip.toString(), port); try { // Create a non-blocking socket channel SocketChannel socketChannel = SocketChannel.open(); socketChannel.configureBlocking(false); // establish connection socketChannel.connect(new InetSocketAddress(ip, port)); // We create an output where to write serialized data (kryo stuff), and we associate a native byte buffer in a bytebufferoutputstream ByteBuffer nativeBuffer = ByteBuffer.allocateDirect(20000); ByteBufferOutputStream bbos = new ByteBufferOutputStream(nativeBuffer); Output o = new Output(bbos); // finally we register this socket to the selector for the async behaviour, and we link nativeBuffer, for the selector to access it directly // SelectionKey key = socketChannel.register(selector, SelectionKey.OP_WRITE, nativeBuffer); //Finally create the metadata structure associated to this connection Buffer buf = new Buffer(); AsynchronousCommunicationChannel acc = new AsynchronousCommunicationChannel(opId, buf, o, nativeBuffer); acc.setSelector(selector); SelectionKey key = socketChannel.register(selector, SelectionKey.OP_WRITE, acc); // To make sure conn is established... try { Thread.sleep(10); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } boolean connSuccess = socketChannel.finishConnect(); if(!connSuccess){ ///\fixme{fix this} LOG.error("Failed connection to: "+key.toString()); System.exit(0); } downstreamTypeConnection.add(acc); remoteDownstream.add(acc); // Set the buffer downstreamBuffers.put((port-40000), buf); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } private void createRemoteSynchronousCommunication(int opID, InetAddress ip, int portD, int portC, String type){ Socket socketD = null; Socket socketC = null; Socket socketBlind = null; int blindPort = new Integer(GLOBALS.valueFor("blindSocket")); try{ if(type.equals("down")){ LOG.debug("-> Trying remote downstream conn to: {}/{}", ip.toString(), portD); socketD = new Socket(ip, portD); if(portC != 0){ socketC = new Socket(ip, portC); } Buffer buffer = new Buffer(); SynchronousCommunicationChannel con = new SynchronousCommunicationChannel(opID, socketD, socketC, socketBlind, buffer); downstreamTypeConnection.add(con); remoteDownstream.add(con); /// \todo{here a 40000 is used, change this line to make it properly} downstreamBuffers.put((portD-40000), buffer); } else if(type.equals("up")){ LOG.debug("-> Trying remote upstream conn to: {}/{}", ip.toString(), portC); socketC = new Socket(ip, portC); socketBlind = new Socket(ip, blindPort); SynchronousCommunicationChannel con = new SynchronousCommunicationChannel(opID, null, socketC, socketBlind, null); upstreamTypeConnection.add(con); remoteUpstream.add(con); } } catch(IOException io){ LOG.error("-> PUContext. While establishing remote connection "+io.getMessage()); if(socketD != null){ LOG.error("-> Data Conn to: "+socketD.toString()); } else if(socketC != null){ LOG.error("-> Control Conn to: "+socketC.toString()); } else{ LOG.error("-> Socket objects are BOTH NULL"); } io.printStackTrace(); } } public SynchronousCommunicationChannel getCCIfromOpId(int opId, String type){ if(type.equals("d")){ for(EndPoint ep : downstreamTypeConnection){ if(ep.getOperatorId() == opId){ return (SynchronousCommunicationChannel)ep; } } } else if(type.equals("u")){ for(EndPoint ep : upstreamTypeConnection){ if(ep.getOperatorId() == opId){ return (SynchronousCommunicationChannel)ep; } } } return null; } public Buffer getBuffer(int opId) { return downstreamBuffers.get(opId); } /** Dynamic Reconfiguration **/ public void updateConnection(int opRecId, Operator opToReconfigure, InetAddress newIp){ int opId = opRecId; int dataPort = 0; int controlPort = 0; int blindPort = new Integer(GLOBALS.valueFor("blindSocket")); if(opToReconfigure.getOpContext().downstreams.size() > 0){ dataPort = opToReconfigure.getOpContext().findDownstream(opId).location().getInD(); controlPort = opToReconfigure.getOpContext().findDownstream(opId).location().getInC(); } for(EndPoint ep : downstreamTypeConnection){ if(ep.getOperatorId() == opId){ try{ Socket dataS = new Socket(newIp, dataPort); Socket controlS = new Socket(newIp, controlPort); Socket blindS = null; Buffer buf = downstreamBuffers.get(opId); int index = opToReconfigure.getOpContext().getDownOpIndexFromOpId(opId); SynchronousCommunicationChannel cci = new SynchronousCommunicationChannel(opId, dataS, controlS, blindS, buf); downstreamTypeConnection.set(index, cci); } catch(IOException io){ System.out.println("While re-creating DOWNSTREAM socket: "+io.getMessage()); } } } for(EndPoint ep : upstreamTypeConnection){ if(ep.getOperatorId() == opId){ int upControlPort = CONTROL_SOCKET + ep.getOperatorId(); try{ Socket controlS = new Socket(newIp, upControlPort); Socket blindS = new Socket(newIp, blindPort); int index = opToReconfigure.getOpContext().getUpOpIndexFromOpId(opId); SynchronousCommunicationChannel cci = new SynchronousCommunicationChannel(opId, null, controlS, blindS, null); upstreamTypeConnection.set(index, cci); } catch(IOException io){ System.out.println("While re-creating UPSTREAM socket: "+io.getMessage()); } } } LOG.debug("-> PUContext. Conns of OP: {} updated", opId); } }