Add DOMRpcProviderService bulk registration 54/87754/32
authorRobert Varga <robert.varga@pantheon.tech>
Sat, 15 Feb 2020 19:53:55 +0000 (20:53 +0100)
committerRobert Varga <nite@hq.sk>
Thu, 1 Oct 2020 13:04:47 +0000 (13:04 +0000)
binding-dom-adapter will need the ability to atomically register
a number of implementations. This patch adds that capability.

JIRA: MDSAL-86
Change-Id: Ia948d449ad83b79ca833d5510a4885493b53e202
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
Signed-off-by: Ilya Igushev <illia.ihushev@pantheon.tech>
dom/mdsal-dom-api/src/main/java/org/opendaylight/mdsal/dom/api/DOMRpcImplementationRegistration.java
dom/mdsal-dom-api/src/main/java/org/opendaylight/mdsal/dom/api/DOMRpcProviderService.java
dom/mdsal-dom-broker/src/main/java/org/opendaylight/mdsal/dom/broker/AbstractDOMRoutingTable.java
dom/mdsal-dom-broker/src/main/java/org/opendaylight/mdsal/dom/broker/DOMRpcRouter.java
dom/mdsal-dom-spi/src/main/java/org/opendaylight/mdsal/dom/spi/ForwardingDOMRpcProviderService.java
dom/mdsal-dom-spi/src/test/java/org/opendaylight/mdsal/dom/spi/AbstractDOMRpcProviderServiceTest.java

index 1e53e9c9d23839847c344ec482dff2b2eae5ed6a..883085e657347aa384a6b5ab604e81f7bd552027 100644 (file)
@@ -15,7 +15,7 @@ import org.opendaylight.yangtools.concepts.ObjectRegistration;
  *
  * @param <T> RPC implementation type
  */
+// FIXME: reconsider usefulness of this capture
 public interface DOMRpcImplementationRegistration<T extends DOMRpcImplementation> extends ObjectRegistration<T> {
-    @Override
-    void close();
+
 }
index 224c6ada2c4f24a3a7ff18ff68caef2adf6418ca..d35bb6f532f91d2236189d4deb32f74dbfc5231d 100644 (file)
@@ -7,8 +7,10 @@
  */
 package org.opendaylight.mdsal.dom.api;
 
+import java.util.Map;
 import java.util.Set;
 import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.concepts.Registration;
 
 /**
  * A {@link DOMService} which allows registration of RPC implementations with a conceptual router. The client
@@ -39,4 +41,14 @@ public interface DOMRpcProviderService extends DOMService {
      */
     @NonNull <T extends DOMRpcImplementation> DOMRpcImplementationRegistration<T>
         registerRpcImplementation(@NonNull T implementation, @NonNull Set<DOMRpcIdentifier> rpcs);
+
+    /**
+     * Register a set of {@link DOMRpcImplementation}s with this service. The registration is performed atomically.
+     *
+     * @param map Map of RPC identifiers and their corresponding implementations
+     * @return A registration object, guaranteed to be non-null
+     * @throws NullPointerException if map is null or contains a null element
+     * @throws IllegalArgumentException if map is empty.
+     */
+    @NonNull Registration registerRpcImplementations(Map<DOMRpcIdentifier, DOMRpcImplementation> map);
 }
index f0fac75ded532a24998ffb75f29cea94247c0293..40bc19463dbbe223cf7f86374cd2bb2ba5de3dc8 100644 (file)
@@ -11,11 +11,13 @@ import static java.util.Objects.requireNonNull;
 
 import com.google.common.annotations.Beta;
 import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.HashMultimap;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableMap.Builder;
 import com.google.common.collect.ListMultimap;
 import com.google.common.collect.Maps;
+import com.google.common.collect.Multimaps;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.EventListener;
@@ -30,6 +32,7 @@ import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
 
 /**
  * Abstract routing table definition for Action and RPC.
+ *
  * @param <I> instance type of RPC or Acton
  * @param <D> identifier type of RPC or Acton
  * @param <M> implementation type of RPC or Acton
@@ -66,18 +69,25 @@ abstract class AbstractDOMRoutingTable<I, D, M, L extends EventListener, K,
             return this;
         }
 
+        final Builder<K, E> mb = ImmutableMap.builder();
+        add(implementation, oprsToAdd, mb);
+
+        return newInstance(mb.build(), schemaContext);
+    }
+
+    private void add(final M implementation, final Set<I> oprsToAdd,
+            final Builder<K, E> tableInputBuilder) {
         // First decompose the identifiers to a multimap
         final ListMultimap<K, D> toAdd = decomposeIdentifiers(oprsToAdd);
 
         // Now iterate over existing entries, modifying them as appropriate...
-        final Builder<K, E> mb = ImmutableMap.builder();
         for (Entry<K, E> re : this.operations.entrySet()) {
             List<D> newOperations = new ArrayList<>(toAdd.removeAll(re.getKey()));
             if (!newOperations.isEmpty()) {
                 final E ne = (E) re.getValue().add(implementation, newOperations);
-                mb.put(re.getKey(), ne);
+                tableInputBuilder.put(re.getKey(), ne);
             } else {
-                mb.put(re);
+                tableInputBuilder.put(re);
             }
         }
 
@@ -89,14 +99,26 @@ abstract class AbstractDOMRoutingTable<I, D, M, L extends EventListener, K,
                 vb.put(i, v);
             }
 
-            final E entry = createOperationEntry(schemaContext, e.getKey(),
-                vb.build());
+            final E entry = createOperationEntry(schemaContext, e.getKey(), vb.build());
             if (entry != null) {
-                mb.put(e.getKey(), entry);
+                tableInputBuilder.put(e.getKey(), entry);
             }
         }
+    }
 
-        return newInstance(mb.build(), schemaContext);
+    AbstractDOMRoutingTable<I, D, M, L, K, E> addAll(final Map<I, M> map) {
+        if (map.isEmpty()) {
+            return this;
+        }
+
+        HashMultimap<M, I> inverted = invertImplementationsMap(map);
+
+        final Builder<K, E> tableInputBuilder = ImmutableMap.builder();
+        for (M impl : inverted.keySet()) {
+            add(impl, inverted.get(impl), tableInputBuilder);
+        }
+
+        return newInstance(tableInputBuilder.build(), schemaContext);
     }
 
     AbstractDOMRoutingTable<I, D, M, L, K, E> remove(final M implementation, final Set<I> instances) {
@@ -125,6 +147,42 @@ abstract class AbstractDOMRoutingTable<I, D, M, L extends EventListener, K,
         return newInstance(b.build(), schemaContext);
     }
 
+
+    private void remove(final M implementation, final Set<I> oprsToRemove,
+                        final Builder<K, E> tableInputBuilder) {
+        final ListMultimap<K, D> toRemove = decomposeIdentifiers(oprsToRemove);
+
+        for (Entry<K, E> e : this.operations.entrySet()) {
+            final List<D> removed = new ArrayList<>(toRemove.removeAll(e.getKey()));
+            if (!removed.isEmpty()) {
+                final E ne = (E) e.getValue().remove(implementation, removed);
+                if (ne != null) {
+                    tableInputBuilder.put(e.getKey(), ne);
+                }
+            } else {
+                tableInputBuilder.put(e);
+            }
+        }
+    }
+
+    AbstractDOMRoutingTable<I, D, M, L, K, E> removeAll(final Map<I, M> map) {
+        if (map.isEmpty()) {
+            return this;
+        }
+        HashMultimap<M, I> inverted = invertImplementationsMap(map);
+
+        final Builder<K, E> tableInputBuilder = ImmutableMap.builder();
+        for (M impl : inverted.keySet()) {
+            remove(impl, inverted.get(impl), tableInputBuilder);
+        }
+
+        return newInstance(tableInputBuilder.build(), schemaContext);
+    }
+
+    HashMultimap<M, I> invertImplementationsMap(final Map<I, M> map) {
+        return Multimaps.invertFrom(Multimaps.forMap(map), HashMultimap.create());
+    }
+
     @VisibleForTesting
     Map<K, Set<D>> getOperations() {
         return Maps.transformValues(operations, AbstractDOMRoutingTableEntry::registeredIdentifiers);
index 53ed7b3d73099deae759ef15b9eef934e7624188..c6f1acaa5aaca8a494690afe2d2c04d17624c3fe 100644 (file)
@@ -7,6 +7,7 @@
  */
 package org.opendaylight.mdsal.dom.broker;
 
+import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Verify.verifyNotNull;
 import static java.util.Objects.requireNonNull;
 
@@ -16,6 +17,7 @@ import com.google.common.collect.Collections2;
 import com.google.common.collect.ImmutableClassToInstanceMap;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableList.Builder;
+import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.MapDifference;
 import com.google.common.collect.MapDifference.ValueDifference;
@@ -141,6 +143,16 @@ public final class DOMRpcRouter extends AbstractRegistration
         listenerNotifier.execute(() -> notifyRemoved(newTable, implementation));
     }
 
+    @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD",
+            justification = "https://github.com/spotbugs/spotbugs/issues/811")
+    private synchronized void removeRpcImplementations(final Map<DOMRpcIdentifier, DOMRpcImplementation> map) {
+        final DOMRpcRoutingTable oldTable = routingTable;
+        final DOMRpcRoutingTable newTable = (DOMRpcRoutingTable) oldTable.removeAll(map);
+        routingTable = newTable;
+
+        listenerNotifier.execute(() -> notifyRemoved(newTable, map.values()));
+    }
+
     @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD",
             justification = "https://github.com/spotbugs/spotbugs/issues/811")
     private synchronized void removeActionImplementation(final DOMActionImplementation implementation,
@@ -172,12 +184,36 @@ public final class DOMRpcRouter extends AbstractRegistration
         }
     }
 
+    @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD",
+            justification = "https://github.com/spotbugs/spotbugs/issues/811")
+    private synchronized void notifyAdded(final DOMRpcRoutingTable newTable,
+            final Collection<? extends DOMRpcImplementation> impls) {
+        for (Registration<?> l : listeners) {
+            for (DOMRpcImplementation impl : impls) {
+                l.addRpc(newTable, impl);
+            }
+        }
+    }
+
+    @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD",
+            justification = "https://github.com/spotbugs/spotbugs/issues/811")
     private synchronized void notifyRemoved(final DOMRpcRoutingTable newTable, final DOMRpcImplementation impl) {
         for (Registration<?> l : listeners) {
             l.removeRpc(newTable, impl);
         }
     }
 
+    @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD",
+            justification = "https://github.com/spotbugs/spotbugs/issues/811")
+    private synchronized void notifyRemoved(final DOMRpcRoutingTable newTable,
+            final Collection<? extends DOMRpcImplementation> impls) {
+        for (Registration<?> l : listeners) {
+            for (DOMRpcImplementation impl : impls) {
+                l.removeRpc(newTable, impl);
+            }
+        }
+    }
+
     private synchronized void notifyActionChanged(final DOMActionRoutingTable newTable,
             final DOMActionImplementation impl) {
         for (ActionRegistration<?> l : actionListeners) {
@@ -461,13 +497,13 @@ public final class DOMRpcRouter extends AbstractRegistration
     private final class RpcProviderServiceFacade implements DOMRpcProviderService {
         @Override
         public <T extends DOMRpcImplementation> DOMRpcImplementationRegistration<T> registerRpcImplementation(
-            final T implementation, final DOMRpcIdentifier... rpcs) {
+                final T implementation, final DOMRpcIdentifier... rpcs) {
             return registerRpcImplementation(implementation, ImmutableSet.copyOf(rpcs));
         }
 
         @Override
         public <T extends DOMRpcImplementation> DOMRpcImplementationRegistration<T> registerRpcImplementation(
-            final T implementation, final Set<DOMRpcIdentifier> rpcs) {
+                final T implementation, final Set<DOMRpcIdentifier> rpcs) {
 
             synchronized (DOMRpcRouter.this) {
                 final DOMRpcRoutingTable oldTable = routingTable;
@@ -484,6 +520,28 @@ public final class DOMRpcRouter extends AbstractRegistration
                 }
             };
         }
+
+        @Override
+        public org.opendaylight.yangtools.concepts.Registration registerRpcImplementations(
+                final Map<DOMRpcIdentifier, DOMRpcImplementation> map) {
+            final ImmutableMap<DOMRpcIdentifier, DOMRpcImplementation> defensive = ImmutableMap.copyOf(map);
+            checkArgument(!map.isEmpty());
+
+            synchronized (DOMRpcRouter.this) {
+                final DOMRpcRoutingTable oldTable = routingTable;
+                final DOMRpcRoutingTable newTable = (DOMRpcRoutingTable) oldTable.addAll(defensive);
+                routingTable = newTable;
+
+                listenerNotifier.execute(() -> notifyAdded(newTable, defensive.values()));
+            }
+
+            return new AbstractRegistration() {
+                @Override
+                protected void removeRegistration() {
+                    removeRpcImplementations(defensive);
+                }
+            };
+        }
     }
 
     static final class OperationInvocation {
index 43bc368ebfc8ccf54f3f00ee9a858a3105d3ea03..96c7a40a7f5432ec112a18a10330e1822f6c3211 100644 (file)
@@ -8,12 +8,14 @@
 package org.opendaylight.mdsal.dom.spi;
 
 import com.google.common.collect.ForwardingObject;
+import java.util.Map;
 import java.util.Set;
 import org.eclipse.jdt.annotation.NonNull;
 import org.opendaylight.mdsal.dom.api.DOMRpcIdentifier;
 import org.opendaylight.mdsal.dom.api.DOMRpcImplementation;
 import org.opendaylight.mdsal.dom.api.DOMRpcImplementationRegistration;
 import org.opendaylight.mdsal.dom.api.DOMRpcProviderService;
+import org.opendaylight.yangtools.concepts.Registration;
 
 /**
  * Utility class which implements {@link DOMRpcProviderService} by forwarding
@@ -34,4 +36,9 @@ public abstract class ForwardingDOMRpcProviderService extends ForwardingObject i
             final T implementation, final Set<DOMRpcIdentifier> types) {
         return delegate().registerRpcImplementation(implementation, types);
     }
+
+    @Override
+    public Registration registerRpcImplementations(final Map<DOMRpcIdentifier, DOMRpcImplementation> map) {
+        return delegate().registerRpcImplementations(map);
+    }
 }
index 60bd7397670196f9e22d62c90690a943e9e28a95..48d758905808c5248b74840201c10a5e27cc8677 100644 (file)
@@ -11,12 +11,14 @@ import static org.junit.Assert.assertEquals;
 import static org.mockito.Mockito.mock;
 import static org.mockito.MockitoAnnotations.initMocks;
 
+import java.util.Map;
 import java.util.Set;
 import org.junit.Test;
 import org.mockito.Mock;
 import org.opendaylight.mdsal.dom.api.DOMRpcIdentifier;
 import org.opendaylight.mdsal.dom.api.DOMRpcImplementation;
 import org.opendaylight.mdsal.dom.api.DOMRpcImplementationRegistration;
+import org.opendaylight.yangtools.concepts.Registration;
 
 public class AbstractDOMRpcProviderServiceTest extends AbstractDOMRpcProviderService {
 
@@ -35,4 +37,9 @@ public class AbstractDOMRpcProviderServiceTest extends AbstractDOMRpcProviderSer
             final T implementation, final Set<DOMRpcIdentifier> rpcs) {
         return domRpcImplementationRegistration;
     }
+
+    @Override
+    public Registration registerRpcImplementations(Map<DOMRpcIdentifier, DOMRpcImplementation> map) {
+        throw new UnsupportedOperationException();
+    }
 }
\ No newline at end of file