*
* @param <T> RPC implementation type
*/
+// FIXME: reconsider usefulness of this capture
public interface DOMRpcImplementationRegistration<T extends DOMRpcImplementation> extends ObjectRegistration<T> {
- @Override
- void close();
+
}
*/
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
*/
@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);
}
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;
/**
* 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
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);
}
}
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) {
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);
*/
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;
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;
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,
}
}
+ @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) {
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;
}
};
}
+
+ @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 {
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
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);
+ }
}
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 {
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