Added a REST API to the Proxy ARP module to inspect the ARP cache
diff --git a/src/main/java/net/onrc/onos/ofcontroller/bgproute/BgpRoute.java b/src/main/java/net/onrc/onos/ofcontroller/bgproute/BgpRoute.java
index c70a2ea..a698fd5 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/bgproute/BgpRoute.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/bgproute/BgpRoute.java
@@ -40,6 +40,7 @@
 import net.onrc.onos.ofcontroller.linkdiscovery.ILinkDiscovery.LDUpdate;
 import net.onrc.onos.ofcontroller.linkdiscovery.ILinkDiscoveryService;
 import net.onrc.onos.ofcontroller.proxyarp.IArpRequester;
+import net.onrc.onos.ofcontroller.proxyarp.IProxyArpService;
 import net.onrc.onos.ofcontroller.proxyarp.ProxyArpManager;
 import net.onrc.onos.ofcontroller.routing.TopoRouteService;
 import net.onrc.onos.ofcontroller.util.DataPath;
@@ -76,7 +77,8 @@
 
 public class BgpRoute implements IFloodlightModule, IBgpRouteService, 
 									ITopologyListener, IArpRequester,
-									IOFSwitchListener, ILayer3InfoService {
+									IOFSwitchListener, ILayer3InfoService,
+									IProxyArpService {
 	
 	protected static Logger log = LoggerFactory.getLogger(BgpRoute.class);
 
@@ -232,6 +234,7 @@
 		Map<Class<? extends IFloodlightService>, IFloodlightService> m 
 			= new HashMap<Class<? extends IFloodlightService>, IFloodlightService>();
 		m.put(IBgpRouteService.class, this);
+		m.put(IProxyArpService.class, this);
 		return m;
 	}
 
@@ -262,7 +265,7 @@
 		
 		//TODO We'll initialise this here for now, but it should really be done as
 		//part of the controller core
-		proxyArp = new ProxyArpManager(floodlightProvider, topology, this);
+		proxyArp = new ProxyArpManager(floodlightProvider, topology, this, restApi);
 		
 		linkUpdates = new ArrayList<LDUpdate>();
 		ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
@@ -1298,4 +1301,27 @@
 	public MACAddress getRouterMacAddress() {
 		return bgpdMacAddress;
 	}
+
+	/*
+	 * TODO This is a hack to get the REST API to work for ProxyArpManager.
+	 * The REST API is currently tied to the Floodlight module system and we
+	 * need to separate it to allow ONOS modules to use it. For now we will 
+	 * proxy calls through to the ProxyArpManager (which is not a Floodlight 
+	 * module) through this class which is a module.
+	 */
+	@Override
+	public MACAddress getMacAddress(InetAddress ipAddress) {
+		return proxyArp.getMacAddress(ipAddress);
+	}
+
+	@Override
+	public void sendArpRequest(InetAddress ipAddress, IArpRequester requester,
+			boolean retry) {
+		proxyArp.sendArpRequest(ipAddress, requester, retry);		
+	}
+
+	@Override
+	public List<String> getMappings() {
+		return proxyArp.getMappings();
+	}
 }
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();
+	}
 }