Moved default flows logic into the PacketManager.

This prevents us duplicating code in each of the apps and allows us to manage
packet requests better in the future.

Change-Id: I5656b2f0f3cecd3e42fe7b4a0a5ab7cb6582bb25
diff --git a/core/net/src/main/java/org/onosproject/net/packet/impl/PacketManager.java b/core/net/src/main/java/org/onosproject/net/packet/impl/PacketManager.java
index a8195e4..94c75dd 100644
--- a/core/net/src/main/java/org/onosproject/net/packet/impl/PacketManager.java
+++ b/core/net/src/main/java/org/onosproject/net/packet/impl/PacketManager.java
@@ -18,7 +18,9 @@
 import static com.google.common.base.Preconditions.checkNotNull;
 import static org.slf4j.LoggerFactory.getLogger;
 
+import java.util.Collections;
 import java.util.Map;
+import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 
 import org.apache.felix.scr.annotations.Activate;
@@ -27,11 +29,21 @@
 import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.ReferenceCardinality;
 import org.apache.felix.scr.annotations.Service;
+import org.onosproject.core.ApplicationId;
 import org.onosproject.net.Device;
+import org.onosproject.net.device.DeviceEvent;
+import org.onosproject.net.device.DeviceListener;
 import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.flow.DefaultFlowRule;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.FlowRuleService;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
 import org.onosproject.net.packet.OutboundPacket;
 import org.onosproject.net.packet.PacketContext;
 import org.onosproject.net.packet.PacketEvent;
+import org.onosproject.net.packet.PacketPriority;
 import org.onosproject.net.packet.PacketProcessor;
 import org.onosproject.net.packet.PacketProvider;
 import org.onosproject.net.packet.PacketProviderRegistry;
@@ -60,19 +72,55 @@
     private DeviceService deviceService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    private FlowRuleService flowService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     private PacketStore store;
 
+    private final DeviceListener deviceListener = new InternalDeviceListener();
+
     private final Map<Integer, PacketProcessor> processors = new ConcurrentHashMap<>();
 
+    private Set<PacketRequest> packetRequests =
+            Collections.newSetFromMap(new ConcurrentHashMap<>());
+
+    private final class PacketRequest {
+        private final TrafficSelector selector;
+        private final PacketPriority priority;
+        private final ApplicationId appId;
+
+        public PacketRequest(TrafficSelector selector, PacketPriority priority,
+                             ApplicationId appId) {
+            this.selector = selector;
+            this.priority = priority;
+            this.appId = appId;
+        }
+
+        public TrafficSelector selector() {
+            return selector;
+        }
+
+        public PacketPriority priority() {
+            return priority;
+        }
+
+        public ApplicationId appId() {
+            return appId;
+        }
+
+    }
+
     @Activate
     public void activate() {
         store.setDelegate(delegate);
+        deviceService.addListener(deviceListener);
         log.info("Started");
     }
 
     @Deactivate
     public void deactivate() {
         store.unsetDelegate(delegate);
+        deviceService.removeListener(deviceListener);
         log.info("Stopped");
     }
 
@@ -89,6 +137,52 @@
     }
 
     @Override
+    public void requestPackets(TrafficSelector selector, PacketPriority priority,
+                               ApplicationId appId) {
+        checkNotNull(selector, "Selector cannot be null");
+        checkNotNull(appId, "Application ID cannot be null");
+
+        PacketRequest request =
+                new PacketRequest(selector, priority, appId);
+
+        packetRequests.add(request);
+        pushToAllDevices(request);
+    }
+
+    /**
+     * Pushes a packet request flow rule to all devices.
+     *
+     * @param request the packet request
+     */
+    private void pushToAllDevices(PacketRequest request) {
+        for (Device device : deviceService.getDevices()) {
+            pushRule(device, request);
+        }
+    }
+
+    /**
+     * Pushes flow rules to the device to request packets be sent to the
+     * controller.
+     *
+     * @param device the device to push the rules to
+     * @param request the packet request
+     */
+    private void pushRule(Device device, PacketRequest request) {
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                                                            .punt()
+                                                            .build();
+
+        FlowRule flow = new DefaultFlowRule(device.id(),
+                                request.selector(),
+                                treatment,
+                                request.priority().priorityValue(),
+                                request.appId(),
+                                0, true);
+
+        flowService.applyFlowRules(flow);
+    }
+
+    @Override
     public void emit(OutboundPacket packet) {
         checkNotNull(packet, "Packet cannot be null");
 
@@ -125,6 +219,7 @@
 
         @Override
         public void processPacket(PacketContext context) {
+            // TODO filter packets sent to processors based on registrations
             for (PacketProcessor processor : processors.values()) {
                 processor.process(context);
             }
@@ -143,4 +238,19 @@
         }
     }
 
+    /**
+     * Internal listener for device service events.
+     */
+    private class InternalDeviceListener implements DeviceListener {
+        @Override
+        public void event(DeviceEvent event) {
+            Device device = event.subject();
+            if (event.type() == DeviceEvent.Type.DEVICE_ADDED) {
+                for (PacketRequest request : packetRequests) {
+                    pushRule(device, request);
+                }
+            }
+        }
+    }
+
 }
diff --git a/core/net/src/main/java/org/onosproject/net/proxyarp/impl/ProxyArpManager.java b/core/net/src/main/java/org/onosproject/net/proxyarp/impl/ProxyArpManager.java
index 5a7dacd..2bb9c4f 100644
--- a/core/net/src/main/java/org/onosproject/net/proxyarp/impl/ProxyArpManager.java
+++ b/core/net/src/main/java/org/onosproject/net/proxyarp/impl/ProxyArpManager.java
@@ -37,8 +37,6 @@
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.VlanId;
-import org.onosproject.core.ApplicationId;
-import org.onosproject.core.CoreService;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.Device;
 import org.onosproject.net.Host;
@@ -49,12 +47,7 @@
 import org.onosproject.net.device.DeviceEvent;
 import org.onosproject.net.device.DeviceListener;
 import org.onosproject.net.device.DeviceService;
-import org.onosproject.net.flow.DefaultFlowRule;
-import org.onosproject.net.flow.DefaultTrafficSelector;
 import org.onosproject.net.flow.DefaultTrafficTreatment;
-import org.onosproject.net.flow.FlowRule;
-import org.onosproject.net.flow.FlowRuleService;
-import org.onosproject.net.flow.TrafficSelector;
 import org.onosproject.net.flow.TrafficTreatment;
 import org.onosproject.net.host.HostService;
 import org.onosproject.net.host.InterfaceIpAddress;
@@ -79,8 +72,6 @@
 
     private final Logger log = getLogger(getClass());
 
-    private static final int FLOW_RULE_PRIORITY = 40000;
-
     private static final String MAC_ADDR_NULL = "Mac address cannot be null.";
     private static final String REQUEST_NULL = "Arp request cannot be null.";
     private static final String REQUEST_NOT_ARP = "Ethernet frame does not contain ARP request.";
@@ -88,12 +79,6 @@
     private static final String NOT_ARP_REPLY = "ARP is not a reply.";
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected CoreService coreService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected FlowRuleService flowRuleService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected HostService hostService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
@@ -111,21 +96,15 @@
     private final Multimap<Device, PortNumber> externalPorts =
             HashMultimap.<Device, PortNumber>create();
 
-    private ApplicationId appId;
-
     /**
      * Listens to both device service and link service to determine
      * whether a port is internal or external.
      */
     @Activate
     public void activate() {
-        appId =
-            coreService.registerApplication("org.onosproject.net.proxyarp");
-
         deviceService.addListener(new InternalDeviceListener());
         linkService.addListener(new InternalLinkListener());
         determinePortLocations();
-        pushRules();
 
         log.info("Started");
     }
@@ -418,36 +397,6 @@
         return eth;
     }
 
-    /**
-     * Pushes flow rules to all devices.
-     */
-    private void pushRules() {
-        for (Device device : deviceService.getDevices()) {
-            pushRules(device);
-        }
-    }
-
-    /**
-     * Pushes flow rules to the device to receive control packets that need
-     * to be processed.
-     *
-     * @param device the device to push the rules to
-     */
-    private synchronized void pushRules(Device device) {
-        TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
-        TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
-
-        // Get all ARP packets
-        sbuilder.matchEthType(Ethernet.TYPE_ARP);
-        tbuilder.punt();
-        FlowRule flowArp =
-            new DefaultFlowRule(device.id(),
-                                sbuilder.build(), tbuilder.build(),
-                                FLOW_RULE_PRIORITY, appId, 0, true);
-
-        flowRuleService.applyFlowRules(flowArp);
-    }
-
     public class InternalLinkListener implements LinkListener {
 
         @Override
@@ -492,8 +441,6 @@
             Device device = event.subject();
             switch (event.type()) {
                 case DEVICE_ADDED:
-                    pushRules(device);
-                    break;
                 case DEVICE_AVAILABILITY_CHANGED:
                 case DEVICE_SUSPENDED:
                 case DEVICE_UPDATED:
diff --git a/core/net/src/test/java/org/onosproject/net/host/impl/HostMonitorTest.java b/core/net/src/test/java/org/onosproject/net/host/impl/HostMonitorTest.java
index eb60979..834379b 100644
--- a/core/net/src/test/java/org/onosproject/net/host/impl/HostMonitorTest.java
+++ b/core/net/src/test/java/org/onosproject/net/host/impl/HostMonitorTest.java
@@ -31,6 +31,12 @@
 
 import org.junit.After;
 import org.junit.Test;
+import org.onlab.packet.ARP;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.MacAddress;
+import org.onosproject.core.ApplicationId;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.Device;
 import org.onosproject.net.DeviceId;
@@ -40,20 +46,17 @@
 import org.onosproject.net.PortNumber;
 import org.onosproject.net.device.DeviceListener;
 import org.onosproject.net.device.DeviceServiceAdapter;
+import org.onosproject.net.flow.TrafficSelector;
 import org.onosproject.net.flow.instructions.Instruction;
 import org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
 import org.onosproject.net.host.HostProvider;
 import org.onosproject.net.host.InterfaceIpAddress;
 import org.onosproject.net.host.PortAddresses;
 import org.onosproject.net.packet.OutboundPacket;
+import org.onosproject.net.packet.PacketPriority;
 import org.onosproject.net.packet.PacketProcessor;
 import org.onosproject.net.packet.PacketService;
 import org.onosproject.net.provider.ProviderId;
-import org.onlab.packet.ARP;
-import org.onlab.packet.Ethernet;
-import org.onlab.packet.IpAddress;
-import org.onlab.packet.IpPrefix;
-import org.onlab.packet.MacAddress;
 
 import com.google.common.collect.HashMultimap;
 import com.google.common.collect.Lists;
@@ -187,6 +190,11 @@
         public void emit(OutboundPacket packet) {
             packets.add(packet);
         }
+
+        @Override
+        public void requestPackets(TrafficSelector selector,
+                                   PacketPriority priority, ApplicationId appId) {
+        }
     }
 
     class TestDeviceService extends DeviceServiceAdapter {
diff --git a/core/net/src/test/java/org/onosproject/net/proxyarp/impl/ProxyArpManagerTest.java b/core/net/src/test/java/org/onosproject/net/proxyarp/impl/ProxyArpManagerTest.java
index c364d8b..a80f0a1 100644
--- a/core/net/src/test/java/org/onosproject/net/proxyarp/impl/ProxyArpManagerTest.java
+++ b/core/net/src/test/java/org/onosproject/net/proxyarp/impl/ProxyArpManagerTest.java
@@ -18,7 +18,6 @@
 import static org.easymock.EasyMock.anyObject;
 import static org.easymock.EasyMock.createMock;
 import static org.easymock.EasyMock.expect;
-import static org.easymock.EasyMock.expectLastCall;
 import static org.easymock.EasyMock.replay;
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
@@ -33,9 +32,13 @@
 
 import org.junit.Before;
 import org.junit.Test;
+import org.onlab.packet.ARP;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.Ip4Prefix;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
 import org.onosproject.core.ApplicationId;
-import org.onosproject.core.CoreService;
-import org.onosproject.core.DefaultApplicationId;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DefaultHost;
 import org.onosproject.net.Device;
@@ -48,8 +51,7 @@
 import org.onosproject.net.PortNumber;
 import org.onosproject.net.device.DeviceListener;
 import org.onosproject.net.device.DeviceService;
-import org.onosproject.net.flow.FlowRule;
-import org.onosproject.net.flow.FlowRuleService;
+import org.onosproject.net.flow.TrafficSelector;
 import org.onosproject.net.flow.instructions.Instruction;
 import org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
 import org.onosproject.net.host.HostService;
@@ -58,15 +60,10 @@
 import org.onosproject.net.link.LinkListener;
 import org.onosproject.net.link.LinkService;
 import org.onosproject.net.packet.OutboundPacket;
+import org.onosproject.net.packet.PacketPriority;
 import org.onosproject.net.packet.PacketProcessor;
 import org.onosproject.net.packet.PacketService;
 import org.onosproject.net.provider.ProviderId;
-import org.onlab.packet.ARP;
-import org.onlab.packet.Ethernet;
-import org.onlab.packet.Ip4Address;
-import org.onlab.packet.Ip4Prefix;
-import org.onlab.packet.MacAddress;
-import org.onlab.packet.VlanId;
 
 import com.google.common.collect.Sets;
 
@@ -102,14 +99,9 @@
     private ProxyArpManager proxyArp;
 
     private TestPacketService packetService;
-
-    private CoreService coreService;
     private DeviceService deviceService;
-    private FlowRuleService flowRuleService;
     private LinkService linkService;
     private HostService hostService;
-    private ApplicationId appId = new DefaultApplicationId((short) 100,
-                "org.onosproject.net.proxyarp");
 
     @Before
     public void setUp() throws Exception {
@@ -123,9 +115,7 @@
         proxyArp.hostService = hostService;
 
         createTopology();
-        proxyArp.coreService = coreService;
         proxyArp.deviceService = deviceService;
-        proxyArp.flowRuleService = flowRuleService;
         proxyArp.linkService = linkService;
 
         proxyArp.activate();
@@ -142,16 +132,6 @@
      * addresses configured.
      */
     private void createTopology() {
-        coreService = createMock(CoreService.class);
-        expect(coreService.registerApplication(appId.name()))
-            .andReturn(appId).anyTimes();
-        replay(coreService);
-
-        flowRuleService = createMock(FlowRuleService.class);
-        flowRuleService.applyFlowRules(anyObject(FlowRule.class));
-        expectLastCall().anyTimes();
-        replay(flowRuleService);
-
         deviceService = createMock(DeviceService.class);
         linkService = createMock(LinkService.class);
 
@@ -602,5 +582,10 @@
         public void emit(OutboundPacket packet) {
             packets.add(packet);
         }
+
+        @Override
+        public void requestPackets(TrafficSelector selector,
+                                   PacketPriority priority, ApplicationId appId) {
+        }
     }
 }