/*******************************************************************************
* 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.infrastructure.master;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import uk.ac.imperial.lsds.seep.GLOBALS;
import uk.ac.imperial.lsds.seep.elastic.IPNotBoundToOperatorException;
import uk.ac.imperial.lsds.seep.elastic.NodePoolEmptyException;
import uk.ac.imperial.lsds.seep.operator.Connectable;
import uk.ac.imperial.lsds.seep.operator.EndPoint;
import uk.ac.imperial.lsds.seep.operator.Operator;
import uk.ac.imperial.lsds.seep.runtimeengine.DisposableCommunicationChannel;
/**
* ManagerWorker. This class implements runnable, it is in charge of listening to events from the running system.
*/
public class ManagerWorker implements Runnable {
final private Logger LOG = LoggerFactory.getLogger(ManagerWorker.class);
private Infrastructure inf = null;
private ServerSocket managerS = null;
private Socket clientSocket = null;
private int refPort = 3500;
private boolean goOn;
private int port;
public ManagerWorker(Infrastructure inf, int port){
this.inf = inf;
this.port = port;
}
private void crashCommand(String oldIP_txt, String oldPort_txt, String newIP_txt, String newPort_txt) throws UnknownHostException, IPNotBoundToOperatorException{
//get opId from ip
int opId = inf.getOpIdFromIp(InetAddress.getByName(oldIP_txt));
System.out.println("OLD OP ID IS FROM: "+opId);
if(opId == -1){
throw new IPNotBoundToOperatorException("IP not bounded to an operator: "+oldIP_txt);
}
//get numDownstreams from opId
int numOfUpstreams = inf.getNumUpstreams(opId);
//set initial time of crash and number of downstreams
Infrastructure.msh.setCrashInitialTime(System.currentTimeMillis(), numOfUpstreams);
InetAddress oldIP = InetAddress.getByName(oldIP_txt);
int oldPort = Integer.parseInt(oldPort_txt);
InetAddress newIP = InetAddress.getByName(newIP_txt);
int newPort = Integer.parseInt(newPort_txt);
/// \todo{this case has never been tested}
if(!(newIP.equals(oldIP) && newPort == oldPort)){
System.out.println("MANAGER: Remapping communications, new IP");
//remap could get nodes instead of IPs to build correct nodes, but
//it also work just with IPs
inf.reMap(oldIP,newIP);
}
System.out.println("MANAGER: Calling reDeploy... the IP: "+oldIP.toString());
Node newNode = new Node(newIP,newPort);
long init = System.currentTimeMillis();
System.out.println("updating star topology");
// Update star topology. remove failed node and add new instantiated one.
inf.removeNodeFromStarTopology(opId);
inf.addNodeToStarTopology(opId, newIP);
// Then broadcast the new star topology
System.out.println("broadcast star topology");
inf.broadcastStarTopology();
for(EndPoint ep: inf.getStarTopology()){
System.out.println("Op: "+ep.getOperatorId()+" IP: "+((DisposableCommunicationChannel)ep).getIp().toString());
}
inf.reDeploy(newNode);
long end = System.currentTimeMillis();
System.out.println("INIT OP: "+(end-init));
System.out.println("MANAGER: reDeploy ops in node IP: "+InetAddress.getByName(newIP_txt).toString());
System.out.println("MANAGER: Updating upstream and downstream connections...");
//updateU_D could get nodes instead of IPs to build correct nodes, but
//it also work just with IPs
System.out.println("calling updateUD");
inf.updateU_D(oldIP,newIP, false);
Operator toInit = inf.getOperatorById(opId);
System.out.println("broadcast state and runtime init");
inf.broadcastState(toInit);
inf.initRuntime(toInit);
// Tell star topology to stream state
System.out.println("calling failure");
inf.failure(opId);
}
private void crashCommandParallelRecovery(String oldIP_txt, String oldPort_txt, String newIP_txt, String newPort_txt) throws UnknownHostException, IPNotBoundToOperatorException{
System.out.println("PARALLEL CRASH RECOVERY");
//get opId from ip
int opId = inf.getOpIdFromIp(InetAddress.getByName(oldIP_txt));
System.out.println("OLD OP ID IS FROM: "+opId);
if(opId == -1){
throw new IPNotBoundToOperatorException("IP not bounded to an operator: "+oldIP_txt);
}
//get numDownstreams from opId
int numOfUpstreams = inf.getNumUpstreams(opId);
//set initial time of crash and number of downstreams
Infrastructure.msh.setCrashInitialTime(System.currentTimeMillis(), numOfUpstreams);
InetAddress oldIP = InetAddress.getByName(oldIP_txt);
int oldPort = Integer.parseInt(oldPort_txt);
InetAddress newIP = InetAddress.getByName(newIP_txt);
int newPort = Integer.parseInt(newPort_txt);
/// \todo{this case has never been tested}
if(!(newIP.equals(oldIP) && newPort == oldPort)){
System.out.println("MANAGER: Remapping communications, new IP");
//remap could get nodes instead of IPs to build correct nodes, but
//it also work just with IPs
inf.reMap(oldIP,newIP);
}
System.out.println("MANAGER: Calling reDeploy... the IP: "+oldIP.toString());
//Node newNode = new Node(newIP,newPort);
Node newNode = null;
try {
newNode = inf.getNodeFromPool();
}
catch (NodePoolEmptyException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
long init = System.currentTimeMillis();
System.out.println("updating star topology");
// Update star topology. remove failed node and add new instantiated one.
inf.removeNodeFromStarTopology(opId);
inf.addNodeToStarTopology(opId, newIP);
// Then broadcast the new star topology
System.out.println("broadcast star topology");
inf.broadcastStarTopology();
for(EndPoint ep: inf.getStarTopology()){
System.out.println("Op: "+ep.getOperatorId()+" IP: "+((DisposableCommunicationChannel)ep).getIp().toString());
}
inf.reDeploy(newNode);
long end = System.currentTimeMillis();
System.out.println("INIT OP: "+(end-init));
System.out.println("MANAGER: reDeploy ops in node IP: "+InetAddress.getByName(newIP_txt).toString());
System.out.println("MANAGER: Updating upstream and downstream connections...");
//updateU_D could get nodes instead of IPs to build correct nodes, but
//it also work just with IPs
System.out.println("calling updateUD");
inf.updateU_D(oldIP, newNode.getIp(), true);
Operator toInit = inf.getOperatorById(opId);
System.out.println("broadcast state and runtime init");
inf.broadcastState(toInit);
inf.initRuntime(toInit);
System.out.println("FAILED OP REINSTANTIATED");
System.out.println("SCALING OUT OP: "+opId);
Node newNodeSO = null;
try {
newNodeSO = inf.getNodeFromPool();
}
catch (NodePoolEmptyException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("NODE NEW FOR SO: "+newNodeSO);
inf.getEiu().scaleOutOperator(opId, 555, newNodeSO);
}
private void bootstrapCommand(String ip, int port) throws UnknownHostException{
InetAddress bootIp = InetAddress.getByName(ip);
Node n = new Node(bootIp, port);
//add node to the stack
inf.addNode(n);
if(inf.isSystemRunning()){
byte[] data = inf.getDataFromFile(inf.getPathToQueryDefinition());
LOG.debug("-> Sending code to recently added worker");
inf.sendCode(n, data);
}
}
private void addOperatorCommand(String className, String opID_txt, String opArg)
throws SecurityException, NoSuchMethodException, ClassNotFoundException, IllegalArgumentException,
InstantiationException, IllegalAccessException, InvocationTargetException{
Operator op = null;
int opID = Integer.parseInt(opID_txt);
if (className.equals("seep.operator.collection.AverageOperator") || className.equals("seep.operator.collection.AdderOperator")) {
Constructor constr = Class.forName(className).getConstructor(int.class,int.class);
op = (Operator) constr.newInstance(opID,Integer.parseInt(opArg));
}
else if (className.equals("seep.operator.collection.WordCounter") || className.equals("seep.operator.collection.WordSplitter")) {
op = (Operator)Class.forName(className).getConstructor(int.class).newInstance(opID);
}
else {
System.err.println("Class not known");
}
inf.addOperator(op);
}
private void addPartitioningConnectionCommand(String[] token) throws NumberFormatException {
Connectable src = inf.elements.get(Integer.parseInt(token[1]));
Connectable dst = inf.elements.get(Integer.parseInt(token[2]));
inf.deployConnection("add_downstream_partition", src, dst, "Null");
}
private void addDownstreamConnectionCommand(String[] token) throws NumberFormatException {
Connectable src = inf.elements.get(Integer.parseInt(token[1]));
Connectable dst = inf.elements.get(Integer.parseInt(token[2]));
inf.deployConnection("add_downstream", src, dst, "null");
}
private void addUpstreamConnectionCommand(String[] token)throws NumberFormatException {
Connectable src = inf.elements.get(Integer.parseInt(token[1]));
Connectable dst = inf.elements.get(Integer.parseInt(token[2]));
inf.deployConnection("add_upstream", dst, src, "NuLL");
}
private void placeCommand(String[] token) throws NumberFormatException, UnknownHostException {
Operator op = (Operator) inf.elements.get(Integer.parseInt(token[1]));
Node n = new Node(InetAddress.getByName(token[2]),Integer.parseInt(token[3]));
System.out.println(op);
inf.placeNew(op,n);
System.out.println(op);
inf.updateContextLocations(op);
System.out.println(op);
}
/// \todo {java 7 supports switch(string)}
public void run(){
try {
//TODO change this
managerS = new ServerSocket(port);
LOG.info(" Listening on {}:{}", InetAddress.getLocalHost(), port);
BufferedReader bis = null;
goOn = true;
while(goOn){
try {
clientSocket = managerS.accept();
bis = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
String com = "";
while( (com = bis.readLine()) != null) {
String token[] = com.split(" ");
System.out.println("Manager: Command received -> "+com);
if(token[0].equals("crash")){
if(GLOBALS.valueFor("parallelRecovery").equals("true")){
crashCommandParallelRecovery(token[1],token[2],token[3],token[4]);
}
else{
//params oldIp, oldPort, newIp, newPort
crashCommand(token[1],token[2],token[3],token[4]);
}
}
else if(token[0].equals("bootstrap")){
bootstrapCommand(token[1], Integer.parseInt(token[2]));
}
else if(token[0].equals("systemStable")){
Infrastructure.msh.setSystemStableTime(System.currentTimeMillis());
}
else if(token[0].equals("add_operator")) {
addOperatorCommand(token[1], token[2], token[3]);
}
else if(token[0].equals("place")) {
placeCommand(token);
}
else if(token[0].equals("deploy_operator")) {
Operator op = (Operator) inf.elements.get(Integer.parseInt(token[1]));
inf.remoteOperatorInstantiation(op);
}
else if(token[0].equals("add_upstream_conn")) {
addUpstreamConnectionCommand(token);
inf.printCurrentInfrastructure();
}
else if(token[0].equals("add_downstream_conn")) {
addDownstreamConnectionCommand(token);
}
else if(token[0].equals("add_partitioning_conn")) {
addPartitioningConnectionCommand(token);
}
//we could have sent this with a message to the operator control socket.
else if(token[0].equals("init_all")) {
Operator op = (Operator) inf.elements.get(Integer.parseInt(token[1]));
inf.init(op);
}
}
//bis.close();
//other options...
}
catch (Exception e) {
e.printStackTrace();
}
finally {
if (clientSocket != null)
try {
clientSocket.close();
}
catch (IOException e) {
e.printStackTrace();
}
}
}
managerS.close();
}
catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}