/*******************************************************************************
* 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
* Matteo Migliavacca - Definition of inner classes and method implementation
* Martin Rouaux - Removal of upstream and downstream connections (OperatorStaticInformation)
* which is required to support scale-in of operators.
******************************************************************************/
package uk.ac.imperial.lsds.seep.operator;
import java.io.Serializable;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import uk.ac.imperial.lsds.seep.infrastructure.master.Node;
public class OperatorContext implements Serializable{
private static final long serialVersionUID = 1L;
/** VAR -> Location information of this operator and of their upstream and downstream **/
private OperatorStaticInformation location;
public final DownIter downstreams = new DownIter();
public final UpIter upstreams = new UpIter();
private ArrayList<Integer> connectionsD = new ArrayList<Integer>();
private ArrayList<Integer> connectionsU = new ArrayList<Integer>();
private ArrayList<OperatorStaticInformation> upstream = new ArrayList<OperatorStaticInformation>();
private ArrayList<OperatorStaticInformation> downstream = new ArrayList<OperatorStaticInformation>();
private ArrayList<Integer> originalDownstream = new ArrayList<Integer>();
private boolean isSource = false;
private boolean isSink = false;
// store the type of input data ingestion mode per upstream operator. <OpId - InputDataIngestionMode>
private Map<Integer, InputDataIngestionMode> inputDataIngestionModePerUpstream = new HashMap<Integer, InputDataIngestionMode>();
//This map stores static info (for different types of downstream operators). StreamId -> list of downstreams
public HashMap<Integer, ArrayList<Integer>> routeInfo = new HashMap<Integer, ArrayList<Integer>>();
/** Tuple dependent information **/
private List<String> declaredWorkingAttributes;
private String keyAttribute = null;
public OperatorContext(){
}
public boolean isSource(){
return isSource;
}
public boolean isSink(){
return isSink;
}
public void setIsSource(boolean isSource){
this.isSource = isSource;
}
public void setIsSink(boolean isSink){
this.isSink = isSink;
}
public boolean doesRequireLogicalRouting(){
// If there are more than one addressable streamIds in the logicalRouting table, it does require specific routing
return routeInfo.size() > 1;
}
public String getKeyAttribute(){
return keyAttribute;
}
public void setKeyAttribute(String key){
this.keyAttribute = key;
}
public void setDeclaredWorkingAttributes(List<String> declaredWorkingAttributes){
this.declaredWorkingAttributes = declaredWorkingAttributes;
}
public List<String> getDeclaredWorkingAttributes(){
return declaredWorkingAttributes;
}
public HashMap<Integer, ArrayList<Integer>> getRouteInfo(){
return routeInfo;
}
public void setRouteInfo(HashMap<Integer, ArrayList<Integer>> map){
routeInfo = map;
}
public Map<Integer, InputDataIngestionMode> getInputDataIngestionModePerUpstream(){
return inputDataIngestionModePerUpstream;
}
public void setInputDataIngestionModePerUpstream(int opId, InputDataIngestionMode mode){
inputDataIngestionModePerUpstream.put(opId, mode);
}
public int getOriginalUpstreamFromOpId(int opId){
for(OperatorStaticInformation op : upstream){
if(op.getOpId() == opId){
return op.getOriginalOpId();
}
}
///\fixme{make operators id consistent, or propagate errors with exceptions otherwise}
return -1000;
}
public int getUpstreamNumberOfType(int originalOpId){
int total = 0;
for(OperatorStaticInformation op : upstream){
if(op.getOriginalOpId() == originalOpId) total++;
}
return total;
}
private static boolean ipEquals(InetAddress ip1, InetAddress ip2) {
String ip1Str = ip1.getHostAddress();
String ip2Str = ip2.getHostAddress();
return ip1.equals(ip2) ||
ip1Str.equals("127.0.0.1") && ip2Str.equals("127.0.1.1") ||
ip1Str.equals("127.0.1.1") && ip2Str.equals("127.0.0.1");
}
public int getOpIdFromUpstreamIp(InetAddress ip){
for(OperatorStaticInformation op : upstream){
if(ipEquals(op.getMyNode().getIp(), ip)){
return op.getOpId();
}
}
///\fixme{make operators id consistent, or propagate errors with exceptions otherwise}
return -1000;
}
public int getDownOpIdFromIndex(int index){
int opId = 0;
for(PlacedOperator down : downstreams){
if(down.index() == index){
return down.opID();
}
}
return opId;
}
public int getUpOpIdFromIndex(int index){
int opId = 0;
for(PlacedOperator up : upstreams){
if(up.index() == index){
return up.opID();
}
}
return opId;
}
public ArrayList<Integer> getListOfDownstreamIndexes() {
ArrayList<Integer> indexes = new ArrayList<Integer>();
for(PlacedOperator down : downstreams){
indexes.add(down.index());
}
return indexes;
}
//Check if the given opId is statefull
public boolean isDownstreamOperatorStateful(int opId) {
for(PlacedOperator op : downstreams){
if(op.opID() == opId){
if(op.location().isStatefull()){
return true;
}
else{
return false;
}
}
}
return false;
}
//Check if all downstreamms are stateful
public boolean isDownstreamStateful() {
for(PlacedOperator op : downstreams){
if((op.location().isStatefull())){
return true;
}
}
return false;
}
public PlacedOperator minimumUpstream() {
PlacedOperator min = null;
for (PlacedOperator op : upstreams) {
if (min==null) { min = op; }
else if (min.opID() > op.opID()) { min = op; }
}
return min;
}
public int getDownstreamSize() {
return downstream.size();
}
public int getUpstreamSize(){
return upstream.size();
}
public OperatorStaticInformation getDownstreamLocation(int opID) {
return downstream.get(findOpIndex(opID, connectionsD));
}
public OperatorStaticInformation getUpstreamLocation(int opID) {
return upstream.get(findOpIndex(opID, connectionsU));
}
public PlacedOperator findUpstream(int opId){
for (PlacedOperator op : upstreams) {
if (op.opID() == opId) return op;
}
return null;
}
public PlacedOperator findDownstream(int opID) {
for (PlacedOperator op : downstreams) {
if (op.opID() == opID) return op;
}
return null;
}
public int getDownOpIndexFromOpId(int opId){
for(PlacedOperator po : downstreams){
if(po.opID() == opId) return po.index;
}
return -1;
}
public int getUpOpIndexFromOpId(int opId){
for(PlacedOperator po : upstreams){
if(po.opID() == opId) return po.index;
}
return -1;
}
/** Methods called by QuerySpecificationI **/
public void setOriginalDownstream(ArrayList<Integer> originalDownstream){
this.originalDownstream = originalDownstream;
}
public void addDownstream(int opID) {
connectionsD.add(new Integer(opID));
}
public void removeDownstream(int opID) {
connectionsD.remove(new Integer(opID));
}
public void addOriginalDownstream(Integer opId){
originalDownstream.add(opId);
}
public void addUpstream(int opID) {
connectionsU.add(new Integer(opID));
}
public void removeUpstream(int opID) {
connectionsU.remove(new Integer(opID));
}
// public void setQueryAttribute(String queryAttribute){
// this.queryAttribute = queryAttribute;
// }
//if less or greater is than a given value. if equal could be with many values, with range is a special case as well
// public void routeValueToDownstream(RelationalOperator operator, int value, int downstream){
// //if it is operator EQUALS, use specific routeInfo
// if(operator.equals(RelationalOperator.EQ)){
// //If there was a downstream assigned for this value
// if(routeInfo.containsKey(value)){
// // add the new downstream
// routeInfo.get(value).add(downstream);
// }
// else{
// ArrayList<Integer> aux = new ArrayList<Integer>();
// aux.add(downstream);
// routeInfo.put(value,aux);
// }
// }
// }
public void routeValueToDownstream(int streamId, int downstream){
if(routeInfo.containsKey(streamId)){
// add the new downstream
routeInfo.get(streamId).add(downstream);
}
else{
ArrayList<Integer> aux = new ArrayList<Integer>();
aux.add(downstream);
routeInfo.put(streamId, aux);
}
}
/** Methods called by Infrastructure **/
public OperatorStaticInformation getOperatorStaticInformation(){
return location;
}
public void setOperatorStaticInformation(OperatorStaticInformation location){
this.location = location;
}
public void setDownstreamOperatorStaticInformation(int opID, OperatorStaticInformation loc) {
addOrReplace(opID, loc, connectionsD, downstream);
}
//Add if it is a new upstrea, replace if it is a previous upstream that failer or that have changed node for any other reason
public void setUpstreamOperatorStaticInformation(int opID, OperatorStaticInformation loc) {
addOrReplace(opID, loc, connectionsU, upstream);
}
private void addOrReplace(int opID, OperatorStaticInformation loc, ArrayList<Integer> IDList, ArrayList<OperatorStaticInformation> LocationList) {
int opIndex = findOpIndex(opID, IDList);
if (opIndex<LocationList.size())
LocationList.set(opIndex, loc);
else {
for (int index = LocationList.size(); index < opIndex; index++) {
LocationList.add(null);
}
LocationList.add(loc);
}
}
public int findOpIndexFromDownstream(int opId){
for(Integer id : connectionsD){
if(id == opId){
return connectionsD.indexOf(id);
}
}
return -1;
}
private int findOpIndex(int opID, ArrayList<Integer> IDList) {
for(Integer id : IDList){
if(id == opID){
return IDList.indexOf(id);
}
}
throw new IllegalArgumentException("opID "+ opID + " not found");
}
//this method and the following one do not support changing the node with
//a new node with a different port, however this is not a problem for operators,
//that do not need to contact the secondary but just need ports for operators.
//the slight problem is that if the new node has a different port this would not
//be reflected in the node object, which might be confusing for debugging.
public void changeLocation(InetAddress oldIp, InetAddress newIp){
for(int i = 0; i < upstream.size(); i++){
if(upstream.get(i).getMyNode().getIp().equals(oldIp)){
Node newNode = upstream.get(i).getMyNode().setIp(newIp);
OperatorStaticInformation newLoc = upstream.get(i).setNode(newNode);
upstream.set(i, newLoc);
}
}
for(int i = 0; i < downstream.size(); i++){
if(downstream.get(i).getMyNode().getIp().equals(oldIp)){
Node newNode = downstream.get(i).getMyNode().setIp(newIp);
OperatorStaticInformation newLoc = downstream.get(i).setNode(newNode);
downstream.set(i, newLoc);
}
}
}
public void changeLocation(int opId, InetAddress newIp){
for(int i = 0; i < upstream.size(); i++){
if((upstream.get(i).getInD() - 40000) == opId){
Node newNode = upstream.get(i).getMyNode().setIp(newIp);
OperatorStaticInformation newLoc = upstream.get(i).setNode(newNode);
upstream.set(i, newLoc);
}
}
for(int i = 0; i < downstream.size(); i++){
if((downstream.get(i).getInD() - 40000) == opId){
Node newNode = downstream.get(i).getMyNode().setIp(newIp);
OperatorStaticInformation newLoc = downstream.get(i).setNode(newNode);
downstream.set(i, newLoc);
}
}
}
public ArrayList<Integer> getOriginalDownstream(){
return originalDownstream;
}
/** Support classes **/
//TODO place opID in location instead of this?
public static class PlacedOperator {
int index;
ArrayList<Integer> conns;
ArrayList<OperatorStaticInformation> locs;
public PlacedOperator(int index, ArrayList<Integer> conns, ArrayList<OperatorStaticInformation> locs) {
this.index = index;
this.conns = conns;
this.locs = locs;
}
public OperatorStaticInformation location() {
return locs.get(index);
}
public boolean isStateful(){
return locs.get(index).isStatefull();
}
public int opID() {
return conns.get(index);
}
public int index() {
return index;
}
};
static class Iter implements Iterator<PlacedOperator> {
int index = -1;
ArrayList<Integer> conns;
ArrayList<OperatorStaticInformation> locs;
private Iter(ArrayList<Integer> conns, ArrayList<OperatorStaticInformation> locs) { this.conns = conns; this.locs = locs; }
public boolean hasNext() { return index<conns.size()-1; }
public PlacedOperator next() { index++; return new PlacedOperator(index, conns, locs); }
public void remove() { throw new UnsupportedOperationException(); }
}
public class DownIter implements Iterable<PlacedOperator>, Serializable {
private static final long serialVersionUID = 1L;
public Iterator<PlacedOperator> iterator() {
return new Iter(connectionsD,downstream);
}
public int size() {
return connectionsD.size();
}
}
public class UpIter implements Iterable<PlacedOperator>, Serializable {
private static final long serialVersionUID = 1L;
public Iterator<PlacedOperator> iterator() {
return new Iter(connectionsU,upstream);
}
public int size() {
return connectionsU.size();
}
}
@Override
public String toString() {
StringBuffer buf = new StringBuffer("@"+ getOperatorStaticInformation());
if (downstream.size()>0) {
buf.append(" down: [");
for (PlacedOperator op : downstreams) {
buf.append("Id: "+op.opID() + op.location());
System.out.println();
}
buf.append("]");
}
if (upstream.size()>0) {
buf.append(" up: [");
for (PlacedOperator op : upstreams) {
buf.append("Id: "+op.opID() + op.location());
System.out.println();
}
buf.append("]");
}
return buf.toString();
}
}