Bug 1234: Force to use legacy OF plugin if it is installed. 60/8860/3
authorShigeru Yasuda <s-yasuda@da.jp.nec.com>
Wed, 9 Jul 2014 13:17:17 +0000 (22:17 +0900)
committerGiovanni Meo <gmeo@cisco.com>
Mon, 25 Aug 2014 10:38:24 +0000 (10:38 +0000)
This patch enables to prioritize AD-SAL protocol plugin services,
and gives higher priority to legacy OF plugin than sal-compatibility.

This patch never affects integration.git artifacts because legacy
OF plugin is filtered out if MD-SAL OF plugin is installed.

Change-Id: Iee40a6f724db2105f2ec36014a10872bad824e86
Signed-off-by: Shigeru Yasuda <s-yasuda@da.jp.nec.com>
opendaylight/protocol_plugins/openflow/src/main/java/org/opendaylight/controller/protocol_plugin/openflow/internal/Activator.java
opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/utils/GlobalConstants.java
opendaylight/sal/implementation/src/main/java/org/opendaylight/controller/sal/implementation/internal/DataPacketService.java
opendaylight/sal/implementation/src/main/java/org/opendaylight/controller/sal/implementation/internal/FlowProgrammerService.java
opendaylight/sal/implementation/src/main/java/org/opendaylight/controller/sal/implementation/internal/ProtocolService.java [new file with mode: 0644]
opendaylight/sal/implementation/src/main/java/org/opendaylight/controller/sal/implementation/internal/ReadService.java
opendaylight/sal/implementation/src/test/java/org/opendaylight/controller/sal/implementation/ProtocolServiceTest.java [new file with mode: 0644]

index 4a6a291e6a0a1518c68ee181c771139be5146ec5..d92c57bd8a59d9852326e2f2238af9dac2784971 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ * Copyright (c) 2013-2014 Cisco Systems, Inc. and others.  All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
@@ -57,6 +57,10 @@ public class Activator extends ComponentActivatorAbstractBase {
     protected static final Logger logger = LoggerFactory
             .getLogger(Activator.class);
 
+    /**
+     * Priority to determine whether to override existing protocol service.
+     */
+    private static final int  PLUGIN_PRIORITY = 10;
 
     /**
      * Function that is used to communicate to dependency manager the list of
@@ -136,6 +140,8 @@ public class Activator extends ComponentActivatorAbstractBase {
             // Set the protocolPluginType property which will be used
             // by SAL
             props.put(GlobalConstants.PROTOCOLPLUGINTYPE.toString(), Node.NodeIDType.OPENFLOW);
+            props.put(GlobalConstants.PROTOCOLPLUGINPRIORITY.toString(),
+                      Integer.valueOf(PLUGIN_PRIORITY));
             c.setInterface(IPluginInDataPacketService.class.getName(), props);
             // Hook the services coming in from SAL, as optional in
             // case SAL is not yet there, could happen
@@ -165,6 +171,8 @@ public class Activator extends ComponentActivatorAbstractBase {
             // Set the protocolPluginType property which will be used
             // by SAL
             props.put(GlobalConstants.PROTOCOLPLUGINTYPE.toString(), Node.NodeIDType.OPENFLOW);
+            props.put(GlobalConstants.PROTOCOLPLUGINPRIORITY.toString(),
+                      Integer.valueOf(PLUGIN_PRIORITY));
             c.setInterface(new String[] {
                     IReadFilterInternalListener.class.getName(),
                     IPluginInReadService.class.getName() }, props);
@@ -256,6 +264,8 @@ public class Activator extends ComponentActivatorAbstractBase {
             // Set the protocolPluginType property which will be used
             // by SAL
             props.put(GlobalConstants.PROTOCOLPLUGINTYPE.toString(), Node.NodeIDType.OPENFLOW);
+            props.put(GlobalConstants.PROTOCOLPLUGINPRIORITY.toString(),
+                      Integer.valueOf(PLUGIN_PRIORITY));
             c.setInterface(
                     new String[] { IPluginInFlowProgrammerService.class.getName(), IMessageListener.class.getName(),
                             IContainerListener.class.getName(), IInventoryShimExternalListener.class.getName(),
index 174f2546bad1039f87352b5e0a8bd129ad383732..ceb4553470933330516bd945652a07045dcf7e85 100644 (file)
@@ -1,6 +1,5 @@
-
 /*
- * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ * Copyright (c) 2013-2014 Cisco Systems, Inc. and others.  All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
@@ -20,7 +19,8 @@ public enum GlobalConstants {
     STATICVLAN("staticvlan"),
     CLUSTERINGSERVICES("clusteringservices"),
     STARTUPHOME("configuration/startup/"),
-    PROTOCOLPLUGINTYPE("protocolPluginType");
+    PROTOCOLPLUGINTYPE("protocolPluginType"),
+    PROTOCOLPLUGINPRIORITY("protocolPluginPriority");
 
     private GlobalConstants(String name) {
         this.name = name;
@@ -31,4 +31,4 @@ public enum GlobalConstants {
     public String toString() {
         return name;
     }
-}
\ No newline at end of file
+}
index e9039261a3724fb733f5a260300c291e9712224a..0c9ebab8c3c126a2442a0b8ef87ab30577602a10 100644 (file)
@@ -1,6 +1,5 @@
-
 /*
- * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ * Copyright (c) 2013-2014 Cisco Systems, Inc. and others.  All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
@@ -40,7 +39,6 @@ import org.opendaylight.controller.sal.packet.LinkEncap;
 import org.opendaylight.controller.sal.packet.Packet;
 import org.opendaylight.controller.sal.packet.PacketResult;
 import org.opendaylight.controller.sal.packet.RawPacket;
-import org.opendaylight.controller.sal.utils.GlobalConstants;
 import org.opendaylight.controller.sal.utils.NetUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -59,9 +57,9 @@ public class DataPacketService implements IPluginOutDataPacketService,
      * adding a new service, removing a service, going through all of
      * them maybe different.
      */
-    private ConcurrentHashMap<String, IPluginInDataPacketService>
+    private ConcurrentHashMap<String, ProtocolService<IPluginInDataPacketService>>
         pluginInDataService =
-        new ConcurrentHashMap<String, IPluginInDataPacketService>();
+        new ConcurrentHashMap<String, ProtocolService<IPluginInDataPacketService>>();
     private Map<String, AtomicInteger> statistics = new HashMap<String, AtomicInteger>();
 
     /**
@@ -186,11 +184,11 @@ public class DataPacketService implements IPluginOutDataPacketService,
                         String t = p.getNode()
                                 .getType();
                         // Now locate the TX dispatcher
-                        IPluginInDataPacketService s = pluginInDataService
-                                .get(t);
-                        if (s != null) {
+                        ProtocolService<IPluginInDataPacketService> service =
+                            pluginInDataService.get(t);
+                        if (service != null) {
                             try {
-                                s.transmitDataPacket(pkt);
+                                service.getService().transmitDataPacket(pkt);
                                 increaseStat("TXPacketSuccess");
                             } catch (Exception e) {
                                 increaseStat("TXPacketFailedForException");
@@ -207,54 +205,11 @@ public class DataPacketService implements IPluginOutDataPacketService,
     }
 
     void setPluginInDataService(Map props, IPluginInDataPacketService s) {
-        if (this.pluginInDataService == null) {
-            logger.error("pluginInDataService store null");
-            return;
-        }
-        String type = null;
-        logger.trace("Received setPluginInDataService request");
-        for (Object e : props.entrySet()) {
-            Map.Entry entry = (Map.Entry) e;
-            logger.trace("Prop key:({}) value:({})",entry.getKey(), entry.getValue());
-        }
-
-        Object value = props.get(GlobalConstants.PROTOCOLPLUGINTYPE.toString());
-        if (value instanceof String) {
-            type = (String) value;
-        }
-        if (type == null) {
-            logger.error("Received a PluginInDataService without any "
-                    + "protocolPluginType provided");
-        } else {
-            this.pluginInDataService.put(type, s);
-            logger.debug("Stored the PluginInDataService for type: {}", type);
-        }
+        ProtocolService.set(this.pluginInDataService, props, s, logger);
     }
 
     void unsetPluginInDataService(Map props, IPluginInDataPacketService s) {
-        if (this.pluginInDataService == null) {
-            logger.error("pluginInDataService store null");
-            return;
-        }
-
-        String type = null;
-        logger.trace("Received unsetPluginInDataService request");
-        for (Object e : props.entrySet()) {
-            Map.Entry entry = (Map.Entry) e;
-            logger.trace("Prop key:({}) value:({})",entry.getKey(), entry.getValue());
-        }
-
-        Object value = props.get(GlobalConstants.PROTOCOLPLUGINTYPE.toString());
-        if (value instanceof String) {
-            type = (String) value;
-        }
-        if (type == null) {
-            logger.error("Received a PluginInDataService without any "
-                    + "protocolPluginType provided");
-        } else if (this.pluginInDataService.get(type).equals(s)) {
-            this.pluginInDataService.remove(type);
-            logger.debug("Removed the PluginInDataService for type: {}", type);
-        }
+        ProtocolService.unset(this.pluginInDataService, props, s, logger);
     }
 
     void setListenDataPacket(Map props, IListenDataPacket s) {
index 854125b3567a196b940556363ed56c82e99d50c0..60a7882bda552b5e2fa5fddfb7ebafad6d51ccbb 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ * Copyright (c) 2013-2014 Cisco Systems, Inc. and others.  All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
@@ -38,7 +38,6 @@ import org.opendaylight.controller.sal.flowprogrammer.IPluginOutFlowProgrammerSe
 import org.opendaylight.controller.sal.match.Match;
 import org.opendaylight.controller.sal.match.MatchType;
 import org.opendaylight.controller.sal.utils.EtherTypes;
-import org.opendaylight.controller.sal.utils.GlobalConstants;
 import org.opendaylight.controller.sal.utils.IPProtocols;
 import org.opendaylight.controller.sal.utils.NodeConnectorCreator;
 import org.opendaylight.controller.sal.utils.Status;
@@ -58,12 +57,12 @@ public class FlowProgrammerService implements IFlowProgrammerService,
 
     protected static final Logger logger = LoggerFactory
             .getLogger(FlowProgrammerService.class);
-    private ConcurrentHashMap<String, IPluginInFlowProgrammerService> pluginFlowProgrammer;
+    private ConcurrentHashMap<String, ProtocolService<IPluginInFlowProgrammerService>> pluginFlowProgrammer;
     private Set<IFlowProgrammerListener> listener;
     private AtomicLong seq;
 
     public FlowProgrammerService() {
-        pluginFlowProgrammer = new ConcurrentHashMap<String, IPluginInFlowProgrammerService>();
+        pluginFlowProgrammer = new ConcurrentHashMap<String, ProtocolService<IPluginInFlowProgrammerService>>();
         listener = new HashSet<IFlowProgrammerListener>();
         seq = new AtomicLong();
         /*
@@ -117,58 +116,11 @@ public class FlowProgrammerService implements IFlowProgrammerService,
 
     // Set the reference to the plugin flow programmer
     public void setService(Map<String, Object> props, IPluginInFlowProgrammerService s) {
-        if (this.pluginFlowProgrammer == null) {
-            logger.error("pluginFlowProgrammer store null");
-            return;
-        }
-
-        if (logger.isTraceEnabled()) {
-            logger.trace("Got a service set request {}", s);
-            for (Map.Entry<String, Object> entry : props.entrySet()) {
-                logger.trace("Prop key:({}) value:({})", entry.getKey(), entry.getValue());
-            }
-        }
-
-        String type = null;
-        Object value = props.get(GlobalConstants.PROTOCOLPLUGINTYPE.toString());
-        if (value instanceof String) {
-            type = (String) value;
-        }
-        if (type == null) {
-            logger.error("Received a pluginFlowProgrammer without any "
-                    + "protocolPluginType provided");
-        } else {
-            this.pluginFlowProgrammer.put(type, s);
-            logger.debug("Stored the pluginFlowProgrammer for type: {}", type);
-        }
+        ProtocolService.set(this.pluginFlowProgrammer, props, s, logger);
     }
 
     public void unsetService(Map<String, Object> props, IPluginInFlowProgrammerService s) {
-        if (this.pluginFlowProgrammer == null) {
-            logger.error("pluginFlowProgrammer store null");
-            return;
-        }
-
-        logger.debug("Received unsetpluginFlowProgrammer request");
-        if (logger.isTraceEnabled()) {
-            logger.trace("Got a service set request {}", s);
-            for (Map.Entry<String, Object> entry : props.entrySet()) {
-                logger.trace("Prop key:({}) value:({})", entry.getKey(), entry.getValue());
-            }
-        }
-
-        String type = null;
-        Object value = props.get(GlobalConstants.PROTOCOLPLUGINTYPE.toString());
-        if (value instanceof String) {
-            type = (String) value;
-        }
-        if (type == null) {
-            logger.error("Received a pluginFlowProgrammer without any "
-                    + "protocolPluginType provided");
-        } else if (this.pluginFlowProgrammer.get(type).equals(s)) {
-            this.pluginFlowProgrammer.remove(type);
-            logger.debug("Removed the pluginFlowProgrammer for type: {}", type);
-        }
+        ProtocolService.unset(this.pluginFlowProgrammer, props, s, logger);
     }
 
     public void setListener(IFlowProgrammerListener s) {
@@ -182,9 +134,10 @@ public class FlowProgrammerService implements IFlowProgrammerService,
     @Override
     public Status addFlow(Node node, Flow flow) {
         if (pluginFlowProgrammer != null) {
-            if (this.pluginFlowProgrammer.get(node.getType()) != null) {
-                return this.pluginFlowProgrammer.get(node.getType()).addFlow(
-                        node, flow);
+            ProtocolService<IPluginInFlowProgrammerService> service =
+                this.pluginFlowProgrammer.get(node.getType());
+            if (service != null) {
+                return service.getService().addFlow(node, flow);
             }
         }
         return new Status(StatusCode.NOSERVICE, "Plugin unuvailable");
@@ -193,9 +146,10 @@ public class FlowProgrammerService implements IFlowProgrammerService,
     @Override
     public Status removeFlow(Node node, Flow flow) {
         if (pluginFlowProgrammer != null) {
-            if (this.pluginFlowProgrammer.get(node.getType()) != null) {
-                return this.pluginFlowProgrammer.get(node.getType())
-                        .removeFlow(node, flow);
+            ProtocolService<IPluginInFlowProgrammerService> service =
+                this.pluginFlowProgrammer.get(node.getType());
+            if (service != null) {
+                return service.getService().removeFlow(node, flow);
             }
         }
         return new Status(StatusCode.NOSERVICE, "Plugin unuvailable");
@@ -204,9 +158,10 @@ public class FlowProgrammerService implements IFlowProgrammerService,
     @Override
     public Status removeAllFlows(Node node) {
         if (pluginFlowProgrammer != null) {
-            if (this.pluginFlowProgrammer.get(node.getType()) != null) {
-                return this.pluginFlowProgrammer.get(node.getType())
-                        .removeAllFlows(node);
+            ProtocolService<IPluginInFlowProgrammerService> service =
+                this.pluginFlowProgrammer.get(node.getType());
+            if (service != null) {
+                return service.getService().removeAllFlows(node);
             }
         }
         return new Status(StatusCode.NOSERVICE, "Plugin unuvailable");
@@ -215,9 +170,10 @@ public class FlowProgrammerService implements IFlowProgrammerService,
     @Override
     public Status modifyFlow(Node node, Flow oldFlow, Flow newFlow) {
         if (pluginFlowProgrammer != null) {
-            if (this.pluginFlowProgrammer.get(node.getType()) != null) {
-                return this.pluginFlowProgrammer.get(node.getType())
-                        .modifyFlow(node, oldFlow, newFlow);
+            ProtocolService<IPluginInFlowProgrammerService> service =
+                this.pluginFlowProgrammer.get(node.getType());
+            if (service != null) {
+                return service.getService().modifyFlow(node, oldFlow, newFlow);
             }
         }
         return new Status(StatusCode.NOSERVICE, "Plugin unuvailable");
@@ -226,9 +182,10 @@ public class FlowProgrammerService implements IFlowProgrammerService,
     @Override
     public Status addFlowAsync(Node node, Flow flow) {
         if (pluginFlowProgrammer != null) {
-            if (this.pluginFlowProgrammer.get(node.getType()) != null) {
-                return this.pluginFlowProgrammer.get(node.getType()).addFlowAsync(
-                        node, flow, getNextRid());
+            ProtocolService<IPluginInFlowProgrammerService> service =
+                this.pluginFlowProgrammer.get(node.getType());
+            if (service != null) {
+                return service.getService().addFlowAsync(node, flow, getNextRid());
             }
         }
         return new Status(StatusCode.NOSERVICE, "Plugin unuvailable");
@@ -237,9 +194,10 @@ public class FlowProgrammerService implements IFlowProgrammerService,
     @Override
     public Status removeFlowAsync(Node node, Flow flow) {
         if (pluginFlowProgrammer != null) {
-            if (this.pluginFlowProgrammer.get(node.getType()) != null) {
-                return this.pluginFlowProgrammer.get(node.getType())
-                        .removeFlowAsync(node, flow, getNextRid());
+            ProtocolService<IPluginInFlowProgrammerService> service =
+                this.pluginFlowProgrammer.get(node.getType());
+            if (service != null) {
+                return service.getService().removeFlowAsync(node, flow, getNextRid());
             }
         }
         return new Status(StatusCode.NOSERVICE, "Plugin unuvailable");
@@ -248,9 +206,10 @@ public class FlowProgrammerService implements IFlowProgrammerService,
     @Override
     public Status modifyFlowAsync(Node node, Flow oldFlow, Flow newFlow) {
         if (pluginFlowProgrammer != null) {
-            if (this.pluginFlowProgrammer.get(node.getType()) != null) {
-                return this.pluginFlowProgrammer.get(node.getType())
-                        .modifyFlowAsync(node, oldFlow, newFlow, getNextRid());
+            ProtocolService<IPluginInFlowProgrammerService> service =
+                this.pluginFlowProgrammer.get(node.getType());
+            if (service != null) {
+                return service.getService().modifyFlowAsync(node, oldFlow, newFlow, getNextRid());
             }
         }
         return new Status(StatusCode.NOSERVICE, "Plugin unuvailable");
@@ -508,9 +467,10 @@ public class FlowProgrammerService implements IFlowProgrammerService,
     @Override
     public Status syncSendBarrierMessage(Node node) {
         if (this.pluginFlowProgrammer != null) {
-            if (this.pluginFlowProgrammer.get(node.getType()) != null) {
-                return this.pluginFlowProgrammer.get(node.getType())
-                        .syncSendBarrierMessage(node);
+            ProtocolService<IPluginInFlowProgrammerService> service =
+                this.pluginFlowProgrammer.get(node.getType());
+            if (service != null) {
+                return service.getService().syncSendBarrierMessage(node);
             }
         }
         return new Status(StatusCode.NOSERVICE, "Plugin unuvailable");
@@ -519,9 +479,10 @@ public class FlowProgrammerService implements IFlowProgrammerService,
     @Override
     public Status asyncSendBarrierMessage(Node node) {
         if (this.pluginFlowProgrammer != null) {
-            if (this.pluginFlowProgrammer.get(node.getType()) != null) {
-                return this.pluginFlowProgrammer.get(node.getType())
-                        .asyncSendBarrierMessage(node);
+            ProtocolService<IPluginInFlowProgrammerService> service =
+                this.pluginFlowProgrammer.get(node.getType());
+            if (service != null) {
+                return service.getService().asyncSendBarrierMessage(node);
             }
         }
         return new Status(StatusCode.NOSERVICE, "Plugin unuvailable");
diff --git a/opendaylight/sal/implementation/src/main/java/org/opendaylight/controller/sal/implementation/internal/ProtocolService.java b/opendaylight/sal/implementation/src/main/java/org/opendaylight/controller/sal/implementation/internal/ProtocolService.java
new file mode 100644 (file)
index 0000000..e9dc2ad
--- /dev/null
@@ -0,0 +1,223 @@
+/*
+ * Copyright (c) 2014 NEC Corporation and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.sal.implementation.internal;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentMap;
+
+import org.slf4j.Logger;
+
+import org.opendaylight.controller.sal.utils.GlobalConstants;
+
+/**
+ * An instance of this class keeps a protocol plugin service handler.
+ *
+ * @param <T>  Type of protocol plugin service.
+ */
+public final class ProtocolService<T> {
+    /**
+     * Default priority value.
+     */
+    private static final int  DEFAULT_PRIORITY = 0;
+
+    /**
+     * A protocol plugin service handler.
+     */
+    private final T  service;
+
+    /**
+     * A priority value assigned to this protocol plugin.
+     */
+    private final int  priority;
+
+    /**
+     * Set protocol plugin service.
+     *
+     * @param map     A map that keeps protocol plugin services.
+     * @param props   Service properties.
+     * @param s       Protocol plugin service.
+     * @param logger  A logger instance.
+     * @param <S>     Type of protocol plugin service.
+     */
+    public static <S> void set(ConcurrentMap<String, ProtocolService<S>> map,
+                               Map<?, ?> props, S s, Logger logger) {
+        if (map == null) {
+            logger.error("Protocol plugin service store is null.");
+            return;
+        }
+        if (s == null) {
+            logger.error("Protocol plugin service is null.");
+            return;
+        }
+        if (props == null) {
+            logger.error("Service property is null.");
+            return;
+        }
+
+        if (logger.isTraceEnabled()) {
+            logger.trace("Received set service request: {}", s);
+            for (Map.Entry<?, ?> entry: props.entrySet()) {
+                logger.trace("Prop key:({}) value:({})", entry.getKey(),
+                             entry.getValue());
+            }
+        }
+
+        Object value = props.get(GlobalConstants.PROTOCOLPLUGINTYPE.toString());
+        if (!(value instanceof String)) {
+            logger.error("Unexpected protocol type: {}", value);
+            return;
+        }
+
+        String type = (String)value;
+        ProtocolService<S> service = new ProtocolService<S>(props, s);
+        ProtocolService<S> old = map.putIfAbsent(type, service);
+        while (old != null) {
+            // Compare priority value.
+            if (old.getPriority() >= service.getPriority()) {
+                logger.trace("Protocol plugin service for {} is already set: " +
+                             "current={}, requested={}", type, old, service);
+                return;
+            }
+
+            if (map.replace(type, old, service)) {
+                break;
+            }
+            old = map.putIfAbsent(type, service);
+        }
+
+        logger.debug("Stored protocol plugin service for {}: {}",
+                     type, service);
+    }
+
+    /**
+     * Unset protocol plugin service.
+     *
+     * @param map     A map that keeps protocol plugin services.
+     * @param props   Service properties.
+     * @param s       Protocol plugin service.
+     * @param logger  A logger instance.
+     * @param <S>     Type of protocol plugin service.
+     */
+    public static <S> void unset(ConcurrentMap<String, ProtocolService<S>> map,
+                                 Map<?, ?> props, S s, Logger logger) {
+        if (map == null) {
+            logger.error("Protocol plugin service store is null.");
+            return;
+        }
+        if (s == null) {
+            logger.error("Protocol plugin service is null.");
+            return;
+        }
+        if (props == null) {
+            logger.error("Service property is null.");
+            return;
+        }
+
+        if (logger.isTraceEnabled()) {
+            logger.trace("Received unset service request: {}", s);
+            for (Map.Entry<?, ?> entry: props.entrySet()) {
+                logger.trace("Prop key:({}) value:({})",
+                             entry.getKey(), entry.getValue());
+            }
+        }
+
+        Object value = props.get(GlobalConstants.PROTOCOLPLUGINTYPE.toString());
+        if (!(value instanceof String)) {
+            logger.error("Unexpected protocol type {}: service={}", value, s);
+            return;
+        }
+
+        String type = (String)value;
+        ProtocolService<S> plugin = new ProtocolService<S>(props, s);
+        if (map.remove(type, plugin)) {
+            logger.debug("Removed protocol plugin service for {}: {}",
+                         type, plugin);
+        } else {
+            logger.trace("Ignore unset service request for {}: {}",
+                         type, plugin);
+        }
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param props  Protocol plugin service properties.
+     * @param s      A protocol plugin service handler.
+     */
+    public ProtocolService(Map<?, ?> props, T s) {
+        service = s;
+
+        String key = GlobalConstants.PROTOCOLPLUGINPRIORITY.toString();
+        Object value = props.get(key);
+        if (value instanceof Integer) {
+            priority = ((Integer)value).intValue();
+        } else {
+            priority = DEFAULT_PRIORITY;
+        }
+    }
+
+    /**
+     * Return a protocol plugin service handler.
+     *
+     * @return  A protocol plugin service handler.
+     */
+    public T getService() {
+        return service;
+    }
+
+    /**
+     * Return a priority value assigned to this protocol plugin.
+     *
+     * @return  A priority value.
+     */
+    public int getPriority() {
+        return priority;
+    }
+
+    /**
+     * Determine whether the given object is identical to this object.
+     *
+     * @param o  An object to be compared.
+     * @return   {@code true} if identical. Otherwise {@code false}.
+     */
+    @Override
+    public boolean equals(Object o) {
+        if (o == this) {
+            return true;
+        }
+        if (o == null || o.getClass() != getClass()) {
+            return false;
+        }
+
+        ProtocolService plugin = (ProtocolService)o;
+        return (service.equals(plugin.service) && priority == plugin.priority);
+    }
+
+    /**
+     * Return the hash code of this object.
+     *
+     * @return  The hash code.
+     */
+    @Override
+    public int hashCode() {
+        return service.hashCode() + (priority * 31);
+    }
+
+    /**
+     * Return a string representation of this instance.
+     *
+     * @return  A string representation of this instance.
+     */
+    @Override
+    public String toString() {
+        StringBuilder builder = new StringBuilder("[service=");
+        return builder.append(service).append(", priority=").append(priority).
+            append(']').toString();
+    }
+}
index 12de35f53677492581412017e9c763de96a05c0b..356c0e57c887f29f5d448ba523976f8fe077b5da 100644 (file)
@@ -1,6 +1,5 @@
-
 /*
- * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ * Copyright (c) 2013-2014 Cisco Systems, Inc. and others.  All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
@@ -43,7 +42,6 @@ import org.opendaylight.controller.sal.reader.NodeConnectorStatistics;
 import org.opendaylight.controller.sal.reader.NodeDescription;
 import org.opendaylight.controller.sal.reader.NodeTableStatistics;
 import org.opendaylight.controller.sal.utils.EtherTypes;
-import org.opendaylight.controller.sal.utils.GlobalConstants;
 import org.opendaylight.controller.sal.utils.IPProtocols;
 import org.opendaylight.controller.sal.utils.NodeConnectorCreator;
 import org.opendaylight.controller.sal.utils.NodeCreator;
@@ -60,8 +58,8 @@ import org.slf4j.LoggerFactory;
 public class ReadService implements IReadService, CommandProvider, IPluginOutReadService {
 
     protected static final Logger logger = LoggerFactory.getLogger(ReadService.class);
-    private ConcurrentHashMap<String, IPluginInReadService> pluginReader =
-        new ConcurrentHashMap<String, IPluginInReadService>();
+    private ConcurrentHashMap<String, ProtocolService<IPluginInReadService>> pluginReader =
+        new ConcurrentHashMap<String, ProtocolService<IPluginInReadService>>();
     private Set<IReadServiceListener> readerListeners =
         new CopyOnWriteArraySet<IReadServiceListener>();
 
@@ -107,58 +105,13 @@ public class ReadService implements IReadService, CommandProvider, IPluginOutRea
 
     // Set the reference to the plugin flow Reader service
     public void setService(Map<?, ?> props, IPluginInReadService s) {
-        if (this.pluginReader == null) {
-            logger.error("pluginReader store null");
-            return;
-        }
-
-        logger.trace("Got a service set request {}", s);
-        String type = null;
-        for (Object e : props.entrySet()) {
-            Map.Entry<?, ?> entry = (Map.Entry<?, ?>) e;
-            logger.trace("Prop key:({}) value:({})", entry.getKey(),
-                    entry.getValue());
-        }
-
-        Object value = props.get(GlobalConstants.PROTOCOLPLUGINTYPE.toString());
-        if (value instanceof String) {
-            type = (String) value;
-        }
-        if (type == null) {
-            logger.error("Received a pluginReader without any "
-                    + "protocolPluginType provided");
-        } else {
-            this.pluginReader.put(type, s);
-            logger.debug("Stored the pluginReader for type: {}", type);
-        }
+        ProtocolService.set(this.pluginReader, props, s, logger);
     }
 
     public void unsetService(Map<?, ?> props, IPluginInReadService s) {
-        if (this.pluginReader == null) {
-            logger.error("pluginReader store null");
-            return;
-        }
-
-        String type = null;
-        logger.debug("Received unsetpluginReader request");
-        for (Object e : props.entrySet()) {
-            Map.Entry<?, ?> entry = (Map.Entry<?, ?>) e;
-            logger.trace("Prop key:({}) value:({})", entry.getKey(),
-                    entry.getValue());
-        }
-
-        Object value = props.get(GlobalConstants.PROTOCOLPLUGINTYPE.toString());
-        if (value instanceof String) {
-            type = (String) value;
-        }
-        if (type == null) {
-            logger.error("Received a pluginReader without any "
-                    + "protocolPluginType provided");
-        } else if (this.pluginReader.get(type).equals(s)) {
-            this.pluginReader.remove(type);
-            logger.debug("Removed the pluginReader for type: {}", type);
-        }
+        ProtocolService.unset(this.pluginReader, props, s, logger);
     }
+
     public void setReaderListener(IReadServiceListener service) {
         logger.trace("Got a listener set request {}", service);
         this.readerListeners.add(service);
@@ -172,9 +125,10 @@ public class ReadService implements IReadService, CommandProvider, IPluginOutRea
     @Override
     public FlowOnNode readFlow(Node node, Flow flow) {
         if (pluginReader != null) {
-            if (this.pluginReader.get(node.getType()) != null) {
-                return this.pluginReader.get(node.getType())
-                        .readFlow(node, flow, true);
+            ProtocolService<IPluginInReadService> service =
+                this.pluginReader.get(node.getType());
+            if (service != null) {
+                return service.getService().readFlow(node, flow, true);
             }
         }
         logger.warn("Plugin {} unavailable", node.getType());
@@ -184,9 +138,10 @@ public class ReadService implements IReadService, CommandProvider, IPluginOutRea
     @Override
     public FlowOnNode nonCachedReadFlow(Node node, Flow flow) {
         if (pluginReader != null) {
-            if (this.pluginReader.get(node.getType()) != null) {
-                return this.pluginReader.get(node.getType())
-                        .readFlow(node, flow, false);
+            ProtocolService<IPluginInReadService> service =
+                this.pluginReader.get(node.getType());
+            if (service != null) {
+                return service.getService().readFlow(node, flow, false);
             }
         }
         logger.warn("Plugin {} unavailable", node.getType());
@@ -196,9 +151,10 @@ public class ReadService implements IReadService, CommandProvider, IPluginOutRea
     @Override
     public List<FlowOnNode> readAllFlows(Node node) {
         if (pluginReader != null) {
-            if (this.pluginReader.get(node.getType()) != null) {
-                return this.pluginReader.get(node.getType())
-                        .readAllFlow(node, true);
+            ProtocolService<IPluginInReadService> service =
+                this.pluginReader.get(node.getType());
+            if (service != null) {
+                return service.getService().readAllFlow(node, true);
             }
         }
         logger.warn("Plugin {} unavailable", node.getType());
@@ -208,9 +164,10 @@ public class ReadService implements IReadService, CommandProvider, IPluginOutRea
     @Override
     public List<FlowOnNode> nonCachedReadAllFlows(Node node) {
         if (pluginReader != null) {
-            if (this.pluginReader.get(node.getType()) != null) {
-                return this.pluginReader.get(node.getType())
-                        .readAllFlow(node, false);
+            ProtocolService<IPluginInReadService> service =
+                this.pluginReader.get(node.getType());
+            if (service != null) {
+                return service.getService().readAllFlow(node, false);
             }
         }
         logger.warn("Plugin {} unavailable", node.getType());
@@ -220,9 +177,10 @@ public class ReadService implements IReadService, CommandProvider, IPluginOutRea
     @Override
     public NodeDescription readDescription(Node node) {
         if (pluginReader != null) {
-            if (this.pluginReader.get(node.getType()) != null) {
-                return this.pluginReader.get(node.getType())
-                        .readDescription(node, true);
+            ProtocolService<IPluginInReadService> service =
+                this.pluginReader.get(node.getType());
+            if (service != null) {
+                return service.getService().readDescription(node, true);
             }
         }
         logger.warn("Plugin {} unavailable", node.getType());
@@ -232,9 +190,10 @@ public class ReadService implements IReadService, CommandProvider, IPluginOutRea
     @Override
     public NodeDescription nonCachedReadDescription(Node node) {
         if (pluginReader != null) {
-            if (this.pluginReader.get(node.getType()) != null) {
-                return this.pluginReader.get(node.getType())
-                        .readDescription(node, false);
+            ProtocolService<IPluginInReadService> service =
+                this.pluginReader.get(node.getType());
+            if (service != null) {
+                return service.getService().readDescription(node, false);
             }
         }
         logger.warn("Plugin {} unavailable", node.getType());
@@ -245,9 +204,10 @@ public class ReadService implements IReadService, CommandProvider, IPluginOutRea
     public NodeConnectorStatistics readNodeConnector(NodeConnector connector) {
         Node node = connector.getNode();
         if (pluginReader != null && node != null) {
-            if (this.pluginReader.get(node.getType()) != null) {
-                return this.pluginReader.get(node.getType())
-                        .readNodeConnector(connector, true);
+            ProtocolService<IPluginInReadService> service =
+                this.pluginReader.get(node.getType());
+            if (service != null) {
+                return service.getService().readNodeConnector(connector, true);
             }
         }
         logger.warn("Plugin {} unavailable", node.getType());
@@ -259,9 +219,10 @@ public class ReadService implements IReadService, CommandProvider, IPluginOutRea
             NodeConnector connector) {
         Node node = connector.getNode();
         if (pluginReader != null && node != null) {
-            if (this.pluginReader.get(node.getType()) != null) {
-                return this.pluginReader.get(node.getType())
-                        .readNodeConnector(connector, false);
+            ProtocolService<IPluginInReadService> service =
+                this.pluginReader.get(node.getType());
+            if (service != null) {
+                return service.getService().readNodeConnector(connector, false);
             }
         }
         logger.warn("Plugin {} unavailable", node.getType());
@@ -271,9 +232,10 @@ public class ReadService implements IReadService, CommandProvider, IPluginOutRea
     @Override
     public List<NodeConnectorStatistics> readNodeConnectors(Node node) {
         if (pluginReader != null) {
-            if (this.pluginReader.get(node.getType()) != null) {
-                return this.pluginReader.get(node.getType())
-                        .readAllNodeConnector(node, true);
+            ProtocolService<IPluginInReadService> service =
+                this.pluginReader.get(node.getType());
+            if (service != null) {
+                return service.getService().readAllNodeConnector(node, true);
             }
         }
         logger.warn("Plugin {} unavailable", node.getType());
@@ -283,9 +245,10 @@ public class ReadService implements IReadService, CommandProvider, IPluginOutRea
     @Override
     public List<NodeTableStatistics> readNodeTable(Node node) {
         if (pluginReader != null) {
-            if (this.pluginReader.get(node.getType()) != null) {
-                return this.pluginReader.get(node.getType())
-                        .readAllNodeTable(node, true);
+            ProtocolService<IPluginInReadService> service =
+                this.pluginReader.get(node.getType());
+            if (service != null) {
+                return service.getService().readAllNodeTable(node, true);
             }
         }
         logger.warn("Plugin {} unavailable", node.getType());
@@ -297,9 +260,10 @@ public class ReadService implements IReadService, CommandProvider, IPluginOutRea
     public NodeTableStatistics nonCachedReadNodeTable(NodeTable table) {
         Node node = table.getNode();
         if (pluginReader != null && node != null) {
-            if (this.pluginReader.get(node.getType()) != null) {
-                return this.pluginReader.get(node.getType())
-                        .readNodeTable(table, false);
+            ProtocolService<IPluginInReadService> service =
+                this.pluginReader.get(node.getType());
+            if (service != null) {
+                return service.getService().readNodeTable(table, false);
             }
         }
         logger.warn("Plugin {} unavailable", node.getType());
@@ -310,9 +274,10 @@ public class ReadService implements IReadService, CommandProvider, IPluginOutRea
     public NodeTableStatistics readNodeTable(NodeTable table) {
         Node node = table.getNode();
         if (pluginReader != null && node != null) {
-            if (this.pluginReader.get(node.getType()) != null) {
-                return this.pluginReader.get(node.getType())
-                        .readNodeTable(table, true);
+            ProtocolService<IPluginInReadService> service =
+                this.pluginReader.get(node.getType());
+            if (service != null) {
+                return service.getService().readNodeTable(table, true);
             }
         }
         logger.warn("Plugin {} unavailable", node.getType());
@@ -322,9 +287,10 @@ public class ReadService implements IReadService, CommandProvider, IPluginOutRea
     @Override
     public List<NodeConnectorStatistics> nonCachedReadNodeConnectors(Node node) {
         if (pluginReader != null) {
-            if (this.pluginReader.get(node.getType()) != null) {
-                return this.pluginReader.get(node.getType())
-                        .readAllNodeConnector(node, false);
+            ProtocolService<IPluginInReadService> service =
+                this.pluginReader.get(node.getType());
+            if (service != null) {
+                return service.getService().readAllNodeConnector(node, false);
             }
         }
         logger.warn("Plugin {} unavailable", node.getType());
@@ -335,9 +301,10 @@ public class ReadService implements IReadService, CommandProvider, IPluginOutRea
     public long getTransmitRate(NodeConnector connector) {
         Node node = connector.getNode();
         if (pluginReader != null && node != null) {
-            if (this.pluginReader.get(node.getType()) != null) {
-                return this.pluginReader.get(node.getType())
-                        .getTransmitRate(connector);
+            ProtocolService<IPluginInReadService> service =
+                this.pluginReader.get(node.getType());
+            if (service != null) {
+                return service.getService().getTransmitRate(connector);
             }
         }
         logger.warn("Plugin {} unavailable", node.getType());
diff --git a/opendaylight/sal/implementation/src/test/java/org/opendaylight/controller/sal/implementation/ProtocolServiceTest.java b/opendaylight/sal/implementation/src/test/java/org/opendaylight/controller/sal/implementation/ProtocolServiceTest.java
new file mode 100644 (file)
index 0000000..079350b
--- /dev/null
@@ -0,0 +1,199 @@
+/*
+ * Copyright (c) 2014 NEC Corporation and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.sal.implementation;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.opendaylight.controller.sal.implementation.internal.ProtocolService;
+import org.opendaylight.controller.sal.utils.GlobalConstants;
+
+/**
+ * Unit test for {@link ProtocolService}.
+ */
+public class ProtocolServiceTest {
+    private static final Logger LOG =
+        LoggerFactory.getLogger(ProtocolServiceTest.class);
+
+    @Test
+    public void testInstance() {
+        HashSet<ProtocolService<ITestService>> set = new HashSet<>();
+        TestService sv1 = new TestService();
+        HashMap<String, Object> prop1 = new HashMap<>();
+
+        ProtocolService<ITestService> ps1 =
+            new ProtocolService<ITestService>(prop1, sv1);
+        assertEquals(sv1, ps1.getService());
+        // Default priority is 0.
+        assertEquals(0, ps1.getPriority());
+        assertTrue(set.add(ps1));
+        assertFalse(set.add(ps1));
+
+        // Specify the same service and priority.
+        String priKey = GlobalConstants.PROTOCOLPLUGINPRIORITY.toString();
+        prop1.put(priKey, Integer.valueOf(0));
+        ProtocolService<ITestService> ps2 =
+            new ProtocolService<ITestService>(prop1, sv1);
+        assertEquals(sv1, ps2.getService());
+        assertEquals(0, ps2.getPriority());
+        assertEquals(ps1, ps2);
+        assertFalse(set.add(ps1));
+
+        // Specify different priority.
+        prop1.put(priKey, Integer.valueOf(Integer.MAX_VALUE));
+        ps2 = new ProtocolService<ITestService>(prop1, sv1);
+        assertEquals(sv1, ps2.getService());
+        assertEquals(Integer.MAX_VALUE, ps2.getPriority());
+        assertFalse(ps1.equals(ps2));
+        assertTrue(set.add(ps2));
+        assertFalse(set.add(ps2));
+
+        // Specify another service.
+        TestService sv2 = new TestService();
+        prop1.put(priKey, Integer.valueOf(0));
+        ps2 = new ProtocolService<ITestService>(prop1, sv2);
+        assertEquals(sv2, ps2.getService());
+        assertEquals(0, ps2.getPriority());
+        assertFalse(ps1.equals(ps2));
+        assertTrue(set.add(ps2));
+        assertFalse(set.add(ps2));
+    }
+
+    @Test
+    public void testSetUnsetError() {
+        ConcurrentMap<String, ProtocolService<ITestService>> services =
+            new ConcurrentHashMap<>();
+        TestService sv = new TestService();
+        Map<String, Object> props = new HashMap<>();
+
+        // null service.
+        ProtocolService.set(services, props, null, LOG);
+        assertTrue(services.isEmpty());
+
+        ProtocolService.unset(services, props, null, LOG);
+        assertTrue(services.isEmpty());
+
+        // null service property.
+        ProtocolService.set(services, null, sv, LOG);
+        assertTrue(services.isEmpty());
+
+        ProtocolService.unset(services, null, sv, LOG);
+        assertTrue(services.isEmpty());
+
+        // Type is not specified.
+        ProtocolService.set(services, props, sv, LOG);
+        assertTrue(services.isEmpty());
+
+        ProtocolService.unset(services, props, sv, LOG);
+        assertTrue(services.isEmpty());
+
+        // null service map.
+        final String typeKey = GlobalConstants.PROTOCOLPLUGINTYPE.toString();
+        assertEquals(null, props.put(typeKey, "OF"));
+        ProtocolService.set(null, props, sv, LOG);
+        assertTrue(services.isEmpty());
+
+        ProtocolService.unset(null, props, sv, LOG);
+        assertTrue(services.isEmpty());
+    }
+
+    @Test
+    public void testSetUnset() {
+        ConcurrentMap<String, ProtocolService<ITestService>> serviceMap =
+            new ConcurrentHashMap<>();
+        ConcurrentMap<String, ProtocolService<ITestService>> expected =
+            new ConcurrentHashMap<>();
+
+        final String typeKey = GlobalConstants.PROTOCOLPLUGINTYPE.toString();
+        final String priKey = GlobalConstants.PROTOCOLPLUGINPRIORITY.toString();
+        final String[] protocols = {"OF", "PE", "PK"};
+        final int basePri = 0;
+        final int loop = 5;
+
+        // Should override the service if higher priority is specified.
+        for (String proto: protocols) {
+            for (int pri = basePri - loop + 1; pri <= basePri; pri++) {
+                TestService sv = new TestService();
+                Map<String, Object> props = new HashMap<>();
+                assertEquals(null, props.put(typeKey, proto));
+                assertEquals(null, props.put(priKey, Integer.valueOf(pri)));
+                ProtocolService.set(serviceMap, props, sv, LOG);
+
+                ProtocolService<ITestService> service = serviceMap.get(proto);
+                assertNotNull(service);
+                assertEquals(sv, service.getService());
+                assertEquals(pri, service.getPriority());
+
+                ProtocolService<ITestService> service1 =
+                    new ProtocolService<ITestService>(props, sv);
+                expected.put(proto, service1);
+                assertEquals(expected, serviceMap);
+
+                // Unset service request should be ignored if different
+                // parameters are specified.
+                TestService another = new TestService();
+                ProtocolService.unset(serviceMap, props, another, LOG);
+                assertEquals(expected, serviceMap);
+
+                props.put(priKey, Integer.valueOf(Integer.MAX_VALUE));
+                ProtocolService.unset(serviceMap, props, sv, LOG);
+                assertEquals(expected, serviceMap);
+            }
+        }
+
+        // Should reject the set service request if lower priority is specified.
+        for (String proto: protocols) {
+            for (int pri = basePri - loop; pri < basePri; pri++) {
+                TestService sv = new TestService();
+                Map<String, Object> props = new HashMap<>();
+                assertEquals(null, props.put(typeKey, proto));
+                assertEquals(null, props.put(priKey, Integer.valueOf(pri)));
+                ProtocolService.set(serviceMap, props, sv, LOG);
+                assertEquals(expected, serviceMap);
+            }
+        }
+
+        // Unset protocol services.
+        for (String proto: protocols) {
+            ProtocolService<ITestService> service = expected.remove(proto);
+            assertNotNull(service);
+
+            ITestService sv = service.getService();
+            Map<String, Object> props = new HashMap<>();
+            assertEquals(null, props.put(typeKey, proto));
+            assertEquals(null, props.put(priKey, Integer.valueOf(basePri)));
+            ProtocolService.unset(serviceMap, props, sv, LOG);
+            assertEquals(expected, serviceMap);
+
+            // Should be ignored if the specified service does not exist.
+            ProtocolService.unset(serviceMap, props, sv, LOG);
+            assertEquals(expected, serviceMap);
+        }
+
+        assertTrue(serviceMap.isEmpty());
+    }
+}
+
+interface ITestService {
+}
+
+class TestService implements ITestService {
+}