fixed ARP returns wrong MAC && ARP reply uses hazelcast && handle ARP request in two steps
diff --git a/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ProxyArpManager.java b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ProxyArpManager.java
index 0b46778..a0aaf2a 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ProxyArpManager.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ProxyArpManager.java
@@ -32,6 +32,7 @@
 import net.onrc.onos.ofcontroller.core.IDeviceStorage;
 import net.onrc.onos.ofcontroller.core.INetMapTopologyObjects.IDeviceObject;
 import net.onrc.onos.ofcontroller.core.INetMapTopologyObjects.IPortObject;
+import net.onrc.onos.ofcontroller.core.INetMapTopologyObjects.ISwitchObject;
 import net.onrc.onos.ofcontroller.core.INetMapTopologyService.ITopoSwitchService;
 import net.onrc.onos.ofcontroller.core.config.IConfigInfoService;
 import net.onrc.onos.ofcontroller.core.internal.DeviceStorageImpl;
@@ -60,9 +61,9 @@
 										IArpEventHandler, IFloodlightModule {
 	private final static Logger log = LoggerFactory.getLogger(ProxyArpManager.class);
 	
-	private final long ARP_TIMER_PERIOD = 60000; //ms (== 1 min) 
-	
-	private static final int ARP_REQUEST_TIMEOUT = 2000; //ms
+	private final long ARP_TIMER_PERIOD = 100; //ms  
+
+	private static final int ARP_REQUEST_TIMEOUT = 500; //ms
 			
 	private IFloodlightProviderService floodlightProvider;
 	private ITopologyService topology;
@@ -221,6 +222,15 @@
 					log.debug("Cleaning expired ARP request for {}", 
 							entry.getKey().getHostAddress());
 		
+					//if he ARP Request is expired and then delete the device
+					IDeviceObject targetDevice = 
+							deviceStorage.getDeviceByIP(InetAddresses.coerceToInteger(entry.getKey()));
+					
+					if(targetDevice!=null)
+					{deviceStorage.removeDevice(targetDevice);
+					 log.debug("RemoveDevice: {} due to no have not recieve the ARP reply", targetDevice.toString());
+					}				
+					
 					it.remove();
 					
 					if (request.shouldRetry()) {
@@ -277,6 +287,7 @@
 		if (eth.getEtherType() == Ethernet.TYPE_ARP){
 			ARP arp = (ARP) eth.getPayload();	
 			if (arp.getOpCode() == ARP.OP_REQUEST) {
+				log.debug("receive ARP request");
 				//TODO check what the DeviceManager does about propagating
 				//or swallowing ARPs. We want to go after DeviceManager in the
 				//chain but we really need it to CONTINUE ARP packets so we can
@@ -284,7 +295,9 @@
 				handleArpRequest(sw, pi, arp, eth);
 			}
 			else if (arp.getOpCode() == ARP.OP_REPLY) {
-				//handleArpReply(sw, pi, arp);
+					log.debug("receive ARP reply");
+					handleArpReply(sw, pi, arp);
+					sendToOtherNodesReply(eth, pi);
 			}
 		}
 		
@@ -322,57 +335,68 @@
 		}
 		
 		//MACAddress macAddress = arpCache.lookup(target);
-		
+				
 		IDeviceObject targetDevice = 
 				deviceStorage.getDeviceByIP(InetAddresses.coerceToInteger(target));
-		
 		log.debug("targetDevice: {}", targetDevice);
 		
+       arpRequests.put(target, new ArpRequest(
+		 new HostArpRequester(arp, sw.getId(), pi.getInPort()), false));
+		
 		if (targetDevice != null) {
-			// We have the device in our database, so send a reply
+          // Even the device in our database is not null, we do not reply to the request directly, but to check whether the device is still valid
 			MACAddress macAddress = MACAddress.valueOf(targetDevice.getMACAddress());
 			
 			if (log.isTraceEnabled()) {
-				log.trace("Sending reply: {} => {} to host at {}/{}", new Object [] {
-						inetAddressToString(arp.getTargetProtocolAddress()),
-						macAddress.toString(),
-						HexString.toHexString(sw.getId()), pi.getInPort()});
+					log.trace("The target Device Record in DB is: {} => {} from ARP request host at {}/{}", new Object [] {
+					inetAddressToString(arp.getTargetProtocolAddress()),
+					macAddress.toString(),
+					HexString.toHexString(sw.getId()), pi.getInPort()});
 			}
 			
-			sendArpReply(arp, sw.getId(), pi.getInPort(), macAddress);
-		}
-		else {
-			// We don't know the device so broadcast the request out
-			// the edge of the network
+           // sendArpReply(arp, sw.getId(), pi.getInPort(), macAddress);
+			try {
+            		log.trace("Checking the device info from DB is still valid or not");
+    				Iterable<IPortObject> outPorts=targetDevice.getAttachedPorts();	
 			
-			//Record where the request came from so we know where to send the reply
-			arpRequests.put(target, new ArpRequest(
-					new HostArpRequester(arp, sw.getId(), pi.getInPort()), false));
-			
-			sendToOtherNodes(eth, pi);
-		}
-		
-		/*if (macAddress == null){
-			//MAC address is not in our ARP cache.
-			
-			//Record where the request came from so we know where to send the reply
-			//arpRequests.put(target, new ArpRequest(
-					//new HostArpRequester(arp, sw.getId(), pi.getInPort()), false));
-						
-			//Flood the request out edge ports
-			//sendArpRequestToSwitches(target, pi.getPacketData(), sw.getId(), pi.getInPort());
-		}
-		else {
-			//We know the address, so send a reply
-			if (log.isTraceEnabled()) {
-				log.trace("Sending reply: {} => {} to host at {}/{}", new Object [] {
-						inetAddressToString(arp.getTargetProtocolAddress()),
-						macAddress.toString(),
-						HexString.toHexString(sw.getId()), pi.getInPort()});
+    				if(!outPorts.iterator().hasNext())
+    				{
+    					log.debug("outPort : null");
+    					sendToOtherNodes(eth, pi);
+    				}
+    				else{
+    						
+						for (IPortObject portObject : outPorts) {
+							long outSwitch=0;
+							short outPort=0;   
+							
+							
+							if (!portObject.getLinkedPorts().iterator().hasNext()) {
+								outPort=portObject.getNumber();					
+								log.debug("outPort:{} ", outPort);
+								}   
+				
+		            		Iterable<ISwitchObject>  outSwitches= targetDevice.getSwitch(); 
+								
+		            		for (ISwitchObject outswitch : outSwitches) {
+							    
+								outSwitch= HexString.toLong(outswitch.getDPID());
+								log.debug("outSwitch.DPID:{}; outPort: {}", outswitch.getDPID(), outPort );
+								sendToOtherNodes( eth, pi, outSwitch, outPort);
+		            		}
+	            						
+						}
+    				}
+			}catch (Exception e) {
+					log.error("Get attach outSwitche and outPort exception", e);
 			}
+		} else {
+				log.debug("The Device info in DB is {} for IP {}", targetDevice, inetAddressToString(arp.getTargetProtocolAddress()));
 			
-			sendArpReply(arp, sw.getId(), pi.getInPort(), macAddress);
-		}*/
+                        // We don't know the device so broadcast the request out
+           				sendToOtherNodes(eth, pi);
+		}
+ 
 	}
 	
 	private void handleArpReply(IOFSwitch sw, OFPacketIn pi, ARP arp){
@@ -393,7 +417,7 @@
 		
 		MACAddress senderMacAddress = MACAddress.valueOf(arp.getSenderHardwareAddress());
 		
-		arpCache.update(senderIpAddress, senderMacAddress);
+		//arpCache.update(senderIpAddress, senderMacAddress);
 		
 		//See if anyone's waiting for this ARP reply
 		Set<ArpRequest> requests = arpRequests.get(senderIpAddress);
@@ -513,6 +537,51 @@
 		
 		datagrid.sendArpRequest(ArpMessage.newRequest(targetAddress, eth.serialize()));
 	}
+	//hazelcast to other ONOS instances to send the ARP packet out on outPort of outSwitch
+	private void sendToOtherNodes(Ethernet eth, OFPacketIn pi, long outSwitch, short outPort) {
+		ARP arp = (ARP) eth.getPayload();
+		
+		if (log.isTraceEnabled()) {
+				log.trace("Sending ARP request for {} to other ONOS instances with outSwitch {} ",
+				inetAddressToString(arp.getTargetProtocolAddress()), String.valueOf(outSwitch));
+			
+		}
+		
+		InetAddress targetAddress;
+		try {
+				targetAddress = InetAddress.getByAddress(arp.getTargetProtocolAddress());
+		} catch (UnknownHostException e) {
+				log.error("Unknown host", e);
+				return;
+		}
+		
+		datagrid.sendArpRequest(ArpMessage.newRequest(targetAddress, eth.serialize(), outSwitch, outPort)); 
+		//datagrid.sendArpRequest(ArpMessage.newRequest(targetAddress, eth.serialize()));
+		
+		
+	}
+	private void sendToOtherNodesReply(Ethernet eth, OFPacketIn pi) {
+		ARP arp = (ARP) eth.getPayload();
+		
+		if (log.isTraceEnabled()) {
+				log.trace("Sending ARP reply for {} to other ONOS instances",
+				inetAddressToString(arp.getSenderProtocolAddress()));
+		}
+		
+		InetAddress targetAddress;		
+		MACAddress mac = new MACAddress(arp.getSenderHardwareAddress());
+		
+		try {
+				targetAddress = InetAddress.getByAddress(arp.getSenderProtocolAddress());
+		} catch (UnknownHostException e) {
+				log.error("Unknown host", e);
+				return;
+		}
+		
+		datagrid.sendArpRequest(ArpMessage.newReply(targetAddress,mac));
+		//datagrid.sendArpReply(ArpMessage.newRequest(targetAddress, eth.serialize()));
+	
+	}
 	
 	private void broadcastArpRequestOutEdge(byte[] arpRequest, long inSwitch, short inPort) {
 		for (IOFSwitch sw : floodlightProvider.getSwitches().values()){
@@ -603,7 +672,7 @@
 			}
 		}
 		
-		log.debug("Broadcast ARP request for to: {}", switchPorts);
+		log.debug("Broadcast ARP request to: {}", switchPorts);
 	}
 	
 	private void sendArpRequestOutPort(byte[] arpRequest, long dpid, short port) {
@@ -743,23 +812,35 @@
 	
 	@Override
 	public void arpRequestNotification(ArpMessage arpMessage) {
-		//log.debug("Received ARP notification from other instances");
+		log.debug("Received ARP notification from other instances");
 		
 		switch (arpMessage.getType()){
 		case REQUEST:
-			log.debug("Received ARP request notification for {}", 
-					arpMessage.getAddress());
-			broadcastArpRequestOutMyEdge(arpMessage.getPacket());
+    			if (arpMessage.getOutSwitch()==-1)
+    			{		log.debug("OutSwitch in ARP request Message is null");
+    			}
+    			if (arpMessage.getOutPort()==-1)
+    			{		log.debug("OutPort in ARP request Message is null");
+    			}
+    			
+    			if(arpMessage.getOutSwitch() ==-1 || arpMessage.getOutPort()==-1){
+    					broadcastArpRequestOutMyEdge(arpMessage.getPacket());
+    			}else if (arpMessage.getOutSwitch() >0 && arpMessage.getOutPort()>0 ){
+    					log.debug("OutSwitch in ARP request message is: {}; OutPort in ARP request message is: {}",arpMessage.getOutSwitch(),arpMessage.getOutPort());
+    					sendArpRequestOutPort(arpMessage.getPacket(),arpMessage.getOutSwitch(),arpMessage.getOutPort());
+    				
+    			}else{
+    					broadcastArpRequestOutMyEdge(arpMessage.getPacket());}
 			break;
 		case REPLY:
 			log.debug("Received ARP reply notification for {}",
 					arpMessage.getAddress());
-			sendArpReplyToWaitingRequesters(arpMessage.getAddress());
+			sendArpReplyToWaitingRequesters(arpMessage.getAddress(),arpMessage.getMAC());
 			break;
 		}
 	}
 	
-	private void sendArpReplyToWaitingRequesters(InetAddress address) {
+	private void sendArpReplyToWaitingRequesters(InetAddress address, MACAddress mac) {
 		log.debug("Sending ARP reply for {} to requesters", 
 				address.getHostAddress());
 		
@@ -777,13 +858,13 @@
 			}
 		}
 		
-		IDeviceObject deviceObject = deviceStorage.getDeviceByIP(
+		/*IDeviceObject deviceObject = deviceStorage.getDeviceByIP(
 				InetAddresses.coerceToInteger(address));
 		
 		MACAddress mac = MACAddress.valueOf(deviceObject.getMACAddress());
 		
 		log.debug("Found {} at {} in network map", 
-				address.getHostAddress(), mac);
+				address.getHostAddress(), mac);*/
 		
 		//Don't hold an ARP lock while dispatching requests
 		for (ArpRequest request : requestsToSend) {