Added a REST API to the Proxy ARP module to inspect the ARP cache
diff --git a/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ArpCache.java b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ArpCache.java
index 4af4b05..83a3b55 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ArpCache.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ArpCache.java
@@ -1,7 +1,9 @@
 package net.onrc.onos.ofcontroller.proxyarp;
 
 import java.net.InetAddress;
+import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
 import net.floodlightcontroller.util.MACAddress;
@@ -20,10 +22,10 @@
  * don't do this which means the cache memory size will never decrease. We already
  * have a periodic thread that can be used to do this in ProxyArpManager.
  */
-public class ArpCache {
+class ArpCache {
 	private final static Logger log = LoggerFactory.getLogger(ArpCache.class);
 	
-	private final long ARP_ENTRY_TIMEOUT = 60000; //ms (1 min)
+	private final static long ARP_ENTRY_TIMEOUT = 60000; //ms (1 min)
 	
 	//Protected by locking on the ArpCache object
 	private final Map<InetAddress, ArpCacheEntry> arpCache;
@@ -40,29 +42,28 @@
 		public MACAddress getMacAddress() {
 			return macAddress;
 		}
-
-		public long getTimeLastSeen() {
-			return timeLastSeen;
-		}
 		
 		public void setTimeLastSeen(long time){
 			timeLastSeen = time;
 		}
+		
+		public boolean isExpired() {
+			return System.currentTimeMillis() - timeLastSeen > ARP_ENTRY_TIMEOUT;
+		}
 	}
 
-	public ArpCache() {
+	ArpCache() {
 		arpCache = new HashMap<InetAddress, ArpCacheEntry>();
 	}
 
-	public synchronized MACAddress lookup(InetAddress ipAddress){	
+	synchronized MACAddress lookup(InetAddress ipAddress){	
 		ArpCacheEntry arpEntry = arpCache.get(ipAddress);
 		
 		if (arpEntry == null){
 			return null;
 		}
 		
-		if (System.currentTimeMillis() - arpEntry.getTimeLastSeen() 
-				> ARP_ENTRY_TIMEOUT){
+		if (arpEntry.isExpired()) {
 			//Entry has timed out so we'll remove it and return null
 			log.trace("Removing expired ARP entry for {}", ipAddress.getHostAddress());
 			
@@ -73,7 +74,7 @@
 		return arpEntry.getMacAddress();
 	}
 
-	public synchronized void update(InetAddress ipAddress, MACAddress macAddress){
+	synchronized void update(InetAddress ipAddress, MACAddress macAddress){
 		ArpCacheEntry arpEntry = arpCache.get(ipAddress);
 		
 		if (arpEntry != null && arpEntry.getMacAddress().equals(macAddress)){
@@ -83,4 +84,16 @@
 			arpCache.put(ipAddress, new ArpCacheEntry(macAddress));
 		}
 	}
+	
+	synchronized List<String> getMappings() {
+		List<String> result = new ArrayList<String>(arpCache.size());
+		
+		for (Map.Entry<InetAddress, ArpCacheEntry> entry : arpCache.entrySet()) {
+			result.add(entry.getKey().getHostAddress() + " => " + 
+					entry.getValue().getMacAddress().toString() + 
+					(entry.getValue().isExpired()?" : EXPIRED":" : VALID"));
+		}
+		
+		return result;
+	}
 }
diff --git a/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ArpCacheResource.java b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ArpCacheResource.java
new file mode 100644
index 0000000..252e66e
--- /dev/null
+++ b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ArpCacheResource.java
@@ -0,0 +1,18 @@
+package net.onrc.onos.ofcontroller.proxyarp;
+
+import java.util.List;
+
+import org.restlet.resource.Get;
+import org.restlet.resource.ServerResource;
+
+public class ArpCacheResource extends ServerResource {
+
+	@Get("json")
+	public List<String> getArpCache() {
+		IProxyArpService arp = (IProxyArpService) getContext().getAttributes().
+				get(IProxyArpService.class.getCanonicalName());
+		
+		return arp.getMappings();
+	}
+
+}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ArpWebRoutable.java b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ArpWebRoutable.java
new file mode 100644
index 0000000..eefa2db
--- /dev/null
+++ b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ArpWebRoutable.java
@@ -0,0 +1,22 @@
+package net.onrc.onos.ofcontroller.proxyarp;
+
+import net.floodlightcontroller.restserver.RestletRoutable;
+
+import org.restlet.Context;
+import org.restlet.Restlet;
+import org.restlet.routing.Router;
+
+public class ArpWebRoutable implements RestletRoutable {
+
+	@Override
+	public Restlet getRestlet(Context context) {
+		Router router = new Router(context);
+		router.attach("/cache/json", ArpCacheResource.class);
+		return router;
+	}
+
+	@Override
+	public String basePath() {
+		return "/wm/arp";
+	}
+}
diff --git a/src/main/java/net/onrc/onos/ofcontroller/proxyarp/IProxyArpService.java b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/IProxyArpService.java
index 2c5dd72..97844d3 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/proxyarp/IProxyArpService.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/IProxyArpService.java
@@ -1,10 +1,13 @@
 package net.onrc.onos.ofcontroller.proxyarp;
 
 import java.net.InetAddress;
+import java.util.List;
 
+import net.floodlightcontroller.core.module.IFloodlightService;
 import net.floodlightcontroller.util.MACAddress;
 
-public interface IProxyArpService {
+//Extends IFloodlightService so we can access it from REST API resources
+public interface IProxyArpService extends IFloodlightService{
 	/**
 	 * Returns the MAC address if there is a valid entry in the cache.
 	 * Otherwise returns null.
@@ -22,4 +25,10 @@
 	 */
 	public void sendArpRequest(InetAddress ipAddress, IArpRequester requester,
 			boolean retry);
+	
+	/**
+	 * Returns a snapshot of the entire ARP cache.
+	 * @return
+	 */
+	public List<String> getMappings();
 }
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 10aa87a..31cc4fc 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ProxyArpManager.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ProxyArpManager.java
@@ -19,6 +19,7 @@
 import net.floodlightcontroller.packet.ARP;
 import net.floodlightcontroller.packet.Ethernet;
 import net.floodlightcontroller.packet.IPv4;
+import net.floodlightcontroller.restserver.IRestApiService;
 import net.floodlightcontroller.topology.ITopologyService;
 import net.floodlightcontroller.util.MACAddress;
 import net.onrc.onos.ofcontroller.bgproute.ILayer3InfoService;
@@ -39,7 +40,6 @@
 import com.google.common.collect.Multimaps;
 import com.google.common.collect.SetMultimap;
 
-//TODO REST API to inspect ARP table
 public class ProxyArpManager implements IProxyArpService, IOFMessageListener {
 	private final static Logger log = LoggerFactory.getLogger(ProxyArpManager.class);
 	
@@ -50,6 +50,7 @@
 	private final IFloodlightProviderService floodlightProvider;
 	private final ITopologyService topology;
 	private final ILayer3InfoService layer3;
+	private final IRestApiService restApi;
 	
 	private final ArpCache arpCache;
 
@@ -103,10 +104,12 @@
 	}
 	
 	public ProxyArpManager(IFloodlightProviderService floodlightProvider,
-				ITopologyService topology, ILayer3InfoService layer3){
+				ITopologyService topology, ILayer3InfoService layer3,
+				IRestApiService restApi){
 		this.floodlightProvider = floodlightProvider;
 		this.topology = topology;
 		this.layer3 = layer3;
+		this.restApi = restApi;
 		
 		arpCache = new ArpCache();
 
@@ -115,6 +118,8 @@
 	}
 	
 	public void startUp() {
+		restApi.addRestletRoutable(new ArpWebRoutable());
+		
 		Timer arpTimer = new Timer("arp-processing");
 		arpTimer.scheduleAtFixedRate(new TimerTask() {
 			@Override
@@ -552,4 +557,9 @@
 			sendArpRequestForAddress(ipAddress);
 		}
 	}
+	
+	@Override
+	public List<String> getMappings() {
+		return arpCache.getMappings();
+	}
 }