Proxy MD-SAL interfaces in DOMMountPointServiceImpl
[controller.git] / opendaylight / md-sal / sal-dom-broker / src / main / java / org / opendaylight / controller / md / sal / dom / broker / impl / DOMRpcRouter.java
index daf0d065f8b5435ed025465d4e3a5918d38c844a..b6604232231a9a53d15c343830e40c2a72244961 100644 (file)
@@ -7,26 +7,14 @@
  */
 package org.opendaylight.controller.md.sal.dom.broker.impl;
 
-import com.google.common.collect.Collections2;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableList.Builder;
+import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.MapDifference;
-import com.google.common.collect.MapDifference.ValueDifference;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
 import com.google.common.util.concurrent.CheckedFuture;
-import com.google.common.util.concurrent.ThreadFactoryBuilder;
-import java.util.ArrayList;
-import java.util.Collection;
+import com.google.common.util.concurrent.FluentFuture;
 import java.util.Collections;
 import java.util.Map;
-import java.util.Map.Entry;
 import java.util.Set;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ThreadFactory;
-import javax.annotation.concurrent.GuardedBy;
+import java.util.WeakHashMap;
 import org.opendaylight.controller.md.sal.dom.api.DOMRpcAvailabilityListener;
 import org.opendaylight.controller.md.sal.dom.api.DOMRpcException;
 import org.opendaylight.controller.md.sal.dom.api.DOMRpcIdentifier;
@@ -36,30 +24,44 @@ import org.opendaylight.controller.md.sal.dom.api.DOMRpcProviderService;
 import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
 import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
 import org.opendaylight.controller.md.sal.dom.spi.AbstractDOMRpcImplementationRegistration;
-import org.opendaylight.controller.sal.core.api.model.SchemaService;
+import org.opendaylight.controller.sal.core.compat.DOMRpcServiceAdapter;
+import org.opendaylight.controller.sal.core.compat.LegacyDOMRpcResultFutureAdapter;
+import org.opendaylight.controller.sal.core.compat.MdsalDOMRpcResultFutureAdapter;
+import org.opendaylight.controller.sal.core.compat.RpcAvailabilityListenerAdapter;
 import org.opendaylight.yangtools.concepts.AbstractListenerRegistration;
 import org.opendaylight.yangtools.concepts.ListenerRegistration;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
 
 public final class DOMRpcRouter implements AutoCloseable, DOMRpcService, DOMRpcProviderService, SchemaContextListener {
-    private static final ThreadFactory THREAD_FACTORY = new ThreadFactoryBuilder()
-            .setNameFormat("DOMRpcRouter-listener-%s").setDaemon(true).build();
-
-    private final ExecutorService listenerNotifier = Executors.newSingleThreadExecutor(THREAD_FACTORY);
-
-    @GuardedBy("this")
-    private Collection<Registration<?>> listeners = Collections.emptyList();
-
-    private volatile DOMRpcRoutingTable routingTable = DOMRpcRoutingTable.EMPTY;
+    // This mapping is used to translate mdsal DOMRpcImplementations to their corresponding legacy
+    // DOMRpcImplementations registered thru this interface when invoking a DOMRpcAvailabilityListener.
+    private final Map<org.opendaylight.mdsal.dom.api.DOMRpcImplementation, DOMRpcImplementation> implMapping =
+            Collections.synchronizedMap(new WeakHashMap<>());
+
+    private final org.opendaylight.mdsal.dom.api.DOMRpcService delegateRpcService;
+    private final org.opendaylight.mdsal.dom.api.DOMRpcProviderService delegateRpcProviderService;
+
+    // Note - this is only used for backward compatibility for UTs that use the empty constructor which creates
+    // a local mdsal DOMRpcRouter that needs to be updated with the SchemaContext. In production, the mdsal API
+    // services are passed via the constructor and are set up externally with the SchemaContext.
+    private final SchemaContextListener delegateSchemaContextListener;
+
+    @VisibleForTesting
+    public DOMRpcRouter() {
+        org.opendaylight.mdsal.dom.broker.DOMRpcRouter delegate = new org.opendaylight.mdsal.dom.broker.DOMRpcRouter();
+        this.delegateRpcService = delegate.getRpcService();
+        this.delegateRpcProviderService = delegate.getRpcProviderService();
+        this.delegateSchemaContextListener = delegate;
+    }
 
-    public static DOMRpcRouter newInstance(final SchemaService schemaService) {
-        final DOMRpcRouter rpcRouter = new DOMRpcRouter();
-        schemaService.registerSchemaContextListener(rpcRouter);
-        return rpcRouter;
+    public DOMRpcRouter(final org.opendaylight.mdsal.dom.api.DOMRpcService delegateRpcService,
+            final org.opendaylight.mdsal.dom.api.DOMRpcProviderService delegateRpcProviderService) {
+        this.delegateRpcService = delegateRpcService;
+        this.delegateRpcProviderService = delegateRpcProviderService;
+        this.delegateSchemaContextListener = null;
     }
 
     @Override
@@ -68,161 +70,82 @@ public final class DOMRpcRouter implements AutoCloseable, DOMRpcService, DOMRpcP
         return registerRpcImplementation(implementation, ImmutableSet.copyOf(rpcs));
     }
 
-    private synchronized void removeRpcImplementation(final DOMRpcImplementation implementation,
-            final Set<DOMRpcIdentifier> rpcs) {
-        final DOMRpcRoutingTable oldTable = routingTable;
-        final DOMRpcRoutingTable newTable = oldTable.remove(implementation, rpcs);
-        routingTable = newTable;
-
-        listenerNotifier.execute(() -> notifyRemoved(newTable, implementation));
-    }
-
     @Override
     public synchronized <T extends DOMRpcImplementation> DOMRpcImplementationRegistration<T> registerRpcImplementation(
             final T implementation, final Set<DOMRpcIdentifier> rpcs) {
-        final DOMRpcRoutingTable oldTable = routingTable;
-        final DOMRpcRoutingTable newTable = oldTable.add(implementation, rpcs);
-        routingTable = newTable;
+        org.opendaylight.mdsal.dom.api.DOMRpcImplementation delegateImpl =
+            new org.opendaylight.mdsal.dom.api.DOMRpcImplementation() {
+                @Override
+                public FluentFuture<org.opendaylight.mdsal.dom.api.DOMRpcResult> invokeRpc(
+                        final org.opendaylight.mdsal.dom.api.DOMRpcIdentifier rpc, final NormalizedNode<?, ?> input) {
+                    return new MdsalDOMRpcResultFutureAdapter(implementation.invokeRpc(DOMRpcIdentifier.fromMdsal(rpc),
+                        input));
+                }
 
-        listenerNotifier.execute(() -> notifyAdded(newTable, implementation));
+
+                @Override
+                public long invocationCost() {
+                    return implementation.invocationCost();
+                }
+            };
+
+        implMapping.put(delegateImpl, implementation);
+
+        final org.opendaylight.mdsal.dom.api.DOMRpcImplementationRegistration
+            <org.opendaylight.mdsal.dom.api.DOMRpcImplementation> reg = delegateRpcProviderService
+                .registerRpcImplementation(delegateImpl, DOMRpcServiceAdapter.convert(rpcs));
 
         return new AbstractDOMRpcImplementationRegistration<T>(implementation) {
             @Override
             protected void removeRegistration() {
-                removeRpcImplementation(getInstance(), rpcs);
+                reg.close();
+                implMapping.remove(delegateImpl);
             }
         };
     }
 
     @Override
     public CheckedFuture<DOMRpcResult, DOMRpcException> invokeRpc(final SchemaPath type,
-            final NormalizedNode<?, ?> input) {
-        return routingTable.invokeRpc(type, input);
-    }
-
-    private synchronized void removeListener(final ListenerRegistration<? extends DOMRpcAvailabilityListener> reg) {
-        listeners = ImmutableList.copyOf(Collections2.filter(listeners, i -> !reg.equals(i)));
-    }
-
-    private synchronized void notifyAdded(final DOMRpcRoutingTable newTable, final DOMRpcImplementation impl) {
-        for (Registration<?> l : listeners) {
-            l.addRpc(newTable, impl);
-        }
-    }
-
-    private synchronized void notifyRemoved(final DOMRpcRoutingTable newTable, final DOMRpcImplementation impl) {
-        for (Registration<?> l : listeners) {
-            l.removeRpc(newTable, impl);
-        }
+                                                                  final NormalizedNode<?, ?> input) {
+        final FluentFuture<org.opendaylight.mdsal.dom.api.DOMRpcResult> future =
+                delegateRpcService.invokeRpc(type, input);
+        return future instanceof MdsalDOMRpcResultFutureAdapter ? ((MdsalDOMRpcResultFutureAdapter)future).delegate()
+                : new LegacyDOMRpcResultFutureAdapter(future);
     }
 
     @Override
     public synchronized <T extends DOMRpcAvailabilityListener> ListenerRegistration<T> registerRpcListener(
             final T listener) {
-        final Registration<T> ret = new Registration<>(this, listener);
-        final Builder<Registration<?>> b = ImmutableList.builder();
-        b.addAll(listeners);
-        b.add(ret);
-        listeners = b.build();
-
-        listenerNotifier.execute(() -> ret.initialTable(routingTable));
-        return ret;
-    }
+        final ListenerRegistration<org.opendaylight.mdsal.dom.api.DOMRpcAvailabilityListener> reg =
+            delegateRpcService.registerRpcListener(new RpcAvailabilityListenerAdapter<T>(listener) {
+                @Override
+                public boolean acceptsImplementation(final org.opendaylight.mdsal.dom.api.DOMRpcImplementation impl) {
+                    // If the DOMRpcImplementation wasn't registered thru this interface then the mapping won't be
+                    // present - in this we can't call the listener so just assume acceptance which is the default
+                    // behavior. This should be fine since a legacy listener would not be aware of implementation types
+                    // registered via the new mdsal API.
+                    final DOMRpcImplementation legacyImpl = implMapping.get(impl);
+                    return legacyImpl != null ? delegate().acceptsImplementation(legacyImpl) : true;
+                }
+            });
 
-    @Override
-    public synchronized void onGlobalContextUpdated(final SchemaContext context) {
-        final DOMRpcRoutingTable oldTable = routingTable;
-        final DOMRpcRoutingTable newTable = oldTable.setSchemaContext(context);
-        routingTable = newTable;
+        return new AbstractListenerRegistration<T>(listener) {
+            @Override
+            protected void removeRegistration() {
+                reg.close();
+            }
+        };
     }
 
     @Override
     public void close() {
-        listenerNotifier.shutdown();
     }
 
-    private static final class Registration<T extends DOMRpcAvailabilityListener>
-        extends AbstractListenerRegistration<T> {
-
-        private final DOMRpcRouter router;
-
-        private Map<SchemaPath, Set<YangInstanceIdentifier>> prevRpcs;
-
-        Registration(final DOMRpcRouter router, final T listener) {
-            super(listener);
-            this.router = router;
-        }
-
-        @Override
-        protected void removeRegistration() {
-            router.removeListener(this);
-        }
-
-        void initialTable(final DOMRpcRoutingTable newTable) {
-            final T l = getInstance();
-            if (l == null) {
-                return;
-            }
-
-            final Map<SchemaPath, Set<YangInstanceIdentifier>> rpcs = newTable.getRpcs(l);
-            final Collection<DOMRpcIdentifier> added = new ArrayList<>();
-            for (Entry<SchemaPath, Set<YangInstanceIdentifier>> e : rpcs.entrySet()) {
-                added.addAll(Collections2.transform(e.getValue(), i -> DOMRpcIdentifier.create(e.getKey(), i)));
-            }
-            prevRpcs = rpcs;
-            if (!added.isEmpty()) {
-                l.onRpcAvailable(added);
-            }
-        }
-
-        void addRpc(final DOMRpcRoutingTable newTable, final DOMRpcImplementation impl) {
-            final T l = getInstance();
-            if (l == null || !l.acceptsImplementation(impl)) {
-                return;
-            }
-
-            final Map<SchemaPath, Set<YangInstanceIdentifier>> rpcs = newTable.getRpcs(l);
-            final MapDifference<SchemaPath, Set<YangInstanceIdentifier>> diff = Maps.difference(prevRpcs, rpcs);
-
-            final Collection<DOMRpcIdentifier> added = new ArrayList<>();
-            for (Entry<SchemaPath, Set<YangInstanceIdentifier>> e : diff.entriesOnlyOnRight().entrySet()) {
-                added.addAll(Collections2.transform(e.getValue(), i -> DOMRpcIdentifier.create(e.getKey(), i)));
-            }
-            for (Entry<SchemaPath, ValueDifference<Set<YangInstanceIdentifier>>> e : diff.entriesDiffering().entrySet()) {
-                for (YangInstanceIdentifier i : Sets.difference(e.getValue().rightValue(), e.getValue().leftValue())) {
-                    added.add(DOMRpcIdentifier.create(e.getKey(), i));
-                }
-            }
-
-            prevRpcs = rpcs;
-            if (!added.isEmpty()) {
-                l.onRpcAvailable(added);
-            }
-        }
-
-        void removeRpc(final DOMRpcRoutingTable newTable, final DOMRpcImplementation impl) {
-            final T l = getInstance();
-            if (l == null || !l.acceptsImplementation(impl)) {
-                return;
-            }
-
-            final Map<SchemaPath, Set<YangInstanceIdentifier>> rpcs = newTable.getRpcs(l);
-            final MapDifference<SchemaPath, Set<YangInstanceIdentifier>> diff = Maps.difference(prevRpcs, rpcs);
-
-            final Collection<DOMRpcIdentifier> removed = new ArrayList<>();
-            for (Entry<SchemaPath, Set<YangInstanceIdentifier>> e : diff.entriesOnlyOnLeft().entrySet()) {
-                removed.addAll(Collections2.transform(e.getValue(), i -> DOMRpcIdentifier.create(e.getKey(), i)));
-            }
-            for (Entry<SchemaPath, ValueDifference<Set<YangInstanceIdentifier>>> e : diff.entriesDiffering().entrySet()) {
-                for (YangInstanceIdentifier i : Sets.difference(e.getValue().leftValue(), e.getValue().rightValue())) {
-                    removed.add(DOMRpcIdentifier.create(e.getKey(), i));
-                }
-            }
-
-            prevRpcs = rpcs;
-            if (!removed.isEmpty()) {
-                l.onRpcUnavailable(removed);
-            }
+    @Override
+    @VisibleForTesting
+    public void onGlobalContextUpdated(final SchemaContext context) {
+        if (delegateSchemaContextListener != null) {
+            delegateSchemaContextListener.onGlobalContextUpdated(context);
         }
     }
 }