Addressed some checkstyle, PMD and findbugs violations in the ARP module

Change-Id: I194533ba5f96a7631ea662a93cbcbd8a2c84dea9
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 83a3b55..0e965ff 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ArpCache.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ArpCache.java
@@ -11,89 +11,133 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+/*
+ * TODO clean out old ARP entries out of the cache periodically. We currently
+ * 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.
+ */
+
 /**
  * Implements a basic ARP cache which maps IPv4 addresses to MAC addresses.
  * Mappings time out after a short period of time (currently 1 min). We don't
- * try and refresh the mapping before the entry times out because as a controller
- * we don't know if the mapping is still needed.
- */
-
-/* TODO clean out old ARP entries out of the cache periodically. We currently 
- * 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.
+ * try and refresh the mapping before the entry times out because as a
+ * controller we don't know if the mapping is still needed.
  */
 class ArpCache {
-	private final static Logger log = LoggerFactory.getLogger(ArpCache.class);
-	
-	private final static long ARP_ENTRY_TIMEOUT = 60000; //ms (1 min)
-	
-	//Protected by locking on the ArpCache object
-	private final Map<InetAddress, ArpCacheEntry> arpCache;
-	
-	private static class ArpCacheEntry {
-		private final MACAddress macAddress;
-		private long timeLastSeen;	
+    private static final Logger log = LoggerFactory.getLogger(ArpCache.class);
 
-		public ArpCacheEntry(MACAddress macAddress) {
-			this.macAddress = macAddress;
-			this.timeLastSeen = System.currentTimeMillis();
-		}
+    private static final long ARP_ENTRY_TIMEOUT = 60000; // ms (1 min)
 
-		public MACAddress getMacAddress() {
-			return macAddress;
-		}
-		
-		public void setTimeLastSeen(long time){
-			timeLastSeen = time;
-		}
-		
-		public boolean isExpired() {
-			return System.currentTimeMillis() - timeLastSeen > ARP_ENTRY_TIMEOUT;
-		}
-	}
+    // Protected by locking on the ArpCache object (this)
+    private final Map<InetAddress, ArpCacheEntry> arpCache;
 
-	ArpCache() {
-		arpCache = new HashMap<InetAddress, ArpCacheEntry>();
-	}
+    /**
+     * Represents a MAC address entry with a timestamp in the ARP cache.
+     * ARP cache entries are considered invalid if their timestamp is older
+     * than a timeout value.
+     */
+    private static class ArpCacheEntry {
+        private final MACAddress macAddress;
+        private long timeLastSeen;
 
-	synchronized MACAddress lookup(InetAddress ipAddress){	
-		ArpCacheEntry arpEntry = arpCache.get(ipAddress);
-		
-		if (arpEntry == null){
-			return null;
-		}
-		
-		if (arpEntry.isExpired()) {
-			//Entry has timed out so we'll remove it and return null
-			log.trace("Removing expired ARP entry for {}", ipAddress.getHostAddress());
-			
-			arpCache.remove(ipAddress);
-			return null;
-		}
-		
-		return arpEntry.getMacAddress();
-	}
+        /**
+         * Class constructor, specifying the MAC address for the entry.
+         * @param macAddress MAC address for the entry
+         */
+        public ArpCacheEntry(MACAddress macAddress) {
+            this.macAddress = macAddress;
+            this.timeLastSeen = System.currentTimeMillis();
+        }
 
-	synchronized void update(InetAddress ipAddress, MACAddress macAddress){
-		ArpCacheEntry arpEntry = arpCache.get(ipAddress);
-		
-		if (arpEntry != null && arpEntry.getMacAddress().equals(macAddress)){
-			arpEntry.setTimeLastSeen(System.currentTimeMillis());
-		}
-		else {
-			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;
-	}
+        /**
+         * Returns the MAC address this entry represents.
+         * @return this entry's MAC address
+         */
+        public MACAddress getMacAddress() {
+            return macAddress;
+        }
+
+        /**
+         * Update the timestamp for this entry.
+         * @param time the new timestamp to update the entry with
+         */
+        public void setTimeLastSeen(long time) {
+            timeLastSeen = time;
+        }
+
+        /**
+         * Returns whether the entry has timed out or not.
+         * @return true if the entry has timed out.
+         */
+        public boolean isExpired() {
+            return System.currentTimeMillis() - timeLastSeen > ARP_ENTRY_TIMEOUT;
+        }
+    }
+
+    /**
+     * Class constructor.
+     */
+    ArpCache() {
+        arpCache = new HashMap<InetAddress, ArpCacheEntry>();
+    }
+
+    /**
+     * Get the MAC address that is mapped to an IP address in the ARP cache.
+     * @param ipAddress the IP address to look up
+     * @return the MAC address if found in the cache, null if not
+     */
+    synchronized MACAddress lookup(InetAddress ipAddress) {
+        ArpCacheEntry arpEntry = arpCache.get(ipAddress);
+
+        if (arpEntry == null) {
+            return null;
+        }
+
+        if (arpEntry.isExpired()) {
+            // Entry has timed out so we'll remove it and return null
+            log.trace("Removing expired ARP entry for {}",
+                    ipAddress.getHostAddress());
+
+            arpCache.remove(ipAddress);
+            return null;
+        }
+
+        return arpEntry.getMacAddress();
+    }
+
+    /**
+     * Update an entry in the ARP cache. If the IP to MAC mapping is already
+     * in the cache, its timestamp will be updated. If not, the entry will
+     * be added with a new timestamp of the current time.
+     * @param ipAddress the IP address that will be mapped in the cache
+     * @param macAddress the MAC address that maps to {@code ipAddress}
+     */
+    synchronized void update(InetAddress ipAddress, MACAddress macAddress) {
+        ArpCacheEntry arpEntry = arpCache.get(ipAddress);
+
+        if (arpEntry != null && arpEntry.getMacAddress().equals(macAddress)) {
+            arpEntry.setTimeLastSeen(System.currentTimeMillis());
+        } else {
+            arpCache.put(ipAddress, new ArpCacheEntry(macAddress));
+        }
+    }
+
+    /**
+     * Retrieve a list of all mappings in the ARP cache.
+     * @return list of all ARP mappings, formatted as a human-readable string
+     *
+     */
+    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
index 252e66e..242543c 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ArpCacheResource.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ArpCacheResource.java
@@ -5,14 +5,22 @@
 import org.restlet.resource.Get;
 import org.restlet.resource.ServerResource;
 
+/**
+ * REST resource to view the IP to MAC mappings in the ARP cache.
+ *
+ */
 public class ArpCacheResource extends ServerResource {
 
-	@Get("json")
-	public List<String> getArpCache() {
-		IProxyArpService arp = (IProxyArpService) getContext().getAttributes().
-				get(IProxyArpService.class.getCanonicalName());
-		
-		return arp.getMappings();
-	}
+    /**
+     * Handler for a REST call to retrieve the ARP cache.
+     * @return list of mappings formatted as a human-readable string.
+     */
+    @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/ArpReplyNotification.java b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ArpReplyNotification.java
index a8afc55..48d04e0 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ArpReplyNotification.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ArpReplyNotification.java
@@ -5,24 +5,42 @@
 
 import net.floodlightcontroller.util.MACAddress;
 
+/**
+ * Inter-instance notification that an ARP reply has been received. The
+ * notification contains both the IP address and the MAC address.
+ */
 public class ArpReplyNotification implements Serializable {
 
-	private static final long serialVersionUID = 1L;
-	
-	private InetAddress targetAddress;
-	private MACAddress targetMacAddress;
-	
-	public ArpReplyNotification(InetAddress targetAddress, MACAddress targetMacAddress) {
-		this.targetAddress = targetAddress;
-		this.targetMacAddress = targetMacAddress;
-	}
+    private static final long serialVersionUID = 1L;
 
-	public InetAddress getTargetAddress() {
-		return targetAddress;
-	}
+    private InetAddress targetAddress;
+    private MACAddress targetMacAddress;
 
-	public MACAddress getTargetMacAddress() {
-		return targetMacAddress;
-	}
+    /**
+     * Class constructor.
+     * @param targetAddress IP address received from the ARP reply
+     * @param targetMacAddress MAC address received from the ARP reply
+     */
+    public ArpReplyNotification(InetAddress targetAddress,
+            MACAddress targetMacAddress) {
+        this.targetAddress = targetAddress;
+        this.targetMacAddress = targetMacAddress;
+    }
+
+    /**
+     * Returns the IP address of the ARP reply.
+     * @return the IP address
+     */
+    public InetAddress getTargetAddress() {
+        return targetAddress;
+    }
+
+    /**
+     * Returns the MAC address of the ARP reply.
+     * @return the MAC address
+     */
+    public MACAddress getTargetMacAddress() {
+        return targetMacAddress;
+    }
 
 }
diff --git a/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ArpWebRoutable.java b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ArpWebRoutable.java
index eefa2db..606fb29 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ArpWebRoutable.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ArpWebRoutable.java
@@ -6,17 +6,31 @@
 import org.restlet.Restlet;
 import org.restlet.routing.Router;
 
+/**
+ * Routing class for ARP module REST URLs.
+ */
 public class ArpWebRoutable implements RestletRoutable {
 
-	@Override
-	public Restlet getRestlet(Context context) {
-		Router router = new Router(context);
-		router.attach("/cache/json", ArpCacheResource.class);
-		return router;
-	}
+    /**
+     * Get a router configured with ARP module REST URLs.
+     *
+     * @param context the restlet context to build a router with
+     * @return the router
+     */
+    @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";
-	}
+    /**
+     * Get the base path of the ARP module URLs.
+     *
+     * @return the string base path
+     */
+    @Override
+    public String basePath() {
+        return "/wm/arp";
+    }
 }
diff --git a/src/main/java/net/onrc/onos/ofcontroller/proxyarp/BroadcastPacketOutNotification.java b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/BroadcastPacketOutNotification.java
index 135c061..22fe965 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/proxyarp/BroadcastPacketOutNotification.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/BroadcastPacketOutNotification.java
@@ -2,7 +2,9 @@
 
 import java.net.InetAddress;
 
-//TODO This class is too generic to be handled by ProxyArpService.
+// TODO This class is too generic to be handled by ProxyArpService.
+// TODO The generic broadcast packet shouldn't contain an IP address which is
+// only for ARP packets.
 /**
  * Notification to all ONOS instances to broadcast this packet out the edge of
  * the network. The edge is defined as any port that doesn't have a link to
@@ -10,33 +12,60 @@
  * on.
  *
  */
-public class BroadcastPacketOutNotification extends
-		PacketOutNotification {
-	
-	private static final long serialVersionUID = 1L;
-	
-	private final InetAddress address;
-	private final long inSwitch;
-	private final short inPort;
+public class BroadcastPacketOutNotification extends PacketOutNotification {
 
-	public BroadcastPacketOutNotification(byte[] packet, InetAddress address,
-			long inSwitch, short inPort) {
-		super(packet);
-		
-		this.address = address;
-		this.inSwitch = inSwitch;
-		this.inPort = inPort;
-	}
+    private static final long serialVersionUID = 1L;
 
-	public long getInSwitch() {
-		return inSwitch;
-	}
+    private final InetAddress address;
+    private final long inSwitch;
+    private final short inPort;
 
-	public short getInPort() {
-		return inPort;
-	}
+    /**
+     * Class constructor.
+     *
+     * @param packet
+     *        packet data to send in the packet-out
+     * @param address
+     *        target IP address if the packet is an ARP packet
+     * @param inSwitch
+     *        dpid of the switch the packet was received on
+     * @param inPort
+     *        port number of the receiving port
+     */
+    public BroadcastPacketOutNotification(byte[] packet, InetAddress address,
+            long inSwitch, short inPort) {
+        super(packet);
 
-	public InetAddress getTargetAddress() {
-		return address;
-	}
+        this.address = address;
+        this.inSwitch = inSwitch;
+        this.inPort = inPort;
+    }
+
+    /**
+     * Get the dpid of the switch the packet was received on.
+     *
+     * @return receiving switch dpid
+     */
+    public long getInSwitch() {
+        return inSwitch;
+    }
+
+    /**
+     * Get the port number of the port the packet was received on.
+     *
+     * @return receiving port number
+     */
+    public short getInPort() {
+        return inPort;
+    }
+
+    /**
+     * Get the target IP address if the packet is an ARP packet.
+     *
+     * @return the target IP address for ARP packets, or null if the packet is
+     *         not an ARP packet
+     */
+    public InetAddress getTargetAddress() {
+        return address;
+    }
 }
diff --git a/src/main/java/net/onrc/onos/ofcontroller/proxyarp/IArpReplyEventHandler.java b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/IArpReplyEventHandler.java
index 75f1d5d..98fcf0c 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/proxyarp/IArpReplyEventHandler.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/IArpReplyEventHandler.java
@@ -1,5 +1,12 @@
 package net.onrc.onos.ofcontroller.proxyarp;
 
+/**
+ * Listener interface for ARP reply event callbacks.
+ */
 public interface IArpReplyEventHandler {
-	public void arpReplyEvent(ArpReplyNotification arpReply);
+    /**
+     * An ARP reply has been received.
+     * @param arpReply data about the received ARP reply
+     */
+    public void arpReplyEvent(ArpReplyNotification arpReply);
 }
diff --git a/src/main/java/net/onrc/onos/ofcontroller/proxyarp/IArpRequester.java b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/IArpRequester.java
index 66a17a2..51a6295 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/proxyarp/IArpRequester.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/IArpRequester.java
@@ -5,17 +5,20 @@
 import net.floodlightcontroller.util.MACAddress;
 
 /**
- * Callback interface for modules using the {@link IProxyArpService} to
- * send ARP requests.
+ * Callback interface for modules using the {@link IProxyArpService} to send ARP
+ * requests.
  *
  */
 public interface IArpRequester {
-	/**
-	 * Callback method that will be called by the {@link IProxyArpService} 
-	 * when it receives a reply for a request previously submitted by this
-	 * {@code IArpRequester}.
-	 * @param ipAddress The IP address than an ARP request was sent for
-	 * @param macAddress The MAC address mapped to the requested IP address
-	 */
-	public void arpResponse(InetAddress ipAddress, MACAddress macAddress);
+    /**
+     * Callback method that will be called by the {@link IProxyArpService} when
+     * it receives a reply for a request previously submitted by this
+     * {@code IArpRequester}.
+     *
+     * @param ipAddress
+     *        The IP address than an ARP request was sent for
+     * @param macAddress
+     *        The MAC address mapped to the requested IP address
+     */
+    public void arpResponse(InetAddress ipAddress, MACAddress macAddress);
 }
diff --git a/src/main/java/net/onrc/onos/ofcontroller/proxyarp/IPacketOutEventHandler.java b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/IPacketOutEventHandler.java
index 86b3728..ce98703 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/proxyarp/IPacketOutEventHandler.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/IPacketOutEventHandler.java
@@ -1,18 +1,20 @@
 package net.onrc.onos.ofcontroller.proxyarp;
 
 /**
- * Classes may implement this interface if they wish to subscribe to 
- * packet out notifications from the datagrid service. Packet out notifications
- * are used to direct other ONOS instances to send packets out particular
- * ports under their control.
+ * Classes may implement this interface if they wish to subscribe to packet out
+ * notifications from the datagrid service. Packet out notifications are used to
+ * direct other ONOS instances to send packets out particular ports under their
+ * control.
  *
  */
 public interface IPacketOutEventHandler {
 
-	/**
-	 * Notify the packet out event handler that an packet out notification has
-	 * been received.
-	 * @param packetOutNotification An object describing the notification
-	 */
-	public void packetOutNotification(PacketOutNotification packetOutNotification);
+    /**
+     * Notify the packet out event handler that an packet out notification has
+     * been received.
+     *
+     * @param packetOutNotification An object describing the notification
+     */
+    public void packetOutNotification(
+            PacketOutNotification packetOutNotification);
 }
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 2029513..57993e5 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/proxyarp/IProxyArpService.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/IProxyArpService.java
@@ -6,29 +6,37 @@
 import net.floodlightcontroller.core.module.IFloodlightService;
 import net.floodlightcontroller.util.MACAddress;
 
-//Extends IFloodlightService so we can access it from REST API resources
+// Extends IFloodlightService so we can access it from REST API resources
+/**
+ * Provides ARP services to other modules.
+ */
 public interface IProxyArpService extends IFloodlightService {
-	/**
-	 * Returns the MAC address if there is a valid entry in the cache.
-	 * Otherwise returns null.
-	 * @param ipAddress
-	 * @return
-	 */
-	public MACAddress getMacAddress(InetAddress ipAddress);
-	
-	/**
-	 * Tell the IProxyArpService to send an ARP request for the IP address.
-	 * The request will be broadcast out all edge ports in the network.
-	 * @param ipAddress
-	 * @param requester
-	 * @param retry Whether to keep sending requests until the MAC is learnt
-	 */
-	public void sendArpRequest(InetAddress ipAddress, IArpRequester requester,
-			boolean retry);
-	
-	/**
-	 * Returns a snapshot of the entire ARP cache.
-	 * @return
-	 */
-	public List<String> getMappings();
+    /**
+     * Returns the MAC address if there is a valid entry in the cache. Otherwise
+     * returns null.
+     *
+     * @param ipAddress the IP address to request the ARP mapping for
+     * @return the MACAddress that maps to the specified IP address, or null if
+     *         no mapping is found
+     */
+    public MACAddress getMacAddress(InetAddress ipAddress);
+
+    /**
+     * Tell the IProxyArpService to send an ARP request for the IP address. The
+     * request will be broadcast out all edge ports in the network.
+     *
+     * @param ipAddress the IP address to send an ARP request for
+     * @param requester the {@link IArpRequester} object that will be called if
+     *                  a reply is received
+     * @param retry whether to keep sending requests until the MAC is learnt
+     */
+    public void sendArpRequest(InetAddress ipAddress, IArpRequester requester,
+            boolean retry);
+
+    /**
+     * Returns a snapshot of the entire ARP cache.
+     *
+     * @return a list of mappings formatted as a human-readable string
+     */
+    public List<String> getMappings();
 }
diff --git a/src/main/java/net/onrc/onos/ofcontroller/proxyarp/PacketOutNotification.java b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/PacketOutNotification.java
index 3d37d25..a201939 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/proxyarp/PacketOutNotification.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/PacketOutNotification.java
@@ -4,18 +4,21 @@
 
 /**
  * A PacketOutNotification contains data sent between ONOS instances that
- * directs other instances to send a packet out a set of ports.
- * This is an abstract base class that will be subclassed by specific
- * types of notifications.
- *
+ * directs other instances to send a packet out a set of ports. This is an
+ * abstract base class that will be subclassed by specific types of
+ * notifications.
  */
 public abstract class PacketOutNotification implements Serializable {
 
-	private static final long serialVersionUID = 1L;
-	
-	protected final byte[] packet;
+    private static final long serialVersionUID = 1L;
 
-	public PacketOutNotification(byte[] packet) {
-		this.packet = packet;
-	}
+    protected final byte[] packet;
+
+    /**
+     * Class constructor.
+     * @param packet the packet data to send in the packet-out
+     */
+    public PacketOutNotification(byte[] packet) {
+        this.packet = packet;
+    }
 }
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 2294a60..33d4d7d 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ProxyArpManager.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/ProxyArpManager.java
@@ -46,780 +46,798 @@
 import com.google.common.collect.SetMultimap;
 
 public class ProxyArpManager implements IProxyArpService, IOFMessageListener,
-										IPacketOutEventHandler, IArpReplyEventHandler, 
-										IFloodlightModule {
-	private final static Logger log = LoggerFactory.getLogger(ProxyArpManager.class);
-	
-	private final long ARP_TIMER_PERIOD = 100; //ms  
+        IPacketOutEventHandler, IArpReplyEventHandler, IFloodlightModule {
+    private static final Logger log = LoggerFactory
+            .getLogger(ProxyArpManager.class);
 
-	private static final int ARP_REQUEST_TIMEOUT = 2000; //ms
-			
-	private IFloodlightProviderService floodlightProvider;
-	private IDatagridService datagrid;
-	private IConfigInfoService configService;
-	private IRestApiService restApi;
-	private IFlowPusherService flowPusher;
-	
-	private short vlan;
-	private static final short NO_VLAN = 0;
-	
-	//private ArpCache arpCache;
+    private static final long ARP_TIMER_PERIOD = 100; // ms
 
-	private SetMultimap<InetAddress, ArpRequest> arpRequests;
-	
-	private static class ArpRequest {
-		private final IArpRequester requester;
-		private final boolean retry;
-		private boolean sent = false;
-		private long requestTime;
-		
-		public ArpRequest(IArpRequester requester, boolean retry){
-			this.requester = requester;
-			this.retry = retry;
-		}
-		
-		public ArpRequest(ArpRequest old) {
-			this.requester = old.requester;
-			this.retry = old.retry;
-		}
-		
-		public boolean isExpired() {
-			return sent && ((System.currentTimeMillis() - requestTime) > ARP_REQUEST_TIMEOUT);
-		}
-		
-		public boolean shouldRetry() {
-			return retry;
-		}
-		
-		public void dispatchReply(InetAddress ipAddress, MACAddress replyMacAddress) {
-			requester.arpResponse(ipAddress, replyMacAddress);
-		}
-		
-		public void setRequestTime() {
-			this.requestTime = System.currentTimeMillis();
-			this.sent = true;
-		}
-	}
-	
-	private class HostArpRequester implements IArpRequester {
-		private final ARP arpRequest;
-		private final long dpid;
-		private final short port;
-		
-		public HostArpRequester(ARP arpRequest, long dpid, short port) {
-			this.arpRequest = arpRequest;
-			this.dpid = dpid;
-			this.port = port;
-		}
+    private static final int ARP_REQUEST_TIMEOUT = 2000; // ms
 
-		@Override
-		public void arpResponse(InetAddress ipAddress, MACAddress macAddress) {
-			ProxyArpManager.this.sendArpReply(arpRequest, dpid, port, macAddress);
-		}
-	}
-	
-	@Override
-	public Collection<Class<? extends IFloodlightService>> getModuleServices() {
-		Collection<Class<? extends IFloodlightService>> l 
-			= new ArrayList<Class<? extends IFloodlightService>>();
-		l.add(IProxyArpService.class);
-		return l;
-	}
+    private IFloodlightProviderService floodlightProvider;
+    private IDatagridService datagrid;
+    private IConfigInfoService configService;
+    private IRestApiService restApi;
+    private IFlowPusherService flowPusher;
 
-	@Override
-	public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
-		Map<Class<? extends IFloodlightService>, IFloodlightService> m 
-			= new HashMap<Class<? extends IFloodlightService>, IFloodlightService>();
-		m.put(IProxyArpService.class, this);
-		return m;
-	}
+    private short vlan;
+    private static final short NO_VLAN = 0;
 
-	@Override
-	public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
-		Collection<Class<? extends IFloodlightService>> dependencies 
-			= new ArrayList<Class<? extends IFloodlightService>>();
-		dependencies.add(IFloodlightProviderService.class);
-		dependencies.add(IRestApiService.class);
-		dependencies.add(IDatagridService.class);
-		dependencies.add(IConfigInfoService.class);
-		dependencies.add(IFlowPusherService.class);
-		return dependencies;
-	}
-	
-	@Override
-	public void init(FloodlightModuleContext context){
-		this.floodlightProvider = 
-				context.getServiceImpl(IFloodlightProviderService.class);
-		this.datagrid = context.getServiceImpl(IDatagridService.class);
-		this.configService = context.getServiceImpl(IConfigInfoService.class);
-		this.restApi = context.getServiceImpl(IRestApiService.class);
-		this.flowPusher = context.getServiceImpl(IFlowPusherService.class);
-		
-		//arpCache = new ArpCache();
+    private SetMultimap<InetAddress, ArpRequest> arpRequests;
 
-		arpRequests = Multimaps.synchronizedSetMultimap(
-				HashMultimap.<InetAddress, ArpRequest>create());
-		
-	}
-	
-	@Override
-	public void startUp(FloodlightModuleContext context) {
-		this.vlan = configService.getVlan();
-		log.info("vlan set to {}", this.vlan);
-		
-		restApi.addRestletRoutable(new ArpWebRoutable());
-		floodlightProvider.addOFMessageListener(OFType.PACKET_IN, this);
-		
-		datagrid.registerPacketOutEventHandler(this);
-		datagrid.registerArpReplyEventHandler(this);
-		
-		Timer arpTimer = new Timer("arp-processing");
-		arpTimer.scheduleAtFixedRate(new TimerTask() {
-			@Override
-			public void run() {
-				doPeriodicArpProcessing();
-			}
-		}, 0, ARP_TIMER_PERIOD);
-	}
-	
-	/*
-	 * Function that runs periodically to manage the asynchronous request mechanism.
-	 * It basically cleans up old ARP requests if we don't get a response for them.
-	 * The caller can designate that a request should be retried indefinitely, and
-	 * this task will handle that as well.
-	 */
-	private void doPeriodicArpProcessing() {
-		SetMultimap<InetAddress, ArpRequest> retryList 
-				= HashMultimap.<InetAddress, ArpRequest>create();
+    private static class ArpRequest {
+        private final IArpRequester requester;
+        private final boolean retry;
+        private boolean sent = false;
+        private long requestTime;
 
-		//Have to synchronize externally on the Multimap while using an iterator,
-		//even though it's a synchronizedMultimap
-		synchronized (arpRequests) {			
-			Iterator<Map.Entry<InetAddress, ArpRequest>> it 
-				= arpRequests.entries().iterator();
-			
-			while (it.hasNext()) {
-				Map.Entry<InetAddress, ArpRequest> entry
-						= it.next();
-				ArpRequest request = entry.getValue();
-				if (request.isExpired()) {
-					log.debug("Cleaning expired ARP request for {}", 
-							entry.getKey().getHostAddress());
-		
-					// If the ARP request is expired and then delete the device
-					// TODO check whether this is OK from this thread
-					// TODO: Fix the code below after deviceStorage was removed
-					/*
-					IDeviceObject targetDevice = 
-							deviceStorage.getDeviceByIP(InetAddresses.coerceToInteger(entry.getKey()));
-					if (targetDevice != null) {
-						deviceStorage.removeDevice(targetDevice);
-						if (log.isDebugEnabled()) {
-							log.debug("RemoveDevice: {} due to no have not recieve the ARP reply", targetDevice);
-						}
-					}
-					*/
-					
-					it.remove();
-					
-					if (request.shouldRetry()) {
-						retryList.put(entry.getKey(), request);
-					}
-				}
-			}
-		}
-		
-		for (Map.Entry<InetAddress, Collection<ArpRequest>> entry 
-				: retryList.asMap().entrySet()) {
-			
-			InetAddress address = entry.getKey();
-			
-			log.debug("Resending ARP request for {}", address.getHostAddress());
-			
-			// Only ARP requests sent by the controller will have the retry flag
-			// set, so for now we can just send a new ARP request for that address.
-			sendArpRequestForAddress(address);
-			
-			for (ArpRequest request : entry.getValue()) {
-				arpRequests.put(address, new ArpRequest(request));
-			}
-		}
-	}
-	
-	@Override
-	public String getName() {
-		return "proxyarpmanager";
-	}
+        public ArpRequest(IArpRequester requester, boolean retry) {
+            this.requester = requester;
+            this.retry = retry;
+        }
 
-	@Override
-	public boolean isCallbackOrderingPrereq(OFType type, String name) {
-		if (type == OFType.PACKET_IN) {
-			return "devicemanager".equals(name) || "onosdevicemanager".equals(name);
-		}
-		else {
-			return false;
-		}
-	}
+        public ArpRequest(ArpRequest old) {
+            this.requester = old.requester;
+            this.retry = old.retry;
+        }
 
-	@Override
-	public boolean isCallbackOrderingPostreq(OFType type, String name) {
-		return type == OFType.PACKET_IN && "onosforwarding".equals(name);
-	}
+        public boolean isExpired() {
+            return sent
+                    && ((System.currentTimeMillis() - requestTime) > ARP_REQUEST_TIMEOUT);
+        }
 
-	@Override
-	public Command receive(
-			IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
-		
-		OFPacketIn pi = (OFPacketIn) msg;
-		
-		Ethernet eth = IFloodlightProviderService.bcStore.get(cntx, 
+        public boolean shouldRetry() {
+            return retry;
+        }
+
+        public void dispatchReply(InetAddress ipAddress,
+                MACAddress replyMacAddress) {
+            requester.arpResponse(ipAddress, replyMacAddress);
+        }
+
+        public void setRequestTime() {
+            this.requestTime = System.currentTimeMillis();
+            this.sent = true;
+        }
+    }
+
+    private class HostArpRequester implements IArpRequester {
+        private final ARP arpRequest;
+        private final long dpid;
+        private final short port;
+
+        public HostArpRequester(ARP arpRequest, long dpid, short port) {
+            this.arpRequest = arpRequest;
+            this.dpid = dpid;
+            this.port = port;
+        }
+
+        @Override
+        public void arpResponse(InetAddress ipAddress, MACAddress macAddress) {
+            ProxyArpManager.this.sendArpReply(arpRequest, dpid, port,
+                    macAddress);
+        }
+    }
+
+    @Override
+    public Collection<Class<? extends IFloodlightService>> getModuleServices() {
+        Collection<Class<? extends IFloodlightService>> l =
+                new ArrayList<Class<? extends IFloodlightService>>();
+        l.add(IProxyArpService.class);
+        return l;
+    }
+
+    @Override
+    public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
+        Map<Class<? extends IFloodlightService>, IFloodlightService> m =
+                new HashMap<Class<? extends IFloodlightService>, IFloodlightService>();
+        m.put(IProxyArpService.class, this);
+        return m;
+    }
+
+    @Override
+    public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
+        Collection<Class<? extends IFloodlightService>> dependencies =
+                new ArrayList<Class<? extends IFloodlightService>>();
+        dependencies.add(IFloodlightProviderService.class);
+        dependencies.add(IRestApiService.class);
+        dependencies.add(IDatagridService.class);
+        dependencies.add(IConfigInfoService.class);
+        dependencies.add(IFlowPusherService.class);
+        return dependencies;
+    }
+
+    @Override
+    public void init(FloodlightModuleContext context) {
+        this.floodlightProvider = context
+                .getServiceImpl(IFloodlightProviderService.class);
+        this.datagrid = context.getServiceImpl(IDatagridService.class);
+        this.configService = context.getServiceImpl(IConfigInfoService.class);
+        this.restApi = context.getServiceImpl(IRestApiService.class);
+        this.flowPusher = context.getServiceImpl(IFlowPusherService.class);
+
+        // arpCache = new ArpCache();
+
+        arpRequests = Multimaps.synchronizedSetMultimap(HashMultimap
+                .<InetAddress, ArpRequest>create());
+
+    }
+
+    @Override
+    public void startUp(FloodlightModuleContext context) {
+        this.vlan = configService.getVlan();
+        log.info("vlan set to {}", this.vlan);
+
+        restApi.addRestletRoutable(new ArpWebRoutable());
+        floodlightProvider.addOFMessageListener(OFType.PACKET_IN, this);
+
+        datagrid.registerPacketOutEventHandler(this);
+        datagrid.registerArpReplyEventHandler(this);
+
+        Timer arpTimer = new Timer("arp-processing");
+        arpTimer.scheduleAtFixedRate(new TimerTask() {
+            @Override
+            public void run() {
+                doPeriodicArpProcessing();
+            }
+        }, 0, ARP_TIMER_PERIOD);
+    }
+
+    /*
+     * Function that runs periodically to manage the asynchronous request mechanism.
+     * It basically cleans up old ARP requests if we don't get a response for them.
+     * The caller can designate that a request should be retried indefinitely, and
+     * this task will handle that as well.
+     */
+    private void doPeriodicArpProcessing() {
+        SetMultimap<InetAddress, ArpRequest> retryList = HashMultimap
+                .<InetAddress, ArpRequest>create();
+
+        // Have to synchronize externally on the Multimap while using an
+        // iterator,
+        // even though it's a synchronizedMultimap
+        synchronized (arpRequests) {
+            Iterator<Map.Entry<InetAddress, ArpRequest>> it = arpRequests
+                    .entries().iterator();
+
+            while (it.hasNext()) {
+                Map.Entry<InetAddress, ArpRequest> entry = it.next();
+                ArpRequest request = entry.getValue();
+                if (request.isExpired()) {
+                    log.debug("Cleaning expired ARP request for {}", entry
+                            .getKey().getHostAddress());
+
+                    // If the ARP request is expired and then delete the device
+                    // TODO check whether this is OK from this thread
+                    // TODO: Fix the code below after deviceStorage was removed
+                    /*
+                    IDeviceObject targetDevice =
+                    		deviceStorage.getDeviceByIP(InetAddresses.coerceToInteger(entry.getKey()));
+                    if (targetDevice != null) {
+                    	deviceStorage.removeDevice(targetDevice);
+                    	if (log.isDebugEnabled()) {
+                    		log.debug("RemoveDevice: {} due to no have not recieve the ARP reply", targetDevice);
+                    	}
+                    }
+                    */
+
+                    it.remove();
+
+                    if (request.shouldRetry()) {
+                        retryList.put(entry.getKey(), request);
+                    }
+                }
+            }
+        }
+
+        for (Map.Entry<InetAddress, Collection<ArpRequest>> entry : retryList
+                .asMap().entrySet()) {
+
+            InetAddress address = entry.getKey();
+
+            log.debug("Resending ARP request for {}", address.getHostAddress());
+
+            // Only ARP requests sent by the controller will have the retry flag
+            // set, so for now we can just send a new ARP request for that
+            // address.
+            sendArpRequestForAddress(address);
+
+            for (ArpRequest request : entry.getValue()) {
+                arpRequests.put(address, new ArpRequest(request));
+            }
+        }
+    }
+
+    @Override
+    public String getName() {
+        return "proxyarpmanager";
+    }
+
+    @Override
+    public boolean isCallbackOrderingPrereq(OFType type, String name) {
+        if (type == OFType.PACKET_IN) {
+            return "devicemanager".equals(name)
+                    || "onosdevicemanager".equals(name);
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public boolean isCallbackOrderingPostreq(OFType type, String name) {
+        return type == OFType.PACKET_IN && "onosforwarding".equals(name);
+    }
+
+    @Override
+    public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
+
+        OFPacketIn pi = (OFPacketIn) msg;
+
+        Ethernet eth = IFloodlightProviderService.bcStore.get(cntx,
                 IFloodlightProviderService.CONTEXT_PI_PAYLOAD);
-		
-		if (eth.getEtherType() == Ethernet.TYPE_ARP){
-			ARP arp = (ARP) eth.getPayload();	
-			if (arp.getOpCode() == ARP.OP_REQUEST) {
-				handleArpRequest(sw, pi, arp, eth);
-			}
-			else if (arp.getOpCode() == ARP.OP_REPLY) {
-				// For replies we simply send a notification via Hazelcast
-				sendArpReplyNotification(eth, pi);
-				
-				//handleArpReply(sw, pi, arp);
-			}
-			
-			// Stop ARP packets here
-			return Command.STOP;
-		}
-		
-		// Propagate everything else
-		return Command.CONTINUE;
-	}
-	
-	private void handleArpRequest(IOFSwitch sw, OFPacketIn pi, ARP arp, Ethernet eth) {
-		if (log.isTraceEnabled()) {
-			log.trace("ARP request received for {}", 
-					inetAddressToString(arp.getTargetProtocolAddress()));
-		}
 
-		InetAddress target;
-		try {
-			target = InetAddress.getByAddress(arp.getTargetProtocolAddress());
-		} catch (UnknownHostException e) {
-			log.debug("Invalid address in ARP request", e);
-			return;
-		}
+        if (eth.getEtherType() == Ethernet.TYPE_ARP) {
+            ARP arp = (ARP) eth.getPayload();
+            if (arp.getOpCode() == ARP.OP_REQUEST) {
+                handleArpRequest(sw, pi, arp, eth);
+            } else if (arp.getOpCode() == ARP.OP_REPLY) {
+                // For replies we simply send a notification via Hazelcast
+                sendArpReplyNotification(eth, pi);
 
-		if (configService.fromExternalNetwork(sw.getId(), pi.getInPort())) {
-			//If the request came from outside our network, we only care if
-			//it was a request for one of our interfaces.
-			if (configService.isInterfaceAddress(target)) {
-				log.trace("ARP request for our interface. Sending reply {} => {}",
-						target.getHostAddress(), configService.getRouterMacAddress());
+                // handleArpReply(sw, pi, arp);
+            }
 
-				sendArpReply(arp, sw.getId(), pi.getInPort(), 
-						configService.getRouterMacAddress());
-			}
+            // Stop ARP packets here
+            return Command.STOP;
+        }
 
-			return;
-		}
-		
-		//MACAddress macAddress = arpCache.lookup(target);
+        // Propagate everything else
+        return Command.CONTINUE;
+    }
 
-		arpRequests.put(target, new ArpRequest(
-				new HostArpRequester(arp, sw.getId(), pi.getInPort()), false));
+    private void handleArpRequest(IOFSwitch sw, OFPacketIn pi, ARP arp,
+            Ethernet eth) {
+        if (log.isTraceEnabled()) {
+            log.trace("ARP request received for {}",
+                    inetAddressToString(arp.getTargetProtocolAddress()));
+        }
 
-		// TODO: Fix the code below after deviceStorage was removed
-		/*
-		IDeviceObject targetDevice = 
-				deviceStorage.getDeviceByIP(InetAddresses.coerceToInteger(target));
-		*/
+        InetAddress target;
+        try {
+            target = InetAddress.getByAddress(arp.getTargetProtocolAddress());
+        } catch (UnknownHostException e) {
+            log.debug("Invalid address in ARP request", e);
+            return;
+        }
 
-		// TODO: Fix the code below after deviceStorage was removed
-		/*
-		if (targetDevice == null) {
-			if (log.isTraceEnabled()) {
-				log.trace("No device info found for {} - broadcasting",
-						target.getHostAddress());
-			}
-			
-			// We don't know the device so broadcast the request out
-			datagrid.sendPacketOutNotification(
-					new BroadcastPacketOutNotification(eth.serialize(),
-							target, sw.getId(), pi.getInPort()));
-		}
-		else {
-			// Even if the device exists in our database, we do not reply to
-			// the request directly, but check whether the device is still valid
-			MACAddress macAddress = MACAddress.valueOf(targetDevice.getMACAddress());
+        if (configService.fromExternalNetwork(sw.getId(), pi.getInPort())) {
+            // If the request came from outside our network, we only care if
+            // it was a request for one of our interfaces.
+            if (configService.isInterfaceAddress(target)) {
+                log.trace(
+                        "ARP request for our interface. Sending reply {} => {}",
+                        target.getHostAddress(),
+                        configService.getRouterMacAddress());
 
-			if (log.isTraceEnabled()) {
-				log.trace("The target Device Record in DB is: {} => {} from ARP request host at {}/{}",
-						new Object [] {
-						inetAddressToString(arp.getTargetProtocolAddress()),
-						macAddress,
-						HexString.toHexString(sw.getId()), pi.getInPort()});
-			}
+                sendArpReply(arp, sw.getId(), pi.getInPort(),
+                        configService.getRouterMacAddress());
+            }
 
-			// sendArpReply(arp, sw.getId(), pi.getInPort(), macAddress);
+            return;
+        }
 
-			Iterable<IPortObject> outPorts = targetDevice.getAttachedPorts();	
+        // MACAddress macAddress = arpCache.lookup(target);
 
-			if (!outPorts.iterator().hasNext()){
-				if (log.isTraceEnabled()) {
-					log.trace("Device {} exists but is not connected to any ports" + 
-							" - broadcasting", macAddress);
-				}
-				
-				datagrid.sendPacketOutNotification(
-						new BroadcastPacketOutNotification(eth.serialize(), 
-								target, sw.getId(), pi.getInPort()));
-			} 
-			else {
-				for (IPortObject portObject : outPorts) {
-					//long outSwitch = 0;
-					//short outPort = 0;
+        arpRequests.put(
+                target,
+                new ArpRequest(new HostArpRequester(arp, sw.getId(), pi
+                        .getInPort()), false));
 
-					// if (!portObject.getLinkedPorts().iterator().hasNext()) {
-					//	outPort = portObject.getNumber();					
-					// }
-					if (portObject.getLinkedPorts().iterator().hasNext()) {
-						continue;
-					}
+        // TODO: Fix the code below after deviceStorage was removed
+        /*
+        IDeviceObject targetDevice =
+        		deviceStorage.getDeviceByIP(InetAddresses.coerceToInteger(target));
+        */
 
-					short outPort = portObject.getNumber();
-					ISwitchObject outSwitchObject = portObject.getSwitch();
-					long outSwitch = HexString.toLong(outSwitchObject.getDPID());
+        // TODO: Fix the code below after deviceStorage was removed
+        /*
+        if (targetDevice == null) {
+        	if (log.isTraceEnabled()) {
+        		log.trace("No device info found for {} - broadcasting",
+        				target.getHostAddress());
+        	}
 
-					if (log.isTraceEnabled()) {
-						log.trace("Probing device {} on port {}/{}", 
-								new Object[] {macAddress, 
-								HexString.toHexString(outSwitch), outPort});
-					}
-					
-					datagrid.sendPacketOutNotification(
-							new SinglePacketOutNotification(eth.serialize(), 
-									target, outSwitch, outPort));
-				}
-			}
-		}
-		*/
-	}
-	
-	private void handleArpReply(IOFSwitch sw, OFPacketIn pi, ARP arp){
-		if (log.isTraceEnabled()) {
-			log.trace("ARP reply recieved: {} => {}, on {}/{}", new Object[] { 
-					inetAddressToString(arp.getSenderProtocolAddress()),
-					HexString.toHexString(arp.getSenderHardwareAddress()),
-					HexString.toHexString(sw.getId()), pi.getInPort()});
-		}
-		
-		InetAddress senderIpAddress;
-		try {
-			senderIpAddress = InetAddress.getByAddress(arp.getSenderProtocolAddress());
-		} catch (UnknownHostException e) {
-			log.debug("Invalid address in ARP reply", e);
-			return;
-		}
-		
-		MACAddress senderMacAddress = MACAddress.valueOf(arp.getSenderHardwareAddress());
-		
-		//See if anyone's waiting for this ARP reply
-		Set<ArpRequest> requests = arpRequests.get(senderIpAddress);
-		
-		//Synchronize on the Multimap while using an iterator for one of the sets
-		List<ArpRequest> requestsToSend = new ArrayList<ArpRequest>(requests.size());
-		synchronized (arpRequests) {
-			Iterator<ArpRequest> it = requests.iterator();
-			while (it.hasNext()) {
-				ArpRequest request = it.next();
-				it.remove();
-				requestsToSend.add(request);
-			}
-		}
-		
-		//Don't hold an ARP lock while dispatching requests
-		for (ArpRequest request : requestsToSend) {
-			request.dispatchReply(senderIpAddress, senderMacAddress);
-		}
-	}
-	
-	private void sendArpRequestForAddress(InetAddress ipAddress) {
-		//TODO what should the sender IP address and MAC address be if no
-		//IP addresses are configured? Will there ever be a need to send
-		//ARP requests from the controller in that case?
-		//All-zero MAC address doesn't seem to work - hosts don't respond to it
-		
-		byte[] zeroIpv4 = {0x0, 0x0, 0x0, 0x0};
-		byte[] zeroMac = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
-		byte[] genericNonZeroMac = {0x0, 0x0, 0x0, 0x0, 0x0, 0x01};
-		byte[] broadcastMac = {(byte)0xff, (byte)0xff, (byte)0xff, 
-				(byte)0xff, (byte)0xff, (byte)0xff};
-		
-		ARP arpRequest = new ARP();
-		
-		arpRequest.setHardwareType(ARP.HW_TYPE_ETHERNET)
-			.setProtocolType(ARP.PROTO_TYPE_IP)
-			.setHardwareAddressLength((byte)Ethernet.DATALAYER_ADDRESS_LENGTH)
-			.setProtocolAddressLength((byte)IPv4.ADDRESS_LENGTH)
-			.setOpCode(ARP.OP_REQUEST)
-			.setTargetHardwareAddress(zeroMac)
-			.setTargetProtocolAddress(ipAddress.getAddress());
+        	// We don't know the device so broadcast the request out
+        	datagrid.sendPacketOutNotification(
+        			new BroadcastPacketOutNotification(eth.serialize(),
+        					target, sw.getId(), pi.getInPort()));
+        }
+        else {
+        	// Even if the device exists in our database, we do not reply to
+        	// the request directly, but check whether the device is still valid
+        	MACAddress macAddress = MACAddress.valueOf(targetDevice.getMACAddress());
 
-		MACAddress routerMacAddress = configService.getRouterMacAddress();
-		//TODO hack for now as it's unclear what the MAC address should be
-		byte[] senderMacAddress = genericNonZeroMac;
-		if (routerMacAddress != null) {
-			senderMacAddress = routerMacAddress.toBytes();
-		}
-		arpRequest.setSenderHardwareAddress(senderMacAddress);
-		
-		byte[] senderIPAddress = zeroIpv4;
-		Interface intf = configService.getOutgoingInterface(ipAddress);
-		if (intf != null) {
-			senderIPAddress = intf.getIpAddress().getAddress();
-		}
-		
-		arpRequest.setSenderProtocolAddress(senderIPAddress);
-		
-		Ethernet eth = new Ethernet();
-		eth.setSourceMACAddress(senderMacAddress)
-			.setDestinationMACAddress(broadcastMac)
-			.setEtherType(Ethernet.TYPE_ARP)
-			.setPayload(arpRequest);
-		
-		if (vlan != NO_VLAN) {
-			eth.setVlanID(vlan)
-			   .setPriorityCode((byte)0);
-		}
-		
-		//sendArpRequestToSwitches(ipAddress, eth.serialize());
-		datagrid.sendPacketOutNotification(new SinglePacketOutNotification(eth.serialize(),
-				ipAddress, intf.getDpid(),intf.getPort()));
-	}
-	
-	private void sendArpRequestToSwitches(InetAddress dstAddress, byte[] arpRequest) {
-		sendArpRequestToSwitches(dstAddress, arpRequest, 
-				0, OFPort.OFPP_NONE.getValue());
-	}
-	
-	private void sendArpRequestToSwitches(InetAddress dstAddress, byte[] arpRequest,
-			long inSwitch, short inPort) {
+        	if (log.isTraceEnabled()) {
+        		log.trace("The target Device Record in DB is: {} => {} from ARP request host at {}/{}",
+        				new Object [] {
+        				inetAddressToString(arp.getTargetProtocolAddress()),
+        				macAddress,
+        				HexString.toHexString(sw.getId()), pi.getInPort()});
+        	}
 
-		if (configService.hasLayer3Configuration()) {
-			Interface intf = configService.getOutgoingInterface(dstAddress);
-			if (intf != null) {
-				sendArpRequestOutPort(arpRequest, intf.getDpid(), intf.getPort());
-			}
-			else {
-				//TODO here it should be broadcast out all non-interface edge ports.
-				//I think we can assume that if it's not a request for an external 
-				//network, it's an ARP for a host in our own network. So we want to 
-				//send it out all edge ports that don't have an interface configured
-				//to ensure it reaches all hosts in our network.
-				log.debug("No interface found to send ARP request for {}",
-						dstAddress.getHostAddress());
-			}
-		}
-		else {
-			//broadcastArpRequestOutEdge(arpRequest, inSwitch, inPort);
-			broadcastArpRequestOutMyEdge(arpRequest, inSwitch, inPort);
-		}
-	}
-	
-	private void sendArpReplyNotification(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;
-		}
+        	// sendArpReply(arp, sw.getId(), pi.getInPort(), macAddress);
 
-		datagrid.sendArpReplyNotification(new ArpReplyNotification(targetAddress, mac));
-	}
-	
-	private void broadcastArpRequestOutMyEdge(byte[] arpRequest,
-			long inSwitch, short inPort) {
-		List<SwitchPort> switchPorts = new ArrayList<SwitchPort>();
-		
-		for (IOFSwitch sw : floodlightProvider.getSwitches().values()) {
-			
-			OFPacketOut po = new OFPacketOut();
-			po.setInPort(OFPort.OFPP_NONE)
-			.setBufferId(-1)
-			.setPacketData(arpRequest);
-			
-			List<OFAction> actions = new ArrayList<OFAction>();
+        	Iterable<IPortObject> outPorts = targetDevice.getAttachedPorts();
 
-			// TODO: Fix the code below after topoSwitchService was removed
-			/*
-			Iterable<IPortObject> ports 
-				= topoSwitchService.getPortsOnSwitch(sw.getStringId());
-			if (ports == null) {
-				continue;
-			}
-			
-			for (IPortObject portObject : ports) {
-				if (!portObject.getLinkedPorts().iterator().hasNext()) {
-					short portNumber = portObject.getNumber();
-					
-					if (sw.getId() == inSwitch && portNumber == inPort) {
-						// This is the port that the ARP message came in,
-						// so don't broadcast out this port
-						continue;
-					}
-					
-					switchPorts.add(new SwitchPort(new Dpid(sw.getId()), 
-							new Port(portNumber)));
-					actions.add(new OFActionOutput(portNumber));
-				}
-			}
-			*/
-			
-			po.setActions(actions);
-			short actionsLength = (short) 
-					(actions.size() * OFActionOutput.MINIMUM_LENGTH);
-			po.setActionsLength(actionsLength);
-			po.setLengthU(OFPacketOut.MINIMUM_LENGTH + actionsLength 
-					+ arpRequest.length);
-			
-			flowPusher.add(sw, po);
-		}
-		
-		if (log.isTraceEnabled()) {
-			log.trace("Broadcast ARP request to: {}", switchPorts);
-		}
-	}
-	
-	private void sendArpRequestOutPort(byte[] arpRequest, long dpid, short port) {
-		if (log.isTraceEnabled()) {
-			log.trace("Sending ARP request out {}/{}", 
-					HexString.toHexString(dpid), port);
-		}
-		
-		OFPacketOut po = new OFPacketOut();
-		po.setInPort(OFPort.OFPP_NONE)
-			.setBufferId(-1)
-			.setPacketData(arpRequest);
-			
-		List<OFAction> actions = new ArrayList<OFAction>();
-		actions.add(new OFActionOutput(port));
-		po.setActions(actions);
-		short actionsLength = (short) (actions.size() * OFActionOutput.MINIMUM_LENGTH);
-		po.setActionsLength(actionsLength);
-		po.setLengthU(OFPacketOut.MINIMUM_LENGTH + actionsLength 
-				+ arpRequest.length);
-		
-		IOFSwitch sw = floodlightProvider.getSwitches().get(dpid);
-		
-		if (sw == null) {
-			log.warn("Switch not found when sending ARP request");
-			return;
-		}
-		
-		flowPusher.add(sw, po);
-	}
-	
-	private void sendArpReply(ARP arpRequest, long dpid, short port, MACAddress targetMac) {
-		if (log.isTraceEnabled()) {
-			log.trace("Sending reply {} => {} to {}", new Object[] {
-					inetAddressToString(arpRequest.getTargetProtocolAddress()),
-					targetMac,
-					inetAddressToString(arpRequest.getSenderProtocolAddress())});
-		}
-		
-		ARP arpReply = new ARP();
-		arpReply.setHardwareType(ARP.HW_TYPE_ETHERNET)
-			.setProtocolType(ARP.PROTO_TYPE_IP)
-			.setHardwareAddressLength((byte)Ethernet.DATALAYER_ADDRESS_LENGTH)
-			.setProtocolAddressLength((byte)IPv4.ADDRESS_LENGTH)
-			.setOpCode(ARP.OP_REPLY)
-			.setSenderHardwareAddress(targetMac.toBytes())
-			.setSenderProtocolAddress(arpRequest.getTargetProtocolAddress())
-			.setTargetHardwareAddress(arpRequest.getSenderHardwareAddress())
-			.setTargetProtocolAddress(arpRequest.getSenderProtocolAddress());
-		
+        	if (!outPorts.iterator().hasNext()){
+        		if (log.isTraceEnabled()) {
+        			log.trace("Device {} exists but is not connected to any ports" +
+        					" - broadcasting", macAddress);
+        		}
 
-		Ethernet eth = new Ethernet();
-		eth.setDestinationMACAddress(arpRequest.getSenderHardwareAddress())
-			.setSourceMACAddress(targetMac.toBytes())
-			.setEtherType(Ethernet.TYPE_ARP)
-			.setPayload(arpReply);
-		
-		if (vlan != NO_VLAN) {
-			eth.setVlanID(vlan)
-			   .setPriorityCode((byte)0);
-		}
-		
-		List<OFAction> actions = new ArrayList<OFAction>();
-		actions.add(new OFActionOutput(port));
-		
-		OFPacketOut po = new OFPacketOut();
-		po.setInPort(OFPort.OFPP_NONE)
-			.setBufferId(-1)
-			.setPacketData(eth.serialize())
-			.setActions(actions)
-			.setActionsLength((short)OFActionOutput.MINIMUM_LENGTH)
-			.setLengthU(OFPacketOut.MINIMUM_LENGTH + OFActionOutput.MINIMUM_LENGTH
-					+ po.getPacketData().length);
-		
-		List<OFMessage> msgList = new ArrayList<OFMessage>();
-		msgList.add(po);
+        		datagrid.sendPacketOutNotification(
+        				new BroadcastPacketOutNotification(eth.serialize(),
+        						target, sw.getId(), pi.getInPort()));
+        	}
+        	else {
+        		for (IPortObject portObject : outPorts) {
+        			//long outSwitch = 0;
+        			//short outPort = 0;
 
-		IOFSwitch sw = floodlightProvider.getSwitches().get(dpid);
-		
-		if (sw == null) {
-			log.warn("Switch {} not found when sending ARP reply", 
-					HexString.toHexString(dpid));
-			return;
-		}
-		
-		flowPusher.add(sw, po);
-	}
-	
-	private String inetAddressToString(byte[] bytes) {
-		try {
-			return InetAddress.getByAddress(bytes).getHostAddress();
-		} catch (UnknownHostException e) {
-			log.debug("Invalid IP address", e);
-			return "";
-		}
-	}
-	
-	/*
-	 * IProxyArpService methods
-	 */
+        			// if (!portObject.getLinkedPorts().iterator().hasNext()) {
+        			//	outPort = portObject.getNumber();
+        			// }
+        			if (portObject.getLinkedPorts().iterator().hasNext()) {
+        				continue;
+        			}
 
-	@Override
-	public MACAddress getMacAddress(InetAddress ipAddress) {
-		//return arpCache.lookup(ipAddress);
-		return null;
-	}
+        			short outPort = portObject.getNumber();
+        			ISwitchObject outSwitchObject = portObject.getSwitch();
+        			long outSwitch = HexString.toLong(outSwitchObject.getDPID());
 
-	@Override
-	public void sendArpRequest(InetAddress ipAddress, IArpRequester requester,
-			boolean retry) {
-		arpRequests.put(ipAddress, new ArpRequest(requester, retry));
-		
-		//Sanity check to make sure we don't send a request for our own address
-		if (!configService.isInterfaceAddress(ipAddress)) {
-			sendArpRequestForAddress(ipAddress);
-		}
-	}
-	
-	@Override
-	public List<String> getMappings() {
-		//return arpCache.getMappings();
-		return new ArrayList<String>();
-	}
+        			if (log.isTraceEnabled()) {
+        				log.trace("Probing device {} on port {}/{}",
+        						new Object[] {macAddress,
+        						HexString.toHexString(outSwitch), outPort});
+        			}
 
-	/*
-	@Override
-	public void arpRequestNotification(ArpMessage arpMessage) {
-		log.debug("Received ARP notification from other instances");
+        			datagrid.sendPacketOutNotification(
+        					new SinglePacketOutNotification(eth.serialize(),
+        							target, outSwitch, outPort));
+        		}
+        	}
+        }
+        */
+    }
 
-		switch (arpMessage.getType()){
-		case REQUEST:
-			if(arpMessage.getOutSwitch() == -1 || arpMessage.getOutPort() == -1){	
-				broadcastArpRequestOutMyEdge(arpMessage.getPacket(),
-						arpMessage.getInSwitch(), arpMessage.getInPort());					
-			}else{					
-				sendArpRequestOutPort(arpMessage.getPacket(),arpMessage.getOutSwitch(),arpMessage.getOutPort());
-				log.debug("OutSwitch in ARP request message is: {}; OutPort in ARP request message is: {}",arpMessage.getOutSwitch(),arpMessage.getOutPort());
-			}
-			break;
-		case REPLY:
-			log.debug("Received ARP reply notification for {}",
-					arpMessage.getAddress());
-			sendArpReplyToWaitingRequesters(arpMessage.getAddress(),arpMessage.getMAC());
-			break;
-		}
-	}
-	*/
-	
-	private void sendArpReplyToWaitingRequesters(InetAddress address, MACAddress mac) {
-		log.debug("Sending ARP reply for {} to requesters", 
-				address.getHostAddress());
-		
-		//See if anyone's waiting for this ARP reply
-		Set<ArpRequest> requests = arpRequests.get(address);
-		
-		//Synchronize on the Multimap while using an iterator for one of the sets
-		List<ArpRequest> requestsToSend = new ArrayList<ArpRequest>(requests.size());
-		synchronized (arpRequests) {
-			Iterator<ArpRequest> it = requests.iterator();
-			while (it.hasNext()) {
-				ArpRequest request = it.next();
-				it.remove();
-				requestsToSend.add(request);
-			}
-		}
-		
-		/*IDeviceObject deviceObject = deviceStorage.getDeviceByIP(
-				InetAddresses.coerceToInteger(address));
-		
-		MACAddress mac = MACAddress.valueOf(deviceObject.getMACAddress());
-		
-		log.debug("Found {} at {} in network map", 
-				address.getHostAddress(), mac);*/
-		
-		//Don't hold an ARP lock while dispatching requests
-		for (ArpRequest request : requestsToSend) {
-			request.dispatchReply(address, mac);
-		}
-	}
+    // Not used because device manager currently updates the database
+    // for ARP replies. May be useful in the future.
+    private void handleArpReply(IOFSwitch sw, OFPacketIn pi, ARP arp) {
+        if (log.isTraceEnabled()) {
+            log.trace("ARP reply recieved: {} => {}, on {}/{}", new Object[] {
+                    inetAddressToString(arp.getSenderProtocolAddress()),
+                    HexString.toHexString(arp.getSenderHardwareAddress()),
+                    HexString.toHexString(sw.getId()), pi.getInPort()});
+        }
 
-	@Override
-	public void arpReplyEvent(ArpReplyNotification arpReply) {
-		log.debug("Received ARP reply notification for {}",
-				arpReply.getTargetAddress());
-		sendArpReplyToWaitingRequesters(arpReply.getTargetAddress(), 
-				arpReply.getTargetMacAddress());
-	}
+        InetAddress senderIpAddress;
+        try {
+            senderIpAddress = InetAddress.getByAddress(arp
+                    .getSenderProtocolAddress());
+        } catch (UnknownHostException e) {
+            log.debug("Invalid address in ARP reply", e);
+            return;
+        }
 
-	@Override
-	public void packetOutNotification(
-			PacketOutNotification packetOutNotification) {
-		
-		if (packetOutNotification instanceof SinglePacketOutNotification) {
-			SinglePacketOutNotification notification = 
-					(SinglePacketOutNotification) packetOutNotification;
-			sendArpRequestOutPort(notification.packet, notification.getOutSwitch(), 
-					notification.getOutPort());
-			
-			// set timestamp
-			InetAddress addr = notification.getTargetAddress();
-			if (addr != null) {
-				for (ArpRequest request : arpRequests.get(addr)) {
-					request.setRequestTime();
-				}
-			}
-		}
-		else if (packetOutNotification instanceof BroadcastPacketOutNotification) {
-			BroadcastPacketOutNotification notification = 
-					(BroadcastPacketOutNotification) packetOutNotification;
-			broadcastArpRequestOutMyEdge(notification.packet, 
-					notification.getInSwitch(), notification.getInPort());
-			
-			// set timestamp
-			InetAddress addr = notification.getTargetAddress();
-			if (addr != null) {
-				for (ArpRequest request : arpRequests.get(addr)) {
-					request.setRequestTime();
-				}
-			}
-		}
-		else {
-			log.warn("Unknown packet out notification received");
-		}
-	}
+        MACAddress senderMacAddress = MACAddress.valueOf(arp
+                .getSenderHardwareAddress());
+
+        // See if anyone's waiting for this ARP reply
+        Set<ArpRequest> requests = arpRequests.get(senderIpAddress);
+
+        // Synchronize on the Multimap while using an iterator for one of the
+        // sets
+        List<ArpRequest> requestsToSend = new ArrayList<ArpRequest>(
+                requests.size());
+        synchronized (arpRequests) {
+            Iterator<ArpRequest> it = requests.iterator();
+            while (it.hasNext()) {
+                ArpRequest request = it.next();
+                it.remove();
+                requestsToSend.add(request);
+            }
+        }
+
+        // Don't hold an ARP lock while dispatching requests
+        for (ArpRequest request : requestsToSend) {
+            request.dispatchReply(senderIpAddress, senderMacAddress);
+        }
+    }
+
+    private void sendArpRequestForAddress(InetAddress ipAddress) {
+        // TODO what should the sender IP address and MAC address be if no
+        // IP addresses are configured? Will there ever be a need to send
+        // ARP requests from the controller in that case?
+        // All-zero MAC address doesn't seem to work - hosts don't respond to it
+
+        byte[] zeroIpv4 = {0x0, 0x0, 0x0, 0x0};
+        byte[] zeroMac = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
+        byte[] genericNonZeroMac = {0x0, 0x0, 0x0, 0x0, 0x0, 0x01};
+        byte[] broadcastMac = {(byte) 0xff, (byte) 0xff, (byte) 0xff,
+                (byte) 0xff, (byte) 0xff, (byte) 0xff};
+
+        ARP arpRequest = new ARP();
+
+        arpRequest
+                .setHardwareType(ARP.HW_TYPE_ETHERNET)
+                .setProtocolType(ARP.PROTO_TYPE_IP)
+                .setHardwareAddressLength(
+                        (byte) Ethernet.DATALAYER_ADDRESS_LENGTH)
+                .setProtocolAddressLength((byte) IPv4.ADDRESS_LENGTH)
+                .setOpCode(ARP.OP_REQUEST).setTargetHardwareAddress(zeroMac)
+                .setTargetProtocolAddress(ipAddress.getAddress());
+
+        MACAddress routerMacAddress = configService.getRouterMacAddress();
+        // TODO hack for now as it's unclear what the MAC address should be
+        byte[] senderMacAddress = genericNonZeroMac;
+        if (routerMacAddress != null) {
+            senderMacAddress = routerMacAddress.toBytes();
+        }
+        arpRequest.setSenderHardwareAddress(senderMacAddress);
+
+        byte[] senderIPAddress = zeroIpv4;
+        Interface intf = configService.getOutgoingInterface(ipAddress);
+        if (intf != null) {
+            senderIPAddress = intf.getIpAddress().getAddress();
+        }
+
+        arpRequest.setSenderProtocolAddress(senderIPAddress);
+
+        Ethernet eth = new Ethernet();
+        eth.setSourceMACAddress(senderMacAddress)
+                .setDestinationMACAddress(broadcastMac)
+                .setEtherType(Ethernet.TYPE_ARP).setPayload(arpRequest);
+
+        if (vlan != NO_VLAN) {
+            eth.setVlanID(vlan).setPriorityCode((byte) 0);
+        }
+
+        // sendArpRequestToSwitches(ipAddress, eth.serialize());
+        datagrid.sendPacketOutNotification(new SinglePacketOutNotification(eth
+                .serialize(), ipAddress, intf.getDpid(), intf.getPort()));
+    }
+
+    private void sendArpRequestToSwitches(InetAddress dstAddress,
+            byte[] arpRequest) {
+        sendArpRequestToSwitches(dstAddress, arpRequest, 0,
+                OFPort.OFPP_NONE.getValue());
+    }
+
+    private void sendArpRequestToSwitches(InetAddress dstAddress,
+            byte[] arpRequest, long inSwitch, short inPort) {
+
+        if (configService.hasLayer3Configuration()) {
+            Interface intf = configService.getOutgoingInterface(dstAddress);
+            if (intf == null) {
+                // TODO here it should be broadcast out all non-interface edge
+                // ports.
+                // I think we can assume that if it's not a request for an
+                // external
+                // network, it's an ARP for a host in our own network. So we
+                // want to
+                // send it out all edge ports that don't have an interface
+                // configured
+                // to ensure it reaches all hosts in our network.
+                log.debug("No interface found to send ARP request for {}",
+                        dstAddress.getHostAddress());
+            } else {
+                sendArpRequestOutPort(arpRequest, intf.getDpid(),
+                        intf.getPort());
+            }
+        } else {
+            // broadcastArpRequestOutEdge(arpRequest, inSwitch, inPort);
+            broadcastArpRequestOutMyEdge(arpRequest, inSwitch, inPort);
+        }
+    }
+
+    private void sendArpReplyNotification(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;
+
+        try {
+            targetAddress = InetAddress.getByAddress(arp
+                    .getSenderProtocolAddress());
+        } catch (UnknownHostException e) {
+            log.error("Unknown host", e);
+            return;
+        }
+
+        MACAddress mac = new MACAddress(arp.getSenderHardwareAddress());
+
+        datagrid.sendArpReplyNotification(new ArpReplyNotification(
+                targetAddress, mac));
+    }
+
+    private void broadcastArpRequestOutMyEdge(byte[] arpRequest, long inSwitch,
+            short inPort) {
+        List<SwitchPort> switchPorts = new ArrayList<SwitchPort>();
+
+        for (IOFSwitch sw : floodlightProvider.getSwitches().values()) {
+
+            OFPacketOut po = new OFPacketOut();
+            po.setInPort(OFPort.OFPP_NONE).setBufferId(-1)
+                    .setPacketData(arpRequest);
+
+            List<OFAction> actions = new ArrayList<OFAction>();
+
+            // TODO: Fix the code below after topoSwitchService was removed
+            /*
+            Iterable<IPortObject> ports
+            	= topoSwitchService.getPortsOnSwitch(sw.getStringId());
+            if (ports == null) {
+            	continue;
+            }
+
+            for (IPortObject portObject : ports) {
+            	if (!portObject.getLinkedPorts().iterator().hasNext()) {
+            		short portNumber = portObject.getNumber();
+
+            		if (sw.getId() == inSwitch && portNumber == inPort) {
+            			// This is the port that the ARP message came in,
+            			// so don't broadcast out this port
+            			continue;
+            		}
+
+            		switchPorts.add(new SwitchPort(new Dpid(sw.getId()),
+            				new Port(portNumber)));
+            		actions.add(new OFActionOutput(portNumber));
+            	}
+            }
+            */
+
+            po.setActions(actions);
+            short actionsLength = (short) (actions.size() * OFActionOutput.MINIMUM_LENGTH);
+            po.setActionsLength(actionsLength);
+            po.setLengthU(OFPacketOut.MINIMUM_LENGTH + actionsLength
+                    + arpRequest.length);
+
+            flowPusher.add(sw, po);
+        }
+
+        if (log.isTraceEnabled()) {
+            log.trace("Broadcast ARP request to: {}", switchPorts);
+        }
+    }
+
+    private void sendArpRequestOutPort(byte[] arpRequest, long dpid, short port) {
+        if (log.isTraceEnabled()) {
+            log.trace("Sending ARP request out {}/{}",
+                    HexString.toHexString(dpid), port);
+        }
+
+        OFPacketOut po = new OFPacketOut();
+        po.setInPort(OFPort.OFPP_NONE).setBufferId(-1)
+                .setPacketData(arpRequest);
+
+        List<OFAction> actions = new ArrayList<OFAction>();
+        actions.add(new OFActionOutput(port));
+        po.setActions(actions);
+        short actionsLength = (short) (actions.size() * OFActionOutput.MINIMUM_LENGTH);
+        po.setActionsLength(actionsLength);
+        po.setLengthU(OFPacketOut.MINIMUM_LENGTH + actionsLength
+                + arpRequest.length);
+
+        IOFSwitch sw = floodlightProvider.getSwitches().get(dpid);
+
+        if (sw == null) {
+            log.warn("Switch not found when sending ARP request");
+            return;
+        }
+
+        flowPusher.add(sw, po);
+    }
+
+    private void sendArpReply(ARP arpRequest, long dpid, short port,
+            MACAddress targetMac) {
+        if (log.isTraceEnabled()) {
+            log.trace(
+                    "Sending reply {} => {} to {}",
+                    new Object[] {
+                            inetAddressToString(arpRequest
+                                    .getTargetProtocolAddress()),
+                            targetMac,
+                            inetAddressToString(arpRequest
+                                    .getSenderProtocolAddress())});
+        }
+
+        ARP arpReply = new ARP();
+        arpReply.setHardwareType(ARP.HW_TYPE_ETHERNET)
+                .setProtocolType(ARP.PROTO_TYPE_IP)
+                .setHardwareAddressLength(
+                        (byte) Ethernet.DATALAYER_ADDRESS_LENGTH)
+                .setProtocolAddressLength((byte) IPv4.ADDRESS_LENGTH)
+                .setOpCode(ARP.OP_REPLY)
+                .setSenderHardwareAddress(targetMac.toBytes())
+                .setSenderProtocolAddress(arpRequest.getTargetProtocolAddress())
+                .setTargetHardwareAddress(arpRequest.getSenderHardwareAddress())
+                .setTargetProtocolAddress(arpRequest.getSenderProtocolAddress());
+
+        Ethernet eth = new Ethernet();
+        eth.setDestinationMACAddress(arpRequest.getSenderHardwareAddress())
+                .setSourceMACAddress(targetMac.toBytes())
+                .setEtherType(Ethernet.TYPE_ARP).setPayload(arpReply);
+
+        if (vlan != NO_VLAN) {
+            eth.setVlanID(vlan).setPriorityCode((byte) 0);
+        }
+
+        List<OFAction> actions = new ArrayList<OFAction>();
+        actions.add(new OFActionOutput(port));
+
+        OFPacketOut po = new OFPacketOut();
+        po.setInPort(OFPort.OFPP_NONE)
+                .setBufferId(-1)
+                .setPacketData(eth.serialize())
+                .setActions(actions)
+                .setActionsLength((short) OFActionOutput.MINIMUM_LENGTH)
+                .setLengthU(
+                        OFPacketOut.MINIMUM_LENGTH
+                                + OFActionOutput.MINIMUM_LENGTH
+                                + po.getPacketData().length);
+
+        List<OFMessage> msgList = new ArrayList<OFMessage>();
+        msgList.add(po);
+
+        IOFSwitch sw = floodlightProvider.getSwitches().get(dpid);
+
+        if (sw == null) {
+            log.warn("Switch {} not found when sending ARP reply",
+                    HexString.toHexString(dpid));
+            return;
+        }
+
+        flowPusher.add(sw, po);
+    }
+
+    private String inetAddressToString(byte[] bytes) {
+        try {
+            return InetAddress.getByAddress(bytes).getHostAddress();
+        } catch (UnknownHostException e) {
+            log.debug("Invalid IP address", e);
+            return "";
+        }
+    }
+
+    /*
+     * IProxyArpService methods
+     */
+
+    @Override
+    public MACAddress getMacAddress(InetAddress ipAddress) {
+        // return arpCache.lookup(ipAddress);
+        return null;
+    }
+
+    @Override
+    public void sendArpRequest(InetAddress ipAddress, IArpRequester requester,
+            boolean retry) {
+        arpRequests.put(ipAddress, new ArpRequest(requester, retry));
+
+        // Sanity check to make sure we don't send a request for our own address
+        if (!configService.isInterfaceAddress(ipAddress)) {
+            sendArpRequestForAddress(ipAddress);
+        }
+    }
+
+    @Override
+    public List<String> getMappings() {
+        return new ArrayList<String>();
+    }
+
+    /*
+    @Override
+    public void arpRequestNotification(ArpMessage arpMessage) {
+    	log.debug("Received ARP notification from other instances");
+
+    	switch (arpMessage.getType()){
+    	case REQUEST:
+    		if(arpMessage.getOutSwitch() == -1 || arpMessage.getOutPort() == -1){
+    			broadcastArpRequestOutMyEdge(arpMessage.getPacket(),
+    					arpMessage.getInSwitch(), arpMessage.getInPort());
+    		}else{
+    			sendArpRequestOutPort(arpMessage.getPacket(),arpMessage.getOutSwitch(),arpMessage.getOutPort());
+    			log.debug("OutSwitch in ARP request message is: {}; " +
+    			"OutPort in ARP request message is: {}",arpMessage.getOutSwitch(),arpMessage.getOutPort());
+    		}
+    		break;
+    	case REPLY:
+    		log.debug("Received ARP reply notification for {}",
+    				arpMessage.getAddress());
+    		sendArpReplyToWaitingRequesters(arpMessage.getAddress(),arpMessage.getMAC());
+    		break;
+    	}
+    }
+    */
+
+    private void sendArpReplyToWaitingRequesters(InetAddress address,
+            MACAddress mac) {
+        log.debug("Sending ARP reply for {} to requesters",
+                address.getHostAddress());
+
+        // See if anyone's waiting for this ARP reply
+        Set<ArpRequest> requests = arpRequests.get(address);
+
+        // Synchronize on the Multimap while using an iterator for one of the
+        // sets
+        List<ArpRequest> requestsToSend = new ArrayList<ArpRequest>(
+                requests.size());
+        synchronized (arpRequests) {
+            Iterator<ArpRequest> it = requests.iterator();
+            while (it.hasNext()) {
+                ArpRequest request = it.next();
+                it.remove();
+                requestsToSend.add(request);
+            }
+        }
+
+        /*IDeviceObject deviceObject = deviceStorage.getDeviceByIP(
+        		InetAddresses.coerceToInteger(address));
+
+        MACAddress mac = MACAddress.valueOf(deviceObject.getMACAddress());
+
+        log.debug("Found {} at {} in network map",
+        		address.getHostAddress(), mac);*/
+
+        // Don't hold an ARP lock while dispatching requests
+        for (ArpRequest request : requestsToSend) {
+            request.dispatchReply(address, mac);
+        }
+    }
+
+    @Override
+    public void arpReplyEvent(ArpReplyNotification arpReply) {
+        log.debug("Received ARP reply notification for {}",
+                arpReply.getTargetAddress());
+        sendArpReplyToWaitingRequesters(arpReply.getTargetAddress(),
+                arpReply.getTargetMacAddress());
+    }
+
+    @Override
+    public void packetOutNotification(
+            PacketOutNotification packetOutNotification) {
+
+        if (packetOutNotification instanceof SinglePacketOutNotification) {
+            SinglePacketOutNotification notification = (SinglePacketOutNotification) packetOutNotification;
+            sendArpRequestOutPort(notification.packet,
+                    notification.getOutSwitch(), notification.getOutPort());
+
+            // set timestamp
+            InetAddress addr = notification.getTargetAddress();
+            if (addr != null) {
+                for (ArpRequest request : arpRequests.get(addr)) {
+                    request.setRequestTime();
+                }
+            }
+        } else if (packetOutNotification instanceof BroadcastPacketOutNotification) {
+            BroadcastPacketOutNotification notification = (BroadcastPacketOutNotification) packetOutNotification;
+            broadcastArpRequestOutMyEdge(notification.packet,
+                    notification.getInSwitch(), notification.getInPort());
+
+            // set timestamp
+            InetAddress addr = notification.getTargetAddress();
+            if (addr != null) {
+                for (ArpRequest request : arpRequests.get(addr)) {
+                    request.setRequestTime();
+                }
+            }
+        } else {
+            log.warn("Unknown packet out notification received");
+        }
+    }
 }
diff --git a/src/main/java/net/onrc/onos/ofcontroller/proxyarp/SinglePacketOutNotification.java b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/SinglePacketOutNotification.java
index d654f67..ab9a9b5 100644
--- a/src/main/java/net/onrc/onos/ofcontroller/proxyarp/SinglePacketOutNotification.java
+++ b/src/main/java/net/onrc/onos/ofcontroller/proxyarp/SinglePacketOutNotification.java
@@ -5,34 +5,53 @@
 // TODO This class is too generic to be handled by ProxyArpService.
 /**
  * Notification to another ONOS instance to send a packet out a single port.
- *
  */
 public class SinglePacketOutNotification extends PacketOutNotification {
 
-	private static final long serialVersionUID = 1L;
-	
-	private final InetAddress address;
-	private final long outSwitch;
-	private final short outPort;
-	
-	public SinglePacketOutNotification(byte[] packet, InetAddress address,
-			long outSwitch, short outPort) {
-		super(packet);
-		
-		this.address = address;
-		this.outSwitch = outSwitch;
-		this.outPort = outPort;
-	}
+    private static final long serialVersionUID = 1L;
 
-	public long getOutSwitch() {
-		return outSwitch;
-	}
+    private final InetAddress address;
+    private final long outSwitch;
+    private final short outPort;
 
-	public short getOutPort() {
-		return outPort;
-	}
+    /**
+     * Class constructor.
+     * @param packet the packet data to send in the packet-out
+     * @param address target IP address if the packet is an ARP packet
+     * @param outSwitch the dpid of the switch to send the packet on
+     * @param outPort the port number of the port to send the packet out
+     */
+    public SinglePacketOutNotification(byte[] packet, InetAddress address,
+            long outSwitch, short outPort) {
+        super(packet);
 
-	public InetAddress getTargetAddress() {
-		return address;
-	}
+        this.address = address;
+        this.outSwitch = outSwitch;
+        this.outPort = outPort;
+    }
+
+    /**
+     * Get the dpid of the switch the packet will be sent out.
+     * @return the switch's dpid
+     */
+    public long getOutSwitch() {
+        return outSwitch;
+    }
+
+    /**
+     * Get the port number of the port the packet will be sent out.
+     * @return the port number
+     */
+    public short getOutPort() {
+        return outPort;
+    }
+
+    /**
+     * Get the target IP address if the packet is an ARP packet.
+     * @return the target IP address for ARP packets, or null if the packet is
+     *         not an ARP packet
+     */
+    public InetAddress getTargetAddress() {
+        return address;
+    }
 }