Refactor NodeListener 18/110218/1
authorRobert Varga <robert.varga@pantheon.tech>
Sun, 11 Feb 2024 21:46:40 +0000 (22:46 +0100)
committerRobert Varga <robert.varga@pantheon.tech>
Sun, 11 Feb 2024 21:47:40 +0000 (22:47 +0100)
This is an active cache, for the purpose of exposing stuff through CLI.
Refactor it to an interface (DpnTracker) and implementation and activate
it via OSGi DS.

JIRA: OPNFLWPLUG-1112
Change-Id: I20594e4f64dab2d688fe90936844da1793ae43da
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
applications/southbound-cli/pom.xml
applications/southbound-cli/src/main/java/org/opendaylight/openflowplugin/applications/southboundcli/DefaultDpnTracker.java [moved from applications/southbound-cli/src/main/java/org/opendaylight/openflowplugin/applications/southboundcli/NodeListener.java with 67% similarity]
applications/southbound-cli/src/main/java/org/opendaylight/openflowplugin/applications/southboundcli/DpnTracker.java [new file with mode: 0644]
applications/southbound-cli/src/main/java/org/opendaylight/openflowplugin/applications/southboundcli/ReconciliationServiceImpl.java
applications/southbound-cli/src/main/java/org/opendaylight/openflowplugin/applications/southboundcli/cli/GetAllNodesCommandProvider.java
applications/southbound-cli/src/main/java/org/opendaylight/openflowplugin/applications/southboundcli/util/ShellUtil.java
applications/southbound-cli/src/main/resources/OSGI-INF/blueprint/commands.xml
applications/southbound-cli/src/main/resources/OSGI-INF/blueprint/southbound-cli.xml

index cfa72112c2738bd5633eaaf96151b45eebea85ee..7f96952cc51c85c68fadd1cf20d9fdf6b6a8534a 100644 (file)
     <packaging>bundle</packaging>
 
     <dependencies>
+        <dependency>
+            <groupId>com.google.code.gson</groupId>
+            <artifactId>gson</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.guicedee.services</groupId>
+            <artifactId>javax.inject</artifactId>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>jakarta.annotation</groupId>
+            <artifactId>jakarta.annotation-api</artifactId>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.karaf.shell</groupId>
+            <artifactId>org.apache.karaf.shell.console</artifactId>
+            <scope>provided</scope>
+        </dependency>
         <dependency>
             <groupId>org.opendaylight.mdsal</groupId>
             <artifactId>mdsal-binding-api</artifactId>
             <groupId>org.opendaylight.openflowplugin.applications</groupId>
             <artifactId>forwardingrules-manager</artifactId>
         </dependency>
-        <dependency>
-            <groupId>com.google.code.gson</groupId>
-            <artifactId>gson</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.karaf.shell</groupId>
-            <artifactId>org.apache.karaf.shell.console</artifactId>
-            <scope>provided</scope>
-        </dependency>
         <dependency>
             <groupId>org.osgi</groupId>
             <artifactId>org.osgi.framework</artifactId>
         </dependency>
         <dependency>
-            <groupId>com.guicedee.services</groupId>
-            <artifactId>javax.inject</artifactId>
-            <optional>true</optional>
-        </dependency>
-        <dependency>
-            <groupId>jakarta.annotation</groupId>
-            <artifactId>jakarta.annotation-api</artifactId>
-            <optional>true</optional>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.service.component.annotations</artifactId>
         </dependency>
 
         <dependency>
@@ -7,60 +7,76 @@
  */
 package org.opendaylight.openflowplugin.applications.southboundcli;
 
-import static java.util.Objects.requireNonNull;
-
+import java.util.ArrayList;
 import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
+import javax.annotation.PreDestroy;
+import javax.inject.Inject;
+import javax.inject.Singleton;
 import org.opendaylight.mdsal.binding.api.ClusteredDataTreeChangeListener;
 import org.opendaylight.mdsal.binding.api.DataBroker;
-import org.opendaylight.mdsal.binding.api.DataObjectModification;
 import org.opendaylight.mdsal.binding.api.DataTreeIdentifier;
 import org.opendaylight.mdsal.binding.api.DataTreeModification;
 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
+import org.opendaylight.openflowplugin.applications.southboundcli.util.OFNode;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
 import org.opendaylight.yangtools.concepts.ListenerRegistration;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
+import org.osgi.service.component.annotations.Reference;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-public class NodeListener implements ClusteredDataTreeChangeListener<FlowCapableNode>, AutoCloseable {
-    private static final Logger LOG = LoggerFactory.getLogger(NodeListener.class);
+@Singleton
+@Component(service = DpnTracker.class)
+public final class DefaultDpnTracker
+        implements DpnTracker, ClusteredDataTreeChangeListener<FlowCapableNode>, AutoCloseable {
+    private static final Logger LOG = LoggerFactory.getLogger(DefaultDpnTracker.class);
     public static final String DEFAULT_DPN_NAME = "UNKNOWN";
     public static final String SEPARATOR = ":";
 
-    private final Map<Long, String> dpnIdToNameCache = new ConcurrentHashMap<>();
-    private final DataBroker dataBroker;
-    private ListenerRegistration<?> listenerReg;
+    private final Map<Long, String> dpnIdToNameCache = new HashMap<>();
+    private final ListenerRegistration<?> listenerReg;
 
-    public NodeListener(final DataBroker broker) {
-        dataBroker = broker;
+    @Inject
+    @Activate
+    public DefaultDpnTracker(@Reference final DataBroker dataBroker) {
+        listenerReg = dataBroker.registerDataTreeChangeListener(
+            DataTreeIdentifier.create(LogicalDatastoreType.OPERATIONAL,
+                InstanceIdentifier.create(Nodes.class).child(Node.class).augmentation(FlowCapableNode.class)), this);
     }
 
-    public void start() {
-        final InstanceIdentifier<FlowCapableNode> path = InstanceIdentifier.create(Nodes.class).child(Node.class)
-                .augmentation(FlowCapableNode.class);
-        final DataTreeIdentifier<FlowCapableNode> identifier =
-                DataTreeIdentifier.create(LogicalDatastoreType.OPERATIONAL, path);
-        listenerReg = dataBroker.registerDataTreeChangeListener(identifier, NodeListener.this);
+    @PreDestroy
+    @Deactivate
+    @Override
+    public void close() {
+        listenerReg.close();
     }
 
     @Override
-    public void close() {
-        if (listenerReg != null) {
-            listenerReg.close();
+    public synchronized List<OFNode> currentNodes() {
+        final var dpnList = new ArrayList<OFNode>();
+        for (var entry : dpnIdToNameCache.entrySet()) {
+            final var dpn = new OFNode(entry.getKey(), entry.getValue());
+            dpnList.add(dpn);
+            LOG.trace("Added OFNode: {} to the list", dpn.getNodeId());
         }
+        dpnList.sort(null);
+        return dpnList;
     }
 
     @Override
-    public void onDataTreeChanged(final Collection<DataTreeModification<FlowCapableNode>> changes) {
-        requireNonNull(changes, "Changes may not be null!");
-        for (DataTreeModification<FlowCapableNode> change : changes) {
-            final InstanceIdentifier<FlowCapableNode> key = change.getRootPath().getRootIdentifier();
-            final DataObjectModification<FlowCapableNode> mod = change.getRootNode();
-            final InstanceIdentifier<FlowCapableNode> nodeIdent = key.firstIdentifierOf(FlowCapableNode.class);
+    public synchronized void onDataTreeChanged(final Collection<DataTreeModification<FlowCapableNode>> changes) {
+        for (var change : changes) {
+            final var key = change.getRootPath().getRootIdentifier();
+            final var mod = change.getRootNode();
+            final var nodeIdent = key.firstIdentifierOf(FlowCapableNode.class);
             switch (mod.getModificationType()) {
                 case DELETE:
                     remove(nodeIdent, mod.getDataBefore());
@@ -89,8 +105,7 @@ public class NodeListener implements ClusteredDataTreeChangeListener<FlowCapable
                     .getValue());
             return;
         }
-        long dpnId = Long.parseLong(node[1]);
-        dpnIdToNameCache.remove(dpnId);
+        dpnIdToNameCache.remove(Long.parseLong(node[1]));
     }
 
     private void update(final InstanceIdentifier<FlowCapableNode> instId,
@@ -126,8 +141,4 @@ public class NodeListener implements ClusteredDataTreeChangeListener<FlowCapable
         }
         dpnIdToNameCache.put(dpnId, dpnName);
     }
-
-    public Map<Long, String> getDpnIdToNameCache() {
-        return dpnIdToNameCache;
-    }
 }
\ No newline at end of file
diff --git a/applications/southbound-cli/src/main/java/org/opendaylight/openflowplugin/applications/southboundcli/DpnTracker.java b/applications/southbound-cli/src/main/java/org/opendaylight/openflowplugin/applications/southboundcli/DpnTracker.java
new file mode 100644 (file)
index 0000000..f10d9e4
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2024 PANTHEON.tech, s.r.o. 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.openflowplugin.applications.southboundcli;
+
+import java.util.List;
+import org.opendaylight.openflowplugin.applications.southboundcli.util.OFNode;
+
+public interface DpnTracker {
+
+    List<OFNode> currentNodes();
+}
index 0f8faf05030bf8f6149cf303480a4c6862dd28ba..2ba7caa548708f4c039e62f1d8472fae995b1071 100644 (file)
@@ -43,7 +43,6 @@ import org.opendaylight.openflowplugin.applications.frm.FlowNodeReconciliation;
 import org.opendaylight.openflowplugin.applications.frm.ForwardingRulesManager;
 import org.opendaylight.openflowplugin.applications.southboundcli.alarm.NodeReconciliationAlarm;
 import org.opendaylight.openflowplugin.applications.southboundcli.util.OFNode;
-import org.opendaylight.openflowplugin.applications.southboundcli.util.ShellUtil;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.DateAndTime;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
@@ -82,19 +81,19 @@ public final class ReconciliationServiceImpl implements ReconciliationService, A
     }
 
     private final NodeReconciliationAlarm alarm = new NodeReconciliationAlarm();
-    private final DataBroker broker;
-    private final FlowNodeReconciliation flowNodeReconciliation;
-    private final NodeListener nodeListener;
     private final Map<String, ReconciliationState> reconciliationStates;
+    private final FlowNodeReconciliation flowNodeReconciliation;
+    private final DpnTracker dpnTracker;
+    private final DataBroker broker;
 
     private ExecutorService executor = Executors.newWorkStealingPool(10);
     private boolean unregister;
 
     public ReconciliationServiceImpl(final DataBroker broker, final ForwardingRulesManager frm,
-            final NodeListener nodeListener, final FlowGroupCacheManager flowGroupCacheManager) {
+            final DpnTracker dpnTracker, final FlowGroupCacheManager flowGroupCacheManager) {
         this.broker = requireNonNull(broker);
         flowNodeReconciliation = frm.getFlowNodeReconciliation();
-        this.nodeListener = requireNonNull(nodeListener);
+        this.dpnTracker = requireNonNull(dpnTracker);
         reconciliationStates = flowGroupCacheManager.getReconciliationStates();
 
         unregister = false;
@@ -191,9 +190,7 @@ public final class ReconciliationServiceImpl implements ReconciliationService, A
     }
 
     private List<Long> getAllNodes() {
-        List<OFNode> nodeList = ShellUtil.getAllNodes(nodeListener);
-        List<Long> nodes = nodeList.stream().distinct().map(OFNode::getNodeId).collect(Collectors.toList());
-        return nodes;
+        return dpnTracker.currentNodes().stream().distinct().map(OFNode::getNodeId).collect(Collectors.toList());
     }
 
     /**
index 5653acb17d0a7f673125708619ca21338168ea61..65c643a220afdaac43c4dcff5a0bcfe371920a6d 100644 (file)
@@ -7,48 +7,48 @@
  */
 package org.opendaylight.openflowplugin.applications.southboundcli.cli;
 
+import static java.util.Objects.requireNonNull;
+
 import java.util.Formatter;
-import java.util.List;
 import org.apache.felix.gogo.commands.Command;
 import org.apache.karaf.shell.console.OsgiCommandSupport;
-import org.opendaylight.openflowplugin.applications.southboundcli.NodeListener;
+import org.opendaylight.openflowplugin.applications.southboundcli.DpnTracker;
 import org.opendaylight.openflowplugin.applications.southboundcli.util.OFNode;
-import org.opendaylight.openflowplugin.applications.southboundcli.util.ShellUtil;
 
 @Command(scope = "openflow", name = "getallnodes", description = "Print all nodes from the operational datastore")
 public class GetAllNodesCommandProvider extends OsgiCommandSupport {
-    private NodeListener nodeListener;
+    private final DpnTracker dpnTracker;
 
-    public void setNodeListener(final NodeListener nodeListener) {
-        this.nodeListener = nodeListener;
+    public GetAllNodesCommandProvider(final DpnTracker dpnTracker) {
+        this.dpnTracker = requireNonNull(dpnTracker);
     }
 
     @SuppressWarnings("checkstyle:RegexpSinglelineJava")
     @Override
     protected Object doExecute() throws Exception {
-        List<OFNode> ofNodeList = ShellUtil.getAllNodes(nodeListener);
+        final var ofNodeList = dpnTracker.currentNodes();
         if (ofNodeList.isEmpty()) {
             System.out.println("No node is connected yet");
-        } else {
-            StringBuilder stringBuilder = new StringBuilder();
-            Formatter formatter = new Formatter(stringBuilder);
+            return null;
+        }
+
+        final var stringBuilder = new StringBuilder();
+        try (var formatter = new Formatter(stringBuilder)) {
             System.out.println("Number of nodes: " + ofNodeList.size());
             System.out.println(getAllLocalNodesHeaderOutput());
             System.out.println("--------------------------------------------------------------------------");
             for (OFNode ofNode : ofNodeList) {
                 System.out.println(formatter.format("%-15s %3s %-15s %n",
-                        ofNode.getNodeId(), "", ofNode.getNodeName()).toString());
+                    ofNode.getNodeId(), "", ofNode.getNodeName()).toString());
                 stringBuilder.setLength(0);
             }
-            formatter.close();
         }
         return null;
     }
 
     private static String getAllLocalNodesHeaderOutput() {
-        Formatter formatter = new Formatter();
-        String header = formatter.format("%-15s %3s %-15s", "NodeId", "", "NodeName").toString();
-        formatter.close();
-        return header;
+        try (var formatter = new Formatter()) {
+            return formatter.format("%-15s %3s %-15s", "NodeId", "", "NodeName").toString();
+        }
     }
 }
\ No newline at end of file
index 09271fba0c051a0214ae77762e8505ec4bae719a..9a91a4fe6b1589aded8a2c9e6455a8170967415d 100644 (file)
@@ -10,15 +10,12 @@ package org.opendaylight.openflowplugin.applications.southboundcli.util;
 
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.Collections;
 import java.util.List;
-import java.util.Map;
 import java.util.Optional;
 import java.util.concurrent.ExecutionException;
 import org.opendaylight.mdsal.binding.api.DataBroker;
 import org.opendaylight.mdsal.binding.api.ReadTransaction;
 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
-import org.opendaylight.openflowplugin.applications.southboundcli.NodeListener;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
@@ -40,17 +37,6 @@ public final class ShellUtil {
     private ShellUtil() {
     }
 
-    public static List<OFNode> getAllNodes(final NodeListener nodeListener) {
-        List<OFNode> dpnList = new ArrayList<>();
-        for (Map.Entry<Long, String> entry : nodeListener.getDpnIdToNameCache().entrySet()) {
-            OFNode dpn = new OFNode(entry.getKey(), entry.getValue());
-            dpnList.add(dpn);
-            LOG.trace("Added OFNode: {} to the list", dpn.getNodeId());
-        }
-        Collections.sort(dpnList);
-        return dpnList;
-    }
-
     public static OFNode getNode(final long nodeId, final DataBroker broker) {
         OFNode nodeInfo = getNodeInfo(nodeId, broker);
         if (nodeInfo == null) {
index 2ab42185d9a9a2c8a00b6676f09cd47e10fed23e..331c6d500cb49fc9c9c711b91aa1aec3d2b05b26 100644 (file)
@@ -20,7 +20,7 @@
     <command-bundle xmlns="http://karaf.apache.org/xmlns/shell/v1.0.0">
         <command name="openflow/getallnodes">
             <action class="org.opendaylight.openflowplugin.applications.southboundcli.cli.GetAllNodesCommandProvider">
-                <property name="nodeListener" ref="nodeListener"/>
+                <argument ref="dpnTracker"/>
             </action>
         </command>
         <command name="openflow/shownode">
index d13cf6e7ff739c824b6f7cd5daa6bd83f5c16eb3..fa3b5bbc4aeabd1e53fcb5f0ddc74fe1e53c5453 100644 (file)
@@ -1,31 +1,24 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
            xmlns:cm="http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.1.0"
-           xmlns:odl="http://opendaylight.org/xmlns/blueprint/v1.0.0"
-           odl:use-default-for-reference-types="true">
-
+           xmlns:odl="http://opendaylight.org/xmlns/blueprint/v1.0.0">
     <reference id="dataBroker"
                interface="org.opendaylight.mdsal.binding.api.DataBroker"/>
     <reference id="flowCacheManager"
                interface="org.opendaylight.openflowplugin.api.openflow.FlowGroupCacheManager"/>
     <reference id="frm"
                interface="org.opendaylight.openflowplugin.applications.frm.ForwardingRulesManager"/>
+    <reference id="dpnTracker"
+               interface="org.opendaylight.openflowplugin.applications.southboundcli.DpnTracker"/>
 
-    <bean id="nodeListener"
-          class="org.opendaylight.openflowplugin.applications.southboundcli.NodeListener"
-          init-method="start"
-          destroy-method="close">
-        <argument ref="dataBroker"/>
-    </bean>
     <bean id="reconciliationService"
           class="org.opendaylight.openflowplugin.applications.southboundcli.ReconciliationServiceImpl"
           destroy-method="close">
         <argument ref="dataBroker"/>
         <argument ref="frm"/>
-        <argument ref="nodeListener"/>
+        <argument ref="dpnTracker"/>
         <argument ref="flowCacheManager"/>
     </bean>
 
     <odl:rpc-implementation ref="reconciliationService"/>
-
 </blueprint>