Cherry-pick from https://gerrit.onos.onlab.us/#/c/342/

Cherry-pick from https://gerrit.onos.onlab.us/#/c/322/

Suppress device update for the same device.
Add function to clean devices.

Conflicts:

	src/main/java/net/onrc/onos/ofcontroller/devicemanager/OnosDeviceManager.java

NOTE: The above conflict has been resolved by hand.
The conflict was in the number of parameters when
calling deviceStorage.init() inside file OnosDeviceManager.java:
deviceStorage.init("") -> deviceStorage.init("","")

Conflicts:

	src/main/java/net/onrc/onos/datagrid/HazelcastDatagrid.java
	src/main/java/net/onrc/onos/datagrid/IDatagridService.java

NOTE: The above conflict has been resolved by hand.

Change-Id: I831e391efce9a795370be99238834b6bc7db2bee
diff --git a/src/main/java/net/onrc/onos/ofcontroller/core/IDeviceStorage.java b/src/main/java/net/onrc/onos/ofcontroller/core/IDeviceStorage.java
index 624137c..53f515c 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/core/IDeviceStorage.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/core/IDeviceStorage.java
@@ -18,4 +18,5 @@
 	public void commit();
 
 	public void addOnosDevice(OnosDevice onosDevice);
+	public void deleteOnosDevice(OnosDevice onosDevice);
 }
diff --git a/src/main/java/net/onrc/onos/ofcontroller/core/internal/DeviceStorageImpl.java b/src/main/java/net/onrc/onos/ofcontroller/core/internal/DeviceStorageImpl.java
index 5a67c12..f794a2b 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/core/internal/DeviceStorageImpl.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/core/internal/DeviceStorageImpl.java
@@ -122,6 +122,7 @@
 	 */
 	@Override
 	public void removeDevice(IDevice device) {
+		//Not used
 		IDeviceObject dev;
 
 		if ((dev = ope.searchDevice(device.getMACAddressString())) != null) {
@@ -428,5 +429,21 @@
 			ope.rollback();
 		}
 	}
+	
+	@Override
+	public void deleteOnosDevice(OnosDevice onosDevice){
+		String macAddress = HexString.toHexString(onosDevice.getMacAddress().toBytes());	
+		try {
+			IDeviceObject device = ope.searchDevice(macAddress);
+			if(device != null){
+				ope.removeDevice(device);
+			}
+			ope.commit();
+		}
+		catch (TitanException e){
+			log.error("deleteOnosDevice {} failed:", macAddress, e);
+			ope.rollback();
+		}
+	}
 
 }
diff --git a/src/main/java/net/onrc/onos/ofcontroller/devicemanager/IDeviceEventHandler.java b/src/main/java/net/onrc/onos/ofcontroller/devicemanager/IDeviceEventHandler.java
new file mode 100644
index 0000000..81bc09f
--- /dev/null
+++ b/src/main/java/net/onrc/onos/ofcontroller/devicemanager/IDeviceEventHandler.java
@@ -0,0 +1,7 @@
+package net.onrc.onos.ofcontroller.devicemanager;
+
+public interface IDeviceEventHandler {
+	public void addDeviceEvent(Long key, OnosDevice value);
+	public void deleteDeviceEvent(Long key, OnosDevice value);
+	public void updateDeviceEvent(Long key, OnosDevice value);
+}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/devicemanager/IOnosDeviceService.java b/src/main/java/net/onrc/onos/ofcontroller/devicemanager/IOnosDeviceService.java
index 1e40aa5..9125985 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/devicemanager/IOnosDeviceService.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/devicemanager/IOnosDeviceService.java
@@ -11,4 +11,8 @@
  */
 public interface IOnosDeviceService extends IFloodlightService {
 
+	public void deleteOnosDevice(OnosDevice dev);
+
+	public void addOnosDevice(Long mac, OnosDevice dev);
+
 }
diff --git a/src/main/java/net/onrc/onos/ofcontroller/devicemanager/OnosDevice.java b/src/main/java/net/onrc/onos/ofcontroller/devicemanager/OnosDevice.java
index 34d2c38..4b47501 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/devicemanager/OnosDevice.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/devicemanager/OnosDevice.java
@@ -17,6 +17,7 @@
 
 package net.onrc.onos.ofcontroller.devicemanager;
 
+import java.io.Serializable;
 import java.util.Date;
 
 import net.floodlightcontroller.devicemanager.internal.Entity;
@@ -37,7 +38,7 @@
  * @author readams
  *
  */
-public class OnosDevice { //implements Comparable<OnosDevice> {
+public class OnosDevice implements Serializable { //implements Comparable<OnosDevice> {
     /**
      * Timeout for computing {@link Entity#activeSince}.
      * @see {@link Entity#activeSince}
@@ -126,6 +127,10 @@
     public Integer getIpv4Address() {
         return ipv4Address;
     }
+    
+    public void setIpv4Address(Integer ipv4Address) {
+    	this.ipv4Address = ipv4Address;
+    }
 
     public Short getVlan() {
         return vlan;
@@ -134,10 +139,18 @@
     public Long getSwitchDPID() {
         return switchDPID;
     }
+    
+    public void setSwitchDPID(long dpid) {
+    	this.switchDPID = dpid;
+    }
 
     public short getSwitchPort() {
         return switchPort;
     }
+    
+    public void setSwitchPort(short port) {
+    	this.switchPort = port;
+    }
 
     public Date getLastSeenTimestamp() {
         return lastSeenTimestamp;
@@ -165,7 +178,6 @@
         this.activeSince = activeSince;
     }
     
-    /*
     @Override
     public int hashCode() {
         if (hashCode != 0) return hashCode;
@@ -173,10 +185,9 @@
         hashCode = 1;
         hashCode = prime * hashCode
                  + ((ipv4Address == null) ? 0 : ipv4Address.hashCode());
-        //hashCode = prime * hashCode + (int) (macAddress ^ (macAddress >>> 32));
-        hashCode = prime * macAddress.hashCode();
-        hashCode = prime * hashCode + (int) (switchDPID ^ (switchDPID >>> 32));
-        hashCode = prime * hashCode + switchPort;
+        hashCode = prime * hashCode + (int) (macAddress.toLong() ^ (macAddress.toLong() >>> 32));
+        hashCode = prime * hashCode + (int)switchDPID;
+        hashCode = prime * hashCode + (int)switchPort;
         hashCode = prime * hashCode + ((vlan == null) ? 0 : vlan.hashCode());
         return hashCode;
     }
@@ -186,26 +197,21 @@
         if (this == obj) return true;
         if (obj == null) return false;
         if (getClass() != obj.getClass()) return false;
-        Entity other = (Entity) obj;
+        OnosDevice other = (OnosDevice) obj;
         if (hashCode() != other.hashCode()) return false;
         if (ipv4Address == null) {
             if (other.ipv4Address != null) return false;
-        } else if (!ipv4Address.equals(other.ipv4Address)) return false;
-        if (macAddress != other.macAddress) return false;
-        if (switchDPID == null) {
-            if (other.switchDPID != null) return false;
-        } else if (!switchDPID.equals(other.switchDPID)) return false;
-        if (switchPort == null) {
-            if (other.switchPort != null) return false;
-        } else if (!switchPort.equals(other.switchPort)) return false;
+        } else if (!ipv4Address.equals(other.ipv4Address)) return false; 
+        if (macAddress == null) {
+            if (other.macAddress != null) return false;
+        } else if (!macAddress.equals(other.macAddress)) return false;
+        if(switchDPID != other.switchDPID) return false;
+        if (switchPort != other.switchPort) return false;
         if (vlan == null) {
             if (other.vlan != null) return false;
         } else if (!vlan.equals(other.vlan)) return false;
         return true;
     }
-    */
-
-    
     
     @Override
     public String toString() {
diff --git a/src/main/java/net/onrc/onos/ofcontroller/devicemanager/OnosDeviceManager.java b/src/main/java/net/onrc/onos/ofcontroller/devicemanager/OnosDeviceManager.java
index 1b14875..e9ddfa7 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/devicemanager/OnosDeviceManager.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/devicemanager/OnosDeviceManager.java
@@ -4,8 +4,14 @@
 import java.util.Collection;
 import java.util.Date;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
 
 import net.floodlightcontroller.core.FloodlightContext;
 import net.floodlightcontroller.core.IFloodlightProviderService;
@@ -22,29 +28,51 @@
 import net.floodlightcontroller.packet.IPv4;
 import net.floodlightcontroller.packet.UDP;
 import net.floodlightcontroller.util.MACAddress;
+import net.onrc.onos.datagrid.IDatagridService;
 import net.onrc.onos.ofcontroller.core.IDeviceStorage;
 import net.onrc.onos.ofcontroller.core.internal.DeviceStorageImpl;
+import net.onrc.onos.ofcontroller.topology.TopologyElement;
+import net.onrc.onos.ofcontroller.topology.TopologyElement.Type;
 
 import org.openflow.protocol.OFMessage;
 import org.openflow.protocol.OFPacketIn;
 import org.openflow.protocol.OFType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 public class OnosDeviceManager implements IFloodlightModule, IOFMessageListener,
 										IOnosDeviceService {
-	private IDeviceStorage deviceStorage;
+	protected final static Logger log = LoggerFactory.getLogger(OnosDeviceManager.class);
+	private static final int CLEANUP_SECOND = 60*60;
+	private static final int AGEING_MILLSEC = 60*60*1000;
 	
+	private IDeviceStorage deviceStorage;
 	private IFloodlightProviderService floodlightProvider;
-
+	private final static ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
+	
+	private IDatagridService datagrid;
+	private Map<Long, OnosDevice> mapDevice = new ConcurrentHashMap<Long, OnosDevice>();
+	
+    public enum OnosDeviceUpdateType {
+        ADD, DELETE, UPDATE;
+    }
+	
 	private class OnosDeviceUpdate implements IUpdate {
 		private OnosDevice device;
+		private OnosDeviceUpdateType type;
 		
-		public OnosDeviceUpdate(OnosDevice device) {
+		public OnosDeviceUpdate(OnosDevice device, OnosDeviceUpdateType type) {
 			this.device = device;
+			this.type = type;
 		}
 		
 		@Override
 		public void dispatch() {
-			deviceStorage.addOnosDevice(device);
+			if(type == OnosDeviceUpdateType.ADD) {
+				deviceStorage.addOnosDevice(device);		
+			} else if (type == OnosDeviceUpdateType.DELETE){
+				deviceStorage.deleteOnosDevice(device);		
+			}
 		}
 	}
 	
@@ -81,18 +109,98 @@
 	}
 	
 	private Command processPacketIn(IOFSwitch sw, OFPacketIn pi, Ethernet eth) {
-		
-        // Extract source entity information
+        long dpid =sw.getId();
+        short portId = pi.getInPort();
+        Long mac = eth.getSourceMAC().toLong();
+
         OnosDevice srcDevice =
-                getSourceDeviceFromPacket(eth, sw.getId(), pi.getInPort());
-        if (srcDevice == null)
-            return Command.STOP;
+                getSourceDeviceFromPacket(eth, dpid, portId);
+
+        if (srcDevice == null){
+        	return Command.STOP;
+        }
         
-        floodlightProvider.publishUpdate(new OnosDeviceUpdate(srcDevice));
+        //We check if it is the same device in datagrid to suppress the device update
+        OnosDevice exDev = null;
+        if((exDev = mapDevice.get(mac)) != null ){
+	    	if(exDev.equals(srcDevice)) {
+	    		//There is the same existing device. Update only ActiveSince time.
+	        	exDev.setLastSeenTimestamp(new Date());
+	        	if(log.isTraceEnabled()) {
+			        log.debug("In the datagrid, there is the same device."
+							+ "Only update last seen time. dpid {}, port {}, mac {}, ip {}, lastSeenTime {}",
+			        		dpid, portId, srcDevice.getMacAddress(), srcDevice.getIpv4Address(), srcDevice.getLastSeenTimestamp().getTime());
+	        	}
+		        return Command.CONTINUE;
+	    	} else if (srcDevice.getIpv4Address() == null && 
+	    			exDev.getSwitchDPID() == srcDevice.getSwitchDPID() &&
+	    			exDev.getSwitchPort() == srcDevice.getSwitchPort() &&
+	    			exDev.getVlan() == srcDevice.getVlan()) {
+	    		//Device attachment point and mac address are the same 
+	    		//but the packet does not have an ip address.
+	        	exDev.setLastSeenTimestamp(new Date());
+	        	if(log.isTraceEnabled()) {
+		        	log.debug("In the datagrid, there is the same device with no ip."
+							+ "Keep ip and update last seen time. dpid {}, port {}, mac {}, ip {}, lastSeenTime {}",
+							dpid, portId, srcDevice.getMacAddress(), srcDevice.getIpv4Address(), srcDevice.getLastSeenTimestamp().getTime());
+	        	}
+	        	return Command.CONTINUE;
+	    	}
+        }
         
+        //If the switch port we try to attach a new device already has a link, then stop adding device
+        Collection<TopologyElement> list = datagrid.getAllTopologyElements();
+        for(TopologyElement elem: list) {
+        	if(elem.getType() == Type.ELEMENT_LINK) {
+        		if((elem.getFromPort() == portId && elem.getFromSwitch() == dpid) ||
+        				(elem.getToPort() == portId && elem.getToSwitch() == dpid)) {
+        			if(log.isTraceEnabled()) {
+	        			log.debug("Stop adding OnosDevice {} due to there is a link to: dpid {} port {}",
+	    						srcDevice.getMacAddress(), dpid, portId);
+        			}
+        			return Command.CONTINUE;
+        		}
+        	}
+        }
+        
+        addOnosDevice(mac, srcDevice);
+        
+        if(log.isTraceEnabled()) {
+	        log.debug("Add device info in the set. dpid {}, port {}, mac {}, ip {}, lastSeenTime {}",
+	       		dpid, portId, srcDevice.getMacAddress(), srcDevice.getIpv4Address(), srcDevice.getLastSeenTimestamp().getTime());
+        }
         return Command.CONTINUE;
 	}
 	
+     //Thread to delete devices periodically. 
+	 //Remove all devices from the map first and then finally delete devices from the DB.
+	private class CleanDevice implements Runnable {
+		@Override
+		public void run() {
+			log.debug("called CleanDevice");
+			try{
+		        	Set<OnosDevice> deleteSet = new HashSet<OnosDevice>();
+			        for (OnosDevice dev : mapDevice.values() ) {
+			        	long now = new Date().getTime();
+			        	if((now - dev.getLastSeenTimestamp().getTime() > AGEING_MILLSEC)) {
+			        		if(log.isTraceEnabled()) {
+			        			log.debug("Remove device info in the datagrid. dpid {}, port {}, mac {}, ip {}, lastSeenTime {}, diff {}",
+				        				dev.getSwitchDPID(), dev.getSwitchPort(), dev.getMacAddress(), dev.getIpv4Address(),
+				        				dev.getLastSeenTimestamp().getTime(), now - dev.getLastSeenTimestamp().getTime());
+			        		}
+			        		deleteSet.add(dev);
+			        	}
+			        }
+			     
+			        for(OnosDevice dev : deleteSet) {
+			        	deleteOnosDevice(dev);        	
+			        }
+			 } catch(Exception e) {
+		    	 log.error("Error:", e);
+		     }
+		}
+	}
+
     /**
      * Get IP address from packet if the packet is either an ARP 
      * or a DHCP packet
@@ -177,14 +285,50 @@
 	public void init(FloodlightModuleContext context)
 			throws FloodlightModuleException {
 		floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
-		
+		executor.scheduleAtFixedRate(new CleanDevice(), 30 ,CLEANUP_SECOND, TimeUnit.SECONDS);
 		deviceStorage = new DeviceStorageImpl();
 		deviceStorage.init("","");
+		
+		datagrid = context.getServiceImpl(IDatagridService.class);
 	}
 
 	@Override
 	public void startUp(FloodlightModuleContext context) {
 		floodlightProvider.addOFMessageListener(OFType.PACKET_IN, this);
+		datagrid.registerMapDeviceEventHandler(new MapDevListener());
 	}
 
+	@Override
+	public void deleteOnosDevice(OnosDevice dev) {
+		datagrid.sendNotificationDeviceDeleted(dev);
+		floodlightProvider.publishUpdate(new OnosDeviceUpdate(dev, OnosDeviceUpdateType.DELETE));
+	}
+	
+	@Override
+	public void addOnosDevice(Long mac, OnosDevice dev) {
+        datagrid.sendNotificationDeviceAdded(mac, dev);
+        floodlightProvider.publishUpdate(new OnosDeviceUpdate(dev, OnosDeviceUpdateType.ADD));
+	}
+	
+	//This is listener for datagrid mapDevice change.
+    class MapDevListener implements IDeviceEventHandler {
+
+		@Override
+		public void addDeviceEvent(Long mac, OnosDevice dev) {	
+			mapDevice.put(mac, dev);
+			log.debug("addDeviceMap: device mac {}", mac);
+		}
+
+		@Override
+		public void deleteDeviceEvent(Long mac, OnosDevice dev) {	
+			mapDevice.remove(mac);
+			log.debug("deleteDeviceMap: device mac {}", mac);	
+		}
+
+		@Override
+		public void updateDeviceEvent(Long mac, OnosDevice dev) {	
+			mapDevice.put(mac, dev);
+			log.debug("updateDeviceMap: device mac {}", mac);
+		}
+    }
 }