ONOS-6059 Favor FlowRuleProgrammable

Enable Driver to override flow programming implementation,
even for full feature southbound implementation, such as OpenFlow.

Change-Id: I3df74d3df741afe9a3900bc6a8e26a26a1ed1ec0
diff --git a/core/net/src/main/java/org/onosproject/net/flow/impl/FlowRuleManager.java b/core/net/src/main/java/org/onosproject/net/flow/impl/FlowRuleManager.java
index cd244ff..2bd6ca2 100644
--- a/core/net/src/main/java/org/onosproject/net/flow/impl/FlowRuleManager.java
+++ b/core/net/src/main/java/org/onosproject/net/flow/impl/FlowRuleManager.java
@@ -52,6 +52,7 @@
 import org.onosproject.net.flow.FlowRuleListener;
 import org.onosproject.net.flow.FlowRuleOperation;
 import org.onosproject.net.flow.FlowRuleOperations;
+import org.onosproject.net.flow.FlowRuleProgrammable;
 import org.onosproject.net.flow.FlowRuleProvider;
 import org.onosproject.net.flow.FlowRuleProviderRegistry;
 import org.onosproject.net.flow.FlowRuleProviderService;
@@ -61,6 +62,7 @@
 import org.onosproject.net.flow.TableStatisticsEntry;
 import org.onosproject.net.provider.AbstractListenerProviderRegistry;
 import org.onosproject.net.provider.AbstractProviderService;
+import org.onosproject.net.provider.ProviderId;
 import org.osgi.service.component.ComponentContext;
 import org.slf4j.Logger;
 
@@ -69,6 +71,7 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ExecutorService;
@@ -118,7 +121,7 @@
     private final FlowRuleStoreDelegate delegate = new InternalStoreDelegate();
     private final DeviceListener deviceListener = new InternalDeviceListener();
 
-    private final FlowRuleDriverProvider defaultProvider = new FlowRuleDriverProvider();
+    private final FlowRuleDriverProvider driverProvider = new FlowRuleDriverProvider();
 
     protected ExecutorService deviceInstallers =
             Executors.newFixedThreadPool(32, groupedThreads("onos/flowservice", "device-installer-%d", log));
@@ -172,13 +175,13 @@
         if (context != null) {
             readComponentConfiguration(context);
         }
-        defaultProvider.init(new InternalFlowRuleProviderService(defaultProvider),
+        driverProvider.init(new InternalFlowRuleProviderService(driverProvider),
                              deviceService, mastershipService, fallbackFlowPollFrequency);
     }
 
     @Override
     protected FlowRuleProvider defaultProvider() {
-        return defaultProvider;
+        return driverProvider;
     }
 
     /**
@@ -323,6 +326,26 @@
         return new InternalFlowRuleProviderService(provider);
     }
 
+    @Override
+    protected synchronized FlowRuleProvider getProvider(ProviderId pid) {
+        log.warn("should not be calling getProvider(ProviderId)");
+        return super.getProvider(pid);
+    }
+
+    /**
+     * {@inheritDoc}
+     * if the Device does not support {@link FlowRuleProgrammable}.
+     */
+    @Override
+    protected synchronized FlowRuleProvider getProvider(DeviceId deviceId) {
+        // if device supports FlowRuleProgrammable,
+        // use FlowRuleProgrammable via FlowRuleDriverProvider
+        return Optional.ofNullable(deviceService.getDevice(deviceId))
+                        .filter(dev -> dev.is(FlowRuleProgrammable.class))
+                        .<FlowRuleProvider>map(x -> driverProvider)
+                        .orElseGet(() -> super.getProvider(deviceId));
+    }
+
     private class InternalFlowRuleProviderService
             extends AbstractProviderService<FlowRuleProvider>
             implements FlowRuleProviderService {
@@ -349,8 +372,7 @@
             if (flowEntry.reason() == FlowEntry.FlowRemoveReason.HARD_TIMEOUT) {
                 ((DefaultFlowEntry) stored).setState(FlowEntry.FlowEntryState.REMOVED);
             }
-            Device device = deviceService.getDevice(flowEntry.deviceId());
-            FlowRuleProvider frp = getProvider(device.providerId());
+            FlowRuleProvider frp = getProvider(flowEntry.deviceId());
             FlowRuleEvent event = null;
             switch (stored.state()) {
                 case ADDED:
@@ -375,8 +397,7 @@
         private void flowMissing(FlowEntry flowRule) {
             checkNotNull(flowRule, FLOW_RULE_NULL);
             checkValidity();
-            Device device = deviceService.getDevice(flowRule.deviceId());
-            FlowRuleProvider frp = getProvider(device.providerId());
+            FlowRuleProvider frp = getProvider(flowRule.deviceId());
             FlowRuleEvent event = null;
             switch (flowRule.state()) {
                 case PENDING_REMOVE:
@@ -409,6 +430,7 @@
         private void extraneousFlow(FlowRule flowRule) {
             checkNotNull(flowRule, FLOW_RULE_NULL);
             checkValidity();
+            // getProvider is customized to favor driverProvider
             FlowRuleProvider frp = getProvider(flowRule.deviceId());
             frp.removeFlowRule(flowRule);
             log.debug("Flow {} is on switch but not in store.", flowRule);
@@ -573,6 +595,7 @@
 
                 DeviceId deviceId = event.deviceId();
                 FlowRuleBatchOperation batchOperation = request.asBatchOperation(deviceId);
+                // getProvider is customized to favor driverProvider
                 FlowRuleProvider flowRuleProvider = getProvider(deviceId);
                 if (flowRuleProvider != null) {
                     flowRuleProvider.executeBatch(batchOperation);