/**
* This file is part of Path Computation Element Emulator (PCEE).
*
* PCEE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* PCEE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with PCEE. If not, see <http://www.gnu.org/licenses/>.
*/
package com.pcee.architecture.computationmodule.ted;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
import com.graph.elements.edge.EdgeElement;
import com.graph.elements.edge.params.EdgeParams;
import com.graph.elements.edge.params.impl.BasicEdgeParams;
import com.graph.graphcontroller.Gcontroller;
import com.graph.graphcontroller.impl.GcontrollerImpl;
import com.graph.topology.importers.ImportTopology;
import com.graph.topology.importers.impl.BRITEImportTopology;
import com.graph.topology.importers.impl.SNDLibImportTopology;
/**
* Class to provide Topology Instances to the computation layer
*
* @author Marek Drogon
*
*/
public class TopologyInformation {
private static Logger logger = LoggerFactory.getLogger(TopologyInformation.class);
//JSON Parser
private static Gson json = new Gson();
//Port on which to listen for topology Information Updates
private static int topologyUpdatePort = 5189;
//Thread for topology Update Listener
private static Thread topologyUpdateThread;
// Static oject instance of the TopologyInformation Class
static private TopologyInformation _instance;
// Graph Instance
private Gcontroller graph;
// Topology Importer used to populate the graph instance
private static ImportTopology topology;
// path to the topology description file
private static String topoPath = ".//atlanta.txt";
/**Function to set the port for topology Updates
*
* @param port
*/
public static void setTopologyUpdatePort(int port){
topologyUpdatePort = port;
}
/**
* @param input
* the topoPath to be used by the TED
*/
public static void setTopoPath(String input) {
topoPath = input;
}
/**
* @param importer
* ONLY SNDLib or BRITE supported as input
*/
public static void setImporter(String importer) {
if (importer.equals("SNDLib")) {
topology = new SNDLibImportTopology();
} else if (importer.equals("BRITE")) {
topology = new BRITEImportTopology();
} else {
topology = new SNDLibImportTopology();
}
}
/** default constructor */
private TopologyInformation() {
topology = new SNDLibImportTopology();
graph = new GcontrollerImpl();
// Source file used to instantiate the topology
File file = new File(topoPath);
// Function to import the topology stored in the text file into the
// graph object
topology.importTopology(graph, file.getAbsolutePath());
if (graph == null)
logger.debug("Error in loading graph from file");
else
logger.info("NetworkSize: " + networkSize());
//Start Topology Update Listener
logger.info ("Starting thread to listen for topology updates on port " + topologyUpdatePort);
startTopologyUpdateListner();
}
/**
* Function to update the graph instance used inside the Topology
* Information object
*
* @param newGraph
*/
public synchronized void updateGraph(Gcontroller newGraph) {
graph = newGraph;
}
/** Function to determine the network size */
public int networkSize() {
return graph.getVertexSet().size();
}
/** Function to get the instance of the TopologyInformation class */
public static TopologyInformation getInstance() {
if (_instance == null)
_instance = new TopologyInformation();
return _instance;
}
/** Function to close the instance of the TopologyInformation class */
public static void closeInstance() {
if (_instance != null)
_instance = null;
}
/** Function to get the graph object used */
public Gcontroller getGraph() {
return graph;
}
/**
* Function to get the topology importer used in the implementation
*
* @return topology importer
*/
public ImportTopology getTopologyImporter() {
return topology;
}
/**
* test case
*
* @param args
*/
public static void main(String[] args) {
System.out.println(new File(topoPath).getAbsolutePath());
}
/** Function to initialize a thread to listen for topology updates */
private void startTopologyUpdateListner() {
topologyUpdateThread = new Thread() {
//Function to parse and implement incoming topology Updates
@SuppressWarnings({ "rawtypes", "unchecked" })
public String parseInput(String text) {
try {
Map input = json.fromJson(text, Map.class);
if (input.containsKey("operation")) {
if (input.get("operation").toString().equalsIgnoreCase("reserve")) {
//Request to reserve capacity on a sequence of nodes
double capacity = Double.parseDouble(input.get("capacity").toString());
ArrayList vertexSequence = ((ArrayList)input.get("vertexSequence"));
synchronized(graph) {
int i=0;
for (i=0;i<vertexSequence.size()-1;i++){
String sourceID = (String)vertexSequence.get(i);
String destID = (String)vertexSequence.get(i+1);
if (graph.aConnectingEdge(sourceID, destID)) {
if (!graph.getConnectingEdge(sourceID, destID).getEdgeParams().reserveCapacity(capacity)) {
logger.info("Cannot reserve capacity between " + sourceID +" and " + destID);
for (int j=0;j<i;j++) {
//Releasing capacity that was reserved till before i
String srcID = (String)vertexSequence.get(j);
String dstID = (String)vertexSequence.get(j+1);
graph.getConnectingEdge(srcID, dstID).getEdgeParams().releaseCapacity(capacity);
}
//break;
Map map = new HashMap();
map.put("response", new Boolean(false));
map.put("reason", "could not reserve capacity on edge from " + sourceID + " to " + destID);
return json.toJson(map);
}
} else {
logger.info("Invalid Vertex Sequence sent, no edge found between " + sourceID +" and " + destID);
for (int j=0;j<i;j++) {
//Releasing capacity that was reserved till before i
String srcID = (String)vertexSequence.get(j);
String dstID = (String)vertexSequence.get(j+1);
graph.getConnectingEdge(srcID, dstID).getEdgeParams().releaseCapacity(capacity);
}
Map map = new HashMap();
map.put("response", new Boolean(false));
map.put("reason", "Invalid Vertex Sequence sent, no edge found between " + sourceID +" and " + destID);
return json.toJson(map);
}
}
if (i==vertexSequence.size()-1) {
logger.info("Successfully reserved capacity on provided sequence");
Map map = new HashMap();
map.put("response", new Boolean(true));
return json.toJson(map);
}
}
} else if (input.get("operation").toString().equalsIgnoreCase("release")) {
//Request to reserve capacity on a sequence of nodes
double capacity = Double.parseDouble(input.get("capacity").toString());
ArrayList vertexSequence = ((ArrayList)input.get("vertexSequence"));
synchronized(graph) {
int i=0;
for (i=0;i<vertexSequence.size()-1;i++){
String sourceID = (String)vertexSequence.get(i);
String destID = (String)vertexSequence.get(i+1);
if (graph.aConnectingEdge(sourceID, destID)) {
if (!graph.getConnectingEdge(sourceID, destID).getEdgeParams().releaseCapacity(capacity)) {
logger.info("Cannot release additional capacity between " + sourceID +" and " + destID);
for (int j=0;j<i;j++) {
//Releasing capacity that was reserved till before i
String srcID = (String)vertexSequence.get(j);
String dstID = (String)vertexSequence.get(j+1);
graph.getConnectingEdge(srcID, dstID).getEdgeParams().reserveCapacity(capacity);
}
Map map = new HashMap();
map.put("response", new Boolean(false));
map.put("reason", "could not release capacity on edge from " + sourceID + " to " + destID);
return json.toJson(map);
}
} else {
logger.info("Invalid Vertex Sequence sent, no edge found between " + sourceID +" and " + destID);
for (int j=0;j<i;j++) {
//Releasing capacity that was reserved till before i
String srcID = (String)vertexSequence.get(j);
String dstID = (String)vertexSequence.get(j+1);
graph.getConnectingEdge(srcID, dstID).getEdgeParams().reserveCapacity(capacity);
}
Map map = new HashMap();
map.put("response", new Boolean(false));
map.put("reason", "Invalid Vertex Sequence sent, no edge found between " + sourceID +" and " + destID);
return json.toJson(map);
}
}
if (i==vertexSequence.size()-1) {
logger.info("Successfully released capacity on provided sequence");
Map map = new HashMap();
map.put("response", new Boolean(true));
return json.toJson(map);
}
}
} else if (input.get("operation").toString().equalsIgnoreCase("updateEdgeDefinition")) {
//Request to update the Edge Definition
double capacity = Double.parseDouble(input.get("capacity").toString());
double avcapacity = Double.parseDouble(input.get("avcapacity").toString());
double weight = Double.parseDouble(input.get("weight").toString());
double delay = Double.parseDouble(input.get("delay").toString());
ArrayList vSequence = ((ArrayList)input.get("vertexSequence"));
synchronized(graph) {
ArrayList<String> vertexSequence = new ArrayList<String>();
for (int i=0;i<vSequence.size();i++) {
vertexSequence.add(vSequence.get(i).toString());
}
String sourceID = vertexSequence.get(0);
String destID = vertexSequence.get(vertexSequence.size()-1);
if (graph.aConnectingEdge(sourceID, destID)) {
EdgeElement edge = graph.getConnectingEdge(sourceID, destID);
EdgeParams params = new BasicEdgeParams(edge, delay, weight, capacity);
params.setAvailableCapacity(avcapacity);
edge.setEdgeParams(params);
logger.info("Updated Edge definition from " + sourceID + " to " + destID);
Map map = new HashMap();
map.put("response", new Boolean(true));
return json.toJson(map);
} else {
logger.info("No existing edge from " + sourceID + " to " + destID + " foud in topology");
Map map = new HashMap();
map.put("response", new Boolean(false));
map.put("reason", "No existing edge from " + sourceID + " to " + destID + " foud in topology");
return json.toJson(map);
}
}
}
}
} catch (JsonSyntaxException e) {
logger.info("Malformed Json sent from Client" + e.getMessage());
Map map = new HashMap();
map.put("response", new Boolean(false));
map.put("reason", "Malformed Json sent from Client" + e.getMessage());
return json.toJson(map);
} catch (Exception e) {
Map map = new HashMap();
map.put("response", new Boolean(false));
map.put("reason", "Exception " + e.getMessage());
return json.toJson(map);
}
Map map = new HashMap();
map.put("response", new Boolean(false));
map.put("reason", "Invalid or unknown operaiton");
return json.toJson(map);
}
// Override the run() method to implement a simple server socket to listen for topology updates
public void run() {
ServerSocket serverSocket;
try {
serverSocket = new ServerSocket(topologyUpdatePort);
while (true) {
try {
Socket clientSocket = serverSocket.accept();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
String text = "";
String line = "";
while ((line = bufferedReader.readLine()) != null) {
if (line.trim().compareTo("@") ==0)
break;
text = text + line;
}
String outText = parseInput(text);
BufferedOutputStream out = new BufferedOutputStream(clientSocket.getOutputStream());
out.write(outText.getBytes());
out.flush();
//Close Input and output streams
out.close();
bufferedReader.close();
// Ignore close of the socket // should be closed by client
//clientSocket.close();
} catch (IOException e) {
logger.debug("IOException during read for new connections. Discarding update");
continue;
}
}
} catch (IOException e1) {
logger.debug("Could not open server socket to listen for topology updates on port:" + topologyUpdatePort);
}
}
};
topologyUpdateThread.setName("TopologyUpdateThread");
topologyUpdateThread.start();
}
}