BUG-2383: split out AbstractPeerRoleTracker and IdentifierUtils 94/16094/4
authorRobert Varga <rovarga@cisco.com>
Thu, 5 Mar 2015 20:12:23 +0000 (21:12 +0100)
committerRobert Varga <rovarga@cisco.com>
Fri, 6 Mar 2015 19:01:15 +0000 (20:01 +0100)
Functionality of these two classes is shared between multiple listeners,
so move them out to be shared. Also make sure EffectiveRibInWriter
instantiates its policy tracker.

Change-Id: If885ea283ed223cdffe8c14f777192d02cabffde
Signed-off-by: Robert Varga <rovarga@cisco.com>
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/AbstractPeerRoleTracker.java [new file with mode: 0644]
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/EffectiveRibInWriter.java
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/IdentifierUtils.java [new file with mode: 0644]
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/ImportPolicyPeerTracker.java [new file with mode: 0644]
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/LocRibWriter.java

diff --git a/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/AbstractPeerRoleTracker.java b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/AbstractPeerRoleTracker.java
new file mode 100644 (file)
index 0000000..e6cee53
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2015 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,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.protocol.bgp.rib.impl;
+
+import com.google.common.base.Optional;
+import java.util.Collection;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeListener;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeService;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeIdentifier;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.PeerRole;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.Peer;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.BindingMapping;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+
+/**
+ * Maintains the mapping of PeerId -> Role. Subclasses get notified of changes and can do their
+ * own thing.
+ */
+abstract class AbstractPeerRoleTracker implements AutoCloseable {
+    /**
+     * We are subscribed to our target leaf, but that is a wildcard:
+     *     /bgp-rib/rib/peer/peer-role
+     *
+     * MD-SAL assumption: we are getting one {@link DataTreeCandidate} for each expanded
+     *                    wildcard path, so are searching for a particular key.
+     */
+    private final class PeerRoleListener implements DOMDataTreeChangeListener {
+        @Override
+        public void onDataTreeChanged(final Collection<DataTreeCandidate> changes) {
+            for (DataTreeCandidate tc : changes) {
+                // Obtain the peer's path
+                final YangInstanceIdentifier peerPath = IdentifierUtils.peerPath(tc.getRootPath());
+
+                // Check for removal
+                final Optional<NormalizedNode<?, ?>> maybePeerRole = tc.getRootNode().getDataAfter();
+                final PeerRole role;
+                if (maybePeerRole.isPresent()) {
+                    final LeafNode<?> peerRoleLeaf = (LeafNode<?>) maybePeerRole.get();
+                    // We could go for a coded, but his is simpler and faster
+                    role = PeerRole.valueOf(BindingMapping.getClassName((String) peerRoleLeaf.getValue()));
+                } else {
+                    role = null;
+                }
+
+                peerRoleChanged(peerPath, role);
+            }
+        }
+    }
+
+    private static final QName PEER_ROLE = QName.cachedReference(QName.create(Peer.QNAME, "peer-role"));
+    private final ListenerRegistration<?> registration;
+
+    protected AbstractPeerRoleTracker(final @Nonnull DOMDataTreeChangeService service, @Nonnull final YangInstanceIdentifier ribId) {
+        // Slightly evil, but our users should be fine with this
+        registration = service.registerDataTreeChangeListener(
+            new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, ribId.node(Peer.QNAME).node(PEER_ROLE)),
+            new PeerRoleListener());
+    }
+
+    @Override
+    public void close() {
+        registration.close();
+    }
+
+    /**
+     * Invoked whenever a peer role changes.
+     *
+     * @param peerPath Peer's path
+     * @param role Peer's new role, null indicates the peer has disappeared.
+     */
+    protected abstract void peerRoleChanged(@Nonnull YangInstanceIdentifier peerPath, @Nullable PeerRole role);
+}
index 45a32878649c981d8da3fe7ebb87598ddad565d8..aff22eb37eab9efa55d215accf02e503f11ab21e 100644 (file)
@@ -7,10 +7,7 @@
  */
 package org.opendaylight.protocol.bgp.rib.impl;
 
-import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
-import com.google.common.base.Predicate;
-import com.google.common.collect.Iterables;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.Map;
@@ -24,17 +21,10 @@ import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
 import org.opendaylight.protocol.bgp.rib.spi.RIBExtensionConsumerContext;
 import org.opendaylight.protocol.bgp.rib.spi.RIBSupport;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.PeerId;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.PeerRole;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.Peer;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.Tables;
 import org.opendaylight.yangtools.concepts.ListenerRegistration;
-import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
-import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
-import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes;
 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
@@ -56,76 +46,9 @@ import org.slf4j.LoggerFactory;
  * structures. This is done so we maintain causality and loose coupling.
  */
 @NotThreadSafe
-final class EffectiveRibInWriter {
-    private static final Predicate<PathArgument> IS_PEER = new Predicate<PathArgument>() {
-        @Override
-        public boolean apply(final PathArgument input) {
-            return input.getNodeType().equals(Peer.QNAME);
-        }
-    };
-    private static final Predicate<PathArgument> IS_TABLES = new Predicate<PathArgument>() {
-        @Override
-        public boolean apply(final PathArgument input) {
-            return input.getNodeType().equals(Tables.QNAME);
-        }
-    };
-    private static final Logger LOG = LoggerFactory.getLogger(EffectiveRibInWriter.class);
-    private static final QName PEER_ID = QName.create(Peer.QNAME, "peer-id");
-
-    // FIXME: implement as id.firstIdentifierOf(IS_PEER), null indicating not found
-    private static final NodeIdentifierWithPredicates firstKeyOf(final YangInstanceIdentifier id, final Predicate<PathArgument> match) {
-        final PathArgument ret = Iterables.find(id.getPathArguments(), IS_PEER);
-        Preconditions.checkArgument(ret instanceof NodeIdentifierWithPredicates, "Non-key peer identifier %s", ret);
-        return (NodeIdentifierWithPredicates) ret;
-    }
+final class EffectiveRibInWriter implements AutoCloseable {
 
-    static final NodeIdentifierWithPredicates peerKey(final YangInstanceIdentifier id) {
-        return firstKeyOf(id, IS_PEER);
-    }
-
-    static final PeerId peerId(final NodeIdentifierWithPredicates peerKey) {
-        return (PeerId) peerKey.getKeyValues().get(PEER_ID);
-    }
-
-    private static final NodeIdentifierWithPredicates tableKey(final YangInstanceIdentifier id) {
-        return firstKeyOf(id, IS_TABLES);
-    }
-
-    /**
-     * Maintains the mapping of PeerId -> Role inside. We are subscribed to our target leaf,
-     * but that is a wildcard:
-     *     /bgp-rib/rib/peer/peer-role
-     *
-     * MD-SAL assumption: we are getting one {@link DataTreeCandidate} for each expanded
-     *                    wildcard path, so are searching for a particular key.
-     */
-    private final class PeerRoleListener implements DOMDataTreeChangeListener {
-        @Override
-        public void onDataTreeChanged(final Collection<DataTreeCandidate> changes) {
-            synchronized (policies) {
-                for (DataTreeCandidate tc : changes) {
-                    // Obtain the peer's key
-                    final NodeIdentifierWithPredicates peerKey = peerKey(tc.getRootPath());
-
-                    // Check for removal
-                    final Optional<NormalizedNode<?, ?>> maybePeerRole = tc.getRootNode().getDataAfter();
-                    if (maybePeerRole.isPresent()) {
-                        final LeafNode<?> peerRoleLeaf = (LeafNode<?>) maybePeerRole.get();
-                        // FIXME: need codec here
-                        final PeerRole peerRole = (PeerRole) peerRoleLeaf.getValue();
-
-                        // Lookup policy based on role
-                        final AbstractImportPolicy policy = AbstractImportPolicy.forRole(peerRole);
-
-                        // Update lookup map
-                        policies.put(peerId(peerKey), policy);
-                    } else {
-                        policies.remove(peerId(peerKey));
-                    }
-                }
-            }
-        }
-    }
+    private static final Logger LOG = LoggerFactory.getLogger(EffectiveRibInWriter.class);
 
     /**
      * Maintains the individual routes for a particular table's routes under:
@@ -142,7 +65,7 @@ final class EffectiveRibInWriter {
             this.tableKey = Preconditions.checkNotNull(tableKey);
 
             // Lookup peer ID
-            this.peerId = (PeerId) Preconditions.checkNotNull(peerKey.getKeyValues().get(PEER_ID));
+            this.peerId = IdentifierUtils.peerId(peerKey);
 
             // FIXME: need target table ID
             target = null;
@@ -172,10 +95,8 @@ final class EffectiveRibInWriter {
                 final ContainerNode effectiveAttrs;
 
                 if (adverisedAttrs != null && tc.getRootNode().getDataAfter().isPresent()) {
-                    synchronized (policies) {
-                        final AbstractImportPolicy policy = policies.get(peerId);
-                        effectiveAttrs = policy.effectiveAttributes(adverisedAttrs);
-                    }
+                    final AbstractImportPolicy policy = peerPolicyTracker.policyFor(peerId);
+                    effectiveAttrs = policy.effectiveAttributes(adverisedAttrs);
                 } else {
                     effectiveAttrs = null;
                 }
@@ -207,10 +128,10 @@ final class EffectiveRibInWriter {
 
             for (DataTreeCandidate tc : changes) {
                 // Obtain the peer's key
-                final NodeIdentifierWithPredicates peerKey = peerKey(tc.getRootPath());
+                final NodeIdentifierWithPredicates peerKey = IdentifierUtils.peerKey(tc.getRootPath());
 
                 // Lookup
-                final NodeIdentifierWithPredicates tableKey = tableKey(tc.getRootPath());
+                final NodeIdentifierWithPredicates tableKey = IdentifierUtils.tableKey(tc.getRootPath());
 
                 switch (tc.getRootNode().getModificationType()) {
                 case DELETE:
@@ -240,12 +161,19 @@ final class EffectiveRibInWriter {
         }
     }
 
-    private final Map<PeerId, AbstractImportPolicy> policies = new HashMap<>();
+    private final ImportPolicyPeerTracker peerPolicyTracker;
     private final DOMTransactionChain chain;
 
-    private EffectiveRibInWriter(final DOMTransactionChain chain) {
+    private EffectiveRibInWriter(final DOMDataTreeChangeService service, final DOMTransactionChain chain, final YangInstanceIdentifier ribId) {
         this.chain = Preconditions.checkNotNull(chain);
 
-        // FIXME: subscribe peerRoleListener, tableListener
+        this.peerPolicyTracker = new ImportPolicyPeerTracker(service, ribId);
+
+        // FIXME: subscribe tableListener
+    }
+
+    @Override
+    public void close() {
+        peerPolicyTracker.close();
     }
 }
diff --git a/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/IdentifierUtils.java b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/IdentifierUtils.java
new file mode 100644 (file)
index 0000000..c8e61e6
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2015 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,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.protocol.bgp.rib.impl;
+
+import com.google.common.base.Preconditions;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.PeerId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.bgp.rib.rib.Peer;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.Tables;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+
+final class IdentifierUtils {
+    private static final Predicate<PathArgument> IS_PEER = new Predicate<PathArgument>() {
+        @Override
+        public boolean apply(final PathArgument input) {
+            return input.getNodeType().equals(Peer.QNAME);
+        }
+    };
+    private static final Predicate<PathArgument> IS_TABLES = new Predicate<PathArgument>() {
+        @Override
+        public boolean apply(final PathArgument input) {
+            return input.getNodeType().equals(Tables.QNAME);
+        }
+    };
+    private static final QName PEER_ID = QName.cachedReference(QName.create(Peer.QNAME, "peer-id"));
+
+    private IdentifierUtils() {
+        throw new UnsupportedOperationException();
+    }
+
+    // FIXME: implement as id.firstIdentifierOf(IS_PEER), null indicating not found
+    private static NodeIdentifierWithPredicates firstKeyOf(final YangInstanceIdentifier id, final Predicate<PathArgument> match) {
+        final PathArgument ret = Iterables.find(id.getPathArguments(), match);
+        Preconditions.checkArgument(ret instanceof NodeIdentifierWithPredicates, "Non-key peer identifier %s", ret);
+        return (NodeIdentifierWithPredicates) ret;
+    }
+
+    private static YangInstanceIdentifier firstIdentifierOf(final YangInstanceIdentifier id, final Predicate<PathArgument> match) {
+        final int idx = Iterables.indexOf(id.getPathArguments(), match);
+        Preconditions.checkArgument(idx != -1, "Failed to find %s in %s", match, id);
+        return YangInstanceIdentifier.create(Iterables.limit(id.getPathArguments(), idx));
+    }
+
+    static YangInstanceIdentifier peerPath(final YangInstanceIdentifier id) {
+        return firstIdentifierOf(id, IS_PEER);
+    }
+
+    static NodeIdentifierWithPredicates peerKey(final YangInstanceIdentifier id) {
+        return firstKeyOf(id, IS_PEER);
+    }
+
+    static PeerId peerId(final NodeIdentifierWithPredicates peerKey) {
+        // We could use a codec, but this is simple enough
+        return new PeerId((String) peerKey.getKeyValues().get(PEER_ID));
+    }
+
+    static NodeIdentifierWithPredicates tableKey(final YangInstanceIdentifier id) {
+        return firstKeyOf(id, IS_TABLES);
+    }
+
+}
diff --git a/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/ImportPolicyPeerTracker.java b/bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/ImportPolicyPeerTracker.java
new file mode 100644 (file)
index 0000000..b5f2a00
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2015 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,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.protocol.bgp.rib.impl;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.PeerId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.PeerRole;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+
+/**
+ * Tracks import policy corresponding to a particular peer.
+ */
+final class ImportPolicyPeerTracker extends AbstractPeerRoleTracker {
+    private final Map<PeerId, AbstractImportPolicy> policies = new ConcurrentHashMap<>();
+
+    protected ImportPolicyPeerTracker(final DOMDataTreeChangeService service, final YangInstanceIdentifier ribId) {
+        super(service, ribId);
+    }
+
+    @Override
+    protected void peerRoleChanged(final YangInstanceIdentifier peerPath, final PeerRole role) {
+        final PeerId peer = IdentifierUtils.peerId((NodeIdentifierWithPredicates) peerPath.getLastPathArgument());
+
+        if (role != null) {
+            // Lookup policy based on role
+            final AbstractImportPolicy policy = AbstractImportPolicy.forRole(role);
+
+            // Update lookup map
+            policies.put(peer, policy);
+        } else {
+            policies.remove(peer);
+        }
+    }
+
+    AbstractImportPolicy policyFor(final PeerId peerId) {
+        return policies.get(peerId);
+    }
+}
\ No newline at end of file
index 7f6c35f2318ff88d4476c5c8e17a91f7803d5351..c36ed77d0f048cdcccecd9be6d8d6ee1304c6563 100644 (file)
@@ -59,8 +59,8 @@ final class LocRibWriter implements DOMDataTreeChangeListener {
         for (DataTreeCandidate tc : changes) {
             final YangInstanceIdentifier path = tc.getRootPath();
             final PathArgument routeId = path.getLastPathArgument();
-            final NodeIdentifierWithPredicates peerKey = EffectiveRibInWriter.peerKey(path);
-            final PeerId peerId = EffectiveRibInWriter.peerId(peerKey);
+            final NodeIdentifierWithPredicates peerKey = IdentifierUtils.peerKey(path);
+            final PeerId peerId = IdentifierUtils.peerId(peerKey);
             final UnsignedInteger routerId = RouterIds.routerIdForPeerId(peerId);
 
             RouteEntry entry = routeEntries.get(routeId);