From 63e9fad5de185e4721aa1ad370630c4bade1201f Mon Sep 17 00:00:00 2001 From: Robert Varga Date: Sat, 15 Feb 2020 20:53:55 +0100 Subject: [PATCH] Add DOMRpcProviderService bulk registration 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 Signed-off-by: Ilya Igushev --- .../api/DOMRpcImplementationRegistration.java | 4 +- .../mdsal/dom/api/DOMRpcProviderService.java | 12 ++++ .../dom/broker/AbstractDOMRoutingTable.java | 72 +++++++++++++++++-- .../mdsal/dom/broker/DOMRpcRouter.java | 62 +++++++++++++++- .../spi/ForwardingDOMRpcProviderService.java | 7 ++ .../AbstractDOMRpcProviderServiceTest.java | 7 ++ 6 files changed, 153 insertions(+), 11 deletions(-) diff --git a/dom/mdsal-dom-api/src/main/java/org/opendaylight/mdsal/dom/api/DOMRpcImplementationRegistration.java b/dom/mdsal-dom-api/src/main/java/org/opendaylight/mdsal/dom/api/DOMRpcImplementationRegistration.java index 1e53e9c9d2..883085e657 100644 --- a/dom/mdsal-dom-api/src/main/java/org/opendaylight/mdsal/dom/api/DOMRpcImplementationRegistration.java +++ b/dom/mdsal-dom-api/src/main/java/org/opendaylight/mdsal/dom/api/DOMRpcImplementationRegistration.java @@ -15,7 +15,7 @@ import org.opendaylight.yangtools.concepts.ObjectRegistration; * * @param RPC implementation type */ +// FIXME: reconsider usefulness of this capture public interface DOMRpcImplementationRegistration extends ObjectRegistration { - @Override - void close(); + } diff --git a/dom/mdsal-dom-api/src/main/java/org/opendaylight/mdsal/dom/api/DOMRpcProviderService.java b/dom/mdsal-dom-api/src/main/java/org/opendaylight/mdsal/dom/api/DOMRpcProviderService.java index 224c6ada2c..d35bb6f532 100644 --- a/dom/mdsal-dom-api/src/main/java/org/opendaylight/mdsal/dom/api/DOMRpcProviderService.java +++ b/dom/mdsal-dom-api/src/main/java/org/opendaylight/mdsal/dom/api/DOMRpcProviderService.java @@ -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 DOMRpcImplementationRegistration registerRpcImplementation(@NonNull T implementation, @NonNull Set 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 map); } diff --git a/dom/mdsal-dom-broker/src/main/java/org/opendaylight/mdsal/dom/broker/AbstractDOMRoutingTable.java b/dom/mdsal-dom-broker/src/main/java/org/opendaylight/mdsal/dom/broker/AbstractDOMRoutingTable.java index f0fac75ded..40bc19463d 100644 --- a/dom/mdsal-dom-broker/src/main/java/org/opendaylight/mdsal/dom/broker/AbstractDOMRoutingTable.java +++ b/dom/mdsal-dom-broker/src/main/java/org/opendaylight/mdsal/dom/broker/AbstractDOMRoutingTable.java @@ -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 instance type of RPC or Acton * @param identifier type of RPC or Acton * @param implementation type of RPC or Acton @@ -66,18 +69,25 @@ abstract class AbstractDOMRoutingTable mb = ImmutableMap.builder(); + add(implementation, oprsToAdd, mb); + + return newInstance(mb.build(), schemaContext); + } + + private void add(final M implementation, final Set oprsToAdd, + final Builder tableInputBuilder) { // First decompose the identifiers to a multimap final ListMultimap toAdd = decomposeIdentifiers(oprsToAdd); // Now iterate over existing entries, modifying them as appropriate... - final Builder mb = ImmutableMap.builder(); for (Entry re : this.operations.entrySet()) { List 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 addAll(final Map map) { + if (map.isEmpty()) { + return this; + } + + HashMultimap inverted = invertImplementationsMap(map); + + final Builder tableInputBuilder = ImmutableMap.builder(); + for (M impl : inverted.keySet()) { + add(impl, inverted.get(impl), tableInputBuilder); + } + + return newInstance(tableInputBuilder.build(), schemaContext); } AbstractDOMRoutingTable remove(final M implementation, final Set instances) { @@ -125,6 +147,42 @@ abstract class AbstractDOMRoutingTable oprsToRemove, + final Builder tableInputBuilder) { + final ListMultimap toRemove = decomposeIdentifiers(oprsToRemove); + + for (Entry e : this.operations.entrySet()) { + final List 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 removeAll(final Map map) { + if (map.isEmpty()) { + return this; + } + HashMultimap inverted = invertImplementationsMap(map); + + final Builder tableInputBuilder = ImmutableMap.builder(); + for (M impl : inverted.keySet()) { + remove(impl, inverted.get(impl), tableInputBuilder); + } + + return newInstance(tableInputBuilder.build(), schemaContext); + } + + HashMultimap invertImplementationsMap(final Map map) { + return Multimaps.invertFrom(Multimaps.forMap(map), HashMultimap.create()); + } + @VisibleForTesting Map> getOperations() { return Maps.transformValues(operations, AbstractDOMRoutingTableEntry::registeredIdentifiers); diff --git a/dom/mdsal-dom-broker/src/main/java/org/opendaylight/mdsal/dom/broker/DOMRpcRouter.java b/dom/mdsal-dom-broker/src/main/java/org/opendaylight/mdsal/dom/broker/DOMRpcRouter.java index 53ed7b3d73..c6f1acaa5a 100644 --- a/dom/mdsal-dom-broker/src/main/java/org/opendaylight/mdsal/dom/broker/DOMRpcRouter.java +++ b/dom/mdsal-dom-broker/src/main/java/org/opendaylight/mdsal/dom/broker/DOMRpcRouter.java @@ -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 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 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 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 DOMRpcImplementationRegistration registerRpcImplementation( - final T implementation, final DOMRpcIdentifier... rpcs) { + final T implementation, final DOMRpcIdentifier... rpcs) { return registerRpcImplementation(implementation, ImmutableSet.copyOf(rpcs)); } @Override public DOMRpcImplementationRegistration registerRpcImplementation( - final T implementation, final Set rpcs) { + final T implementation, final Set 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 map) { + final ImmutableMap 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 { diff --git a/dom/mdsal-dom-spi/src/main/java/org/opendaylight/mdsal/dom/spi/ForwardingDOMRpcProviderService.java b/dom/mdsal-dom-spi/src/main/java/org/opendaylight/mdsal/dom/spi/ForwardingDOMRpcProviderService.java index 43bc368ebf..96c7a40a7f 100644 --- a/dom/mdsal-dom-spi/src/main/java/org/opendaylight/mdsal/dom/spi/ForwardingDOMRpcProviderService.java +++ b/dom/mdsal-dom-spi/src/main/java/org/opendaylight/mdsal/dom/spi/ForwardingDOMRpcProviderService.java @@ -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 types) { return delegate().registerRpcImplementation(implementation, types); } + + @Override + public Registration registerRpcImplementations(final Map map) { + return delegate().registerRpcImplementations(map); + } } diff --git a/dom/mdsal-dom-spi/src/test/java/org/opendaylight/mdsal/dom/spi/AbstractDOMRpcProviderServiceTest.java b/dom/mdsal-dom-spi/src/test/java/org/opendaylight/mdsal/dom/spi/AbstractDOMRpcProviderServiceTest.java index 60bd739767..48d7589058 100644 --- a/dom/mdsal-dom-spi/src/test/java/org/opendaylight/mdsal/dom/spi/AbstractDOMRpcProviderServiceTest.java +++ b/dom/mdsal-dom-spi/src/test/java/org/opendaylight/mdsal/dom/spi/AbstractDOMRpcProviderServiceTest.java @@ -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 rpcs) { return domRpcImplementationRegistration; } + + @Override + public Registration registerRpcImplementations(Map map) { + throw new UnsupportedOperationException(); + } } \ No newline at end of file -- 2.36.6