/**
* @copyright 2013 Computer Science Department, Recursive InterNetworking Architecture (RINA) laboratory, Boston University.
* All rights reserved. Permission to use, copy, modify, and distribute this software and its documentation
* for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all
* copies and that both the copyright notice and this permission notice appear in supporting documentation.
* The RINA laboratory of the Computer Science Department at Boston University makes no
* representations about the suitability of this software for any purpose.
*/
package rina.routing.util;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.Timer;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import rina.object.gpb.LinkStateRoutingEntrySet_t.routingEntrySetForwardedByNeighbor_t;
import rina.object.gpb.LinkStateRoutingEntrySet_t.routingEntrySet_t;
import rina.object.gpb.LinkStateRoutingEntrySet_t.routingEntry_t;
import rina.object.internal.ForwardingTable;
import rina.object.internal.LinkStateRoutingEntry;
import rina.routing.RoutingDaemon;
/**
* This is a component of RoutingDaemon, which contains all the info related to Link State routing
* @author Yuefeng Wang. Computer Science Department, Boston University
*
*/
public class LinkStateRoutingInfo {
private Log log = LogFactory.getLog(this.getClass());
private double inf = RoutingDaemon.inf;
//rina address of this IPC process
private int rinaAddr = -1;
/**
* RoutingEntry for all neighbours
* key is a neighbour, value is a routing entry
*/
private LinkedHashMap<Integer, LinkStateRoutingEntry> neighbourCost = new LinkedHashMap <Integer, LinkStateRoutingEntry>();
// just to make the code efficient
private LinkedList<LinkStateRoutingEntry> neighbourCostList = new LinkedList<LinkStateRoutingEntry>();
//this stores the timestamp of the latest routing entry set from a certain address
//<addr, timestamp>
private LinkedHashMap<Integer,Long> routingEntrySetTimestamp = null;
/**
* information about the whole DIF containing the network topology
* This one is used to calculate the forwarding table
* < addr, < addr, cost > >
*/
private LinkedHashMap <Integer,LinkedHashMap <Integer, Double>> map = null;
private ForwardingTable forwardingTable = null;
//this is the routing entry set received from its neighbor, and need forward to its other neighbor, bacially flooding
private LinkedHashMap<Integer,routingEntrySet_t> routingEntrySetForward = null;
private LinkedList<routingEntrySet_t> routingEntrySetForwardList = null;
public LinkStateRoutingInfo(int rinaAddr, ForwardingTable forwardingTable)
{
this.rinaAddr = rinaAddr;
this.neighbourCost = new LinkedHashMap <Integer, LinkStateRoutingEntry>();
this.neighbourCostList = new LinkedList<LinkStateRoutingEntry>();
this.routingEntrySetTimestamp = new LinkedHashMap<Integer,Long>();
this.map = new LinkedHashMap<Integer, LinkedHashMap <Integer, Double> >();
this.map.put(this.rinaAddr, new LinkedHashMap <Integer, Double>());
this.forwardingTable = forwardingTable;
this.routingEntrySetForward = new LinkedHashMap<Integer,routingEntrySet_t>();
this.routingEntrySetForwardList = new LinkedList<routingEntrySet_t>();
}
/**
* Return true if old entry changes
* Return false if nothing changes
* @param neighborAddr
* @param cost
* @return
*/
public synchronized boolean addCostToNeighbor(int neighborAddr, double cost)
{
long currentTimeStamp = System.currentTimeMillis();
LinkStateRoutingEntry entry = new LinkStateRoutingEntry(this.rinaAddr, neighborAddr, cost,currentTimeStamp);
//this.log.debug(entry.getPrint());
if(this.neighbourCost.containsKey(neighborAddr))
{
// this.log.debug("Old entry exist, now update");
LinkStateRoutingEntry oldEntry = this.neighbourCost.get(neighborAddr);
if(oldEntry.getCost() != entry.getCost() && oldEntry.getTimeStamp() < entry.getTimeStamp())
{
this.neighbourCostList.remove(oldEntry);
this.neighbourCost.put(neighborAddr, entry);
this.neighbourCostList.add(entry);
//this.map.get(this.rinaAddr).put((int)entry.getDstAddr(), entry.getCost());
this.map.get(this.rinaAddr).put(neighborAddr, cost);
//this is added to make the forwardingTable build algorithm work
if( !this.map.containsKey(neighborAddr) )
{
this.map.put(neighborAddr, new LinkedHashMap <Integer, Double>());
this.routingEntrySetTimestamp.put(neighborAddr, (long) 0);
}
// System.out.println("Symmetric added into map:"
// + neighborAddr+ "/" + this.rinaAddr + "/" + entry.getCost());
////////////////////////////////////////////////////////////////////////////
//update forwarding table
this.log.debug("addCostToNeighbor, leads to update FT");
this.buildForwrdingTable();
return true;
}else
{
// this.log.debug("Old entry exist, but nothing changed");
return false; //nothing changes
}
}else
{
// this.log.debug("New entry add");
this.neighbourCost.put(neighborAddr, entry);
this.neighbourCostList.add(entry);
this.map.get(this.rinaAddr).put((int)entry.getDstAddr(), entry.getCost());
//this is added to make the forwardingTable build algorithm work
if( !this.map.containsKey(neighborAddr) )
{
this.map.put(neighborAddr, new LinkedHashMap <Integer, Double>());
this.routingEntrySetTimestamp.put(neighborAddr, (long) 0);
}
// System.out.println("Symmetric added into map:"
// + neighborAddr+ "/" + this.rinaAddr + "/" + entry.getCost());
////////////////////////////////////////////////////////////////////////////
//update forwarding table
this.log.debug("addCostToNeighbor, leads to update FT");
this.buildForwrdingTable();
return true;
}
}
/**
* update when receiving a routingEntrySet object from it neighbors
* Note: it might be not the neighbor's routing entry set, as each node forwards
* the routing entry it received to its neighbors, basically the routing entry set is broadcasting
*
* Return true if old entry changes
* Return false if nothing changes
* @param routingEntrySet
* @return
*/
public synchronized boolean addRoutingEntrySet( routingEntrySet_t routingEntrySet)
{
int addr = (int) routingEntrySet.getAddr();
long timestamp = routingEntrySet.getTimestamp();
//this.log.debug("Routing entry set received about " + addr + ", and time stamp is " + timestamp);
if(addr == this.rinaAddr)
{
//this.log.debug("RoutingEntrySet received about itself, no need to call addRoutingEntrySet()");
return false;
}
if(this.map.containsKey(addr) && this.routingEntrySetTimestamp.get(addr) != (long)0 ) //
{
if(this.routingEntrySetTimestamp.get(addr) >= timestamp)
{
// this.log.debug("New routing entry set received about " + addr + ", but discarded as the timestamp is old");
return false;
}else
{
this.routingEntrySetTimestamp.put(addr, timestamp);
//TESTME
boolean changed = false;
LinkedHashMap <Integer, Double> oldEntrySet = new LinkedHashMap( this.map.get(addr));
this.map.get(addr).clear();
// this.log.debug("OldEntrySet is " + oldEntrySet);
for(int i = 0; i< routingEntrySet.getRoutingEntrySetCount(); i++)
{
routingEntry_t entry = routingEntrySet.getRoutingEntrySet(i);
double oldCost;
if(oldEntrySet.containsKey((int)entry.getDstAddr()))
{
oldCost = oldEntrySet.get((int)entry.getDstAddr() );
}else
{
oldCost = -1; // first time received, and initialized with -1
}
//add to the map, as we cleared the map entry before the for loop
this.map.get(addr).put((int)entry.getDstAddr(), entry.getCost());
if(oldCost != entry.getCost())
{
// this.log.debug("oldCost is " + oldCost + ", and new old is " + entry.getCost() + ", so changed for " + entry.getDstAddr());
changed = true;
}else
{
// this.log.debug("oldCost is " + oldCost + ", and new old is " + entry.getCost() + ", so not changed for " + entry.getDstAddr());
}
//this is added to make the forwardingTable build algorithm work
if( !this.map.containsKey((int)entry.getDstAddr()) )
{
this.map.put((int)entry.getDstAddr(), new LinkedHashMap <Integer, Double>());
this.routingEntrySetTimestamp.put((int)entry.getDstAddr(), (long)0);
}
// System.out.println("Symmetric added into map:"
// + (int)entry.getDstAddr() + "/" + addr + "/" + entry.getCost());
////////////////////////////////////////////////////////////////////////////
}
// this.log.debug("Routing entry set updated for " + addr + " and map is " + this.map + ", and the value of changed is " + changed);
//compare the old set received and the new one, if the new one has less entry, then means the map is change, need return true to trigger updateFT
LinkedHashMap <Integer, Double> newEntrySet = this.map.get(addr);
if(newEntrySet.size() != oldEntrySet.size())
{
changed = true;
this.log.debug("the new one has less entries that the old one");
this.log.debug("oldEntrySet in the map is " + oldEntrySet);
this.log.debug("new EntrySet in the map is " + newEntrySet);
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
return changed;
}
}else // first time got routing entry set about this addr
{
System.out.print(" first time got routing entry set about this addr");
if(!this.map.containsKey(addr))
{
this.map.put(addr, new LinkedHashMap <Integer, Double>() );
}
this.routingEntrySetTimestamp.put(addr, timestamp);
for(int i = 0; i< routingEntrySet.getRoutingEntrySetCount(); i++)
{
routingEntry_t entry = routingEntrySet.getRoutingEntrySet(i);
this.map.get(addr).put((int)entry.getDstAddr(), entry.getCost());
//this is added to make the forwardingTable build algorithm work
if( !this.map.containsKey((int)entry.getDstAddr()) )
{
this.map.put((int)entry.getDstAddr(), new LinkedHashMap <Integer, Double>());
this.routingEntrySetTimestamp.put((int)entry.getDstAddr(), (long)0);
}
// System.out.println("Symmetric added into map:"
// + (int)entry.getDstAddr() + "/" + addr + "/" + entry.getCost());
////////////////////////////////////////////////////////////////////////////
}
// this.log.debug("Routing entry set updated for " + addr + "and map is " + this.map);
return true;
}
}
/**
* This is used when broadcast local routing entry set to its neighbors
* @return
*/
public synchronized routingEntrySet_t getRoutingEntrySet()
{
routingEntrySet_t.Builder entrySet = routingEntrySet_t.newBuilder();
entrySet.setAddr(this.rinaAddr);
entrySet.setTimestamp(System.currentTimeMillis());
for(int i =0; i< this.neighbourCostList.size(); i++)
{
//this.log.debug( "getRoutingEntrySet(): " + this.neighbourCostList.get(i).getPrint());
entrySet.addRoutingEntrySet(this.neighbourCostList.get(i).convert());
}
return entrySet.buildPartial();
}
/**
*
* @return
*/
public synchronized routingEntrySetForwardedByNeighbor_t getRoutingEntrySetForwardToNeighbor()
{
routingEntrySetForwardedByNeighbor_t.Builder routingEntrySetForwardedToNeighbor =
routingEntrySetForwardedByNeighbor_t.newBuilder();
int num = this.routingEntrySetForwardList.size();
//this.log.debug("rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrroutingEntrySetForwardList.size is " + num);
for(int i = 0; i < num; i++)
{
routingEntrySetForwardedToNeighbor.addRoutingEntrySetForwardedByNeighbor(this.routingEntrySetForwardList.get(i));
}
this.printRoutingEntrySetForwardToNeighbor(routingEntrySetForwardedToNeighbor.buildPartial());
return routingEntrySetForwardedToNeighbor.buildPartial();
}
private void printRoutingEntrySetForwardToNeighbor(routingEntrySetForwardedByNeighbor_t forwardedSets)
{
int num = forwardedSets.getRoutingEntrySetForwardedByNeighborCount();
// this.log.debug("printRoutingEntrySetForwardToNeighbor() is called, and num to sent is " + num);
for(int i = 0; i< num; i++)
{
routingEntrySet_t entrySet = forwardedSets.getRoutingEntrySetForwardedByNeighbor(i);
int n =entrySet.getRoutingEntrySetCount();
// this.log.debug("Now print entrySet: origin's addr/timestamp:" + entrySet.getAddr() + "/"
// + entrySet.getTimestamp() + ", and number of the entries inside it is " + n );
for(int j = 0; j < n; j++)
{
LinkStateRoutingEntry entry = new LinkStateRoutingEntry(entrySet.getRoutingEntrySet(j));
//this.log.debug(entry.getPrint());
}
}
}
/**
*
* @param routingEntrySet
*/
public synchronized void addForwardedRoutingEntrySet(routingEntrySet_t routingEntrySet)
{
//this.log.debug("current this.routingEntrySetForwardList.size() " + this.routingEntrySetForwardList.size() );
long originalSender = routingEntrySet.getAddr();
long sendTimeStamp = routingEntrySet.getTimestamp();
if((int)originalSender == this.rinaAddr)
{
// this.log.debug("RoutingEntrySet forwarded received about itself, discard");
return;
}
if(this.routingEntrySetForward.containsKey((int)originalSender))
{
routingEntrySet_t routingEntrySetOld = this.routingEntrySetForward.get((int)originalSender);
long sendTimeStampLast = routingEntrySetOld.getTimestamp();
if(sendTimeStamp <= sendTimeStampLast)
{
// this.log.debug("Old routingEntrySet forwarded by its neighbors, discarded ");
}else
{
boolean remove_result = this.routingEntrySetForwardList.remove(routingEntrySetOld); //remove the old one
// this.log.debug("remove_result is " + remove_result);
this.routingEntrySetForward.put((int)originalSender, routingEntrySet);
this.routingEntrySetForwardList.add(routingEntrySet); //add the new one
// this.log.debug("New routingEntrySet forwarded updated");
// //compare the old entry set received and the new one, remove the one that not in the new set
// for(int i = 0; i< routingEntrySetOld.getRoutingEntrySetCount(); i ++)
// {
// boolean exist = false;
//
// routingEntry_t oldEntry = routingEntrySetOld.getRoutingEntrySet(i);
//
// for(int j = 0; j < routingEntrySet.getRoutingEntrySetCount(); j ++)
// {
// routingEntry_t entry = routingEntrySet.getRoutingEntrySet(j);
//
// if(entry.getDstAddr() == oldEntry.getDstAddr())
// {
// exist = true;
// }
// }
//
// if(exist == false) // in the new set no such entry, remove from the list
// {
// boolean result = this.routingEntrySetForwardList.remove(oldEntry);
//
// this.log.debug( result + ":old Entry removed(src/dst/cost.timestamp) " + oldEntry.getSrcAddr() + "/" + oldEntry.getDstAddr()
// + "/" + oldEntry.getCost() + "/" + oldEntry.getTimestamp());
// }
//
// }
// ///////////////////////////////////////////////////////////////////////////////////////////////////////
}
}else
{
this.routingEntrySetForward.put((int)originalSender, routingEntrySet);
this.routingEntrySetForwardList.add(routingEntrySet);
// this.log.debug("New routingEntrySet forwarded added");
}
// this.log.debug("current this.routingEntrySetForwardList.size() " + this.routingEntrySetForwardList.size() );
}
public synchronized void buildForwrdingTable()
{
this.log.debug("this.map: " + this.map );
//pass the LinkedHashMap<Integer, Integer> to the algorithm, not the forwardingTable object, but
//the forwardingTable object in the object
//Note: routing update sync problem
Dijkstra.buildForwardingTable(this.forwardingTable.getForwardingTable(),this.map,this.rinaAddr);
this.log.debug("ForwardingTable is " + this.forwardingTable.getForwardingTable());
}
public synchronized LinkedHashMap<Integer, LinkedHashMap<Integer, Double>> getMap() {
return map;
}
/**
* This will be used when a neighbor is found to be down
* @param neighborAddr
*/
public synchronized void removeNeighbor(int neighborAddr) {
LinkStateRoutingEntry oldEntry = this.neighbourCost.get(neighborAddr);
this.neighbourCostList.remove(oldEntry);
this.neighbourCost.remove(neighborAddr);
this.map.get(this.rinaAddr).remove(neighborAddr);
this.map.get(neighborAddr).clear();
//this.map.remove(neighborAddr);
//this.routingEntrySetTimestamp.remove(neighborAddr);
// remove the routing entry received from this neighbor, and no need to send this info to its neighbor any more
if(this.routingEntrySetForward.containsKey(neighborAddr))
{
routingEntrySet_t routingEntrySetOld = this.routingEntrySetForward.get((int)neighborAddr);
this.routingEntrySetForwardList.remove(routingEntrySetOld); //remove the old one
this.routingEntrySetForward.remove((int)neighborAddr);
this.log.debug("routingEntrySetForward is modified to clear info about " + neighborAddr);
}
this.log.debug("neighbor removed, leads to update FT");
this.buildForwrdingTable();
}
}