From 4e34227ab875f0c1ecb25fc315ec96c9a44333d3 Mon Sep 17 00:00:00 2001 From: Robert Varga Date: Thu, 5 Feb 2015 20:11:09 +0100 Subject: [PATCH] BUG-2573: create DOMRpcRouter This patch provides a default implementation of DOMRpc(Provider)Service. The implementation is geared heavily towards steady state, e.g. so that invocations are fast. Change-Id: Icb50ae097aebb5e798177625e04ed9befc7337d4 Signed-off-by: Robert Varga --- .../impl/AbstractDOMRpcRoutingTableEntry.java | 93 ++++++++ .../md/sal/dom/broker/impl/DOMRpcRouter.java | 177 ++++++++++++++++ .../dom/broker/impl/DOMRpcRoutingTable.java | 198 ++++++++++++++++++ .../impl/GlobalDOMRpcRoutingTableEntry.java | 46 ++++ .../impl/RoutedDOMRpcRoutingTableEntry.java | 77 +++++++ .../impl/UnknownDOMRpcRoutingTableEntry.java | 40 ++++ 6 files changed, 631 insertions(+) create mode 100644 opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/AbstractDOMRpcRoutingTableEntry.java create mode 100644 opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMRpcRouter.java create mode 100644 opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMRpcRoutingTable.java create mode 100644 opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/GlobalDOMRpcRoutingTableEntry.java create mode 100644 opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/RoutedDOMRpcRoutingTableEntry.java create mode 100644 opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/UnknownDOMRpcRoutingTableEntry.java diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/AbstractDOMRpcRoutingTableEntry.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/AbstractDOMRpcRoutingTableEntry.java new file mode 100644 index 0000000000..09c4f4fe6d --- /dev/null +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/AbstractDOMRpcRoutingTableEntry.java @@ -0,0 +1,93 @@ +/* + * 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.controller.md.sal.dom.broker.impl; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableMap.Builder; +import com.google.common.util.concurrent.CheckedFuture; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcException; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementation; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.model.api.SchemaPath; + +abstract class AbstractDOMRpcRoutingTableEntry { + private final Map> impls; + private final SchemaPath schemaPath; + + protected AbstractDOMRpcRoutingTableEntry(final SchemaPath schemaPath, final Map> impls) { + this.schemaPath = Preconditions.checkNotNull(schemaPath); + this.impls = Preconditions.checkNotNull(impls); + } + + protected final SchemaPath getSchemaPath() { + return schemaPath; + } + + protected final List getImplementations(final YangInstanceIdentifier context) { + return impls.get(context); + } + + final Map> getImplementations() { + return impls; + } + + public boolean containsContext(final YangInstanceIdentifier contextReference) { + return impls.containsKey(contextReference); + } + + final Set registeredIdentifiers() { + return impls.keySet(); + } + + final AbstractDOMRpcRoutingTableEntry add(final DOMRpcImplementation implementation, final List newRpcs) { + final Builder> vb = ImmutableMap.builder(); + for (Entry> ve : impls.entrySet()) { + if (newRpcs.remove(ve.getKey())) { + final ArrayList i = new ArrayList<>(ve.getValue().size() + 1); + i.addAll(ve.getValue()); + i.add(implementation); + vb.put(ve.getKey(), i); + } else { + vb.put(ve); + } + } + + return newInstance(vb.build()); + } + + final AbstractDOMRpcRoutingTableEntry remove(final DOMRpcImplementation implementation, final List removed) { + final Builder> vb = ImmutableMap.builder(); + for (Entry> ve : impls.entrySet()) { + if (removed.remove(ve.getKey())) { + final ArrayList i = new ArrayList<>(ve.getValue()); + i.remove(implementation); + // We could trimToSize(), but that may perform another copy just to get rid + // of a single element. That is probably not worth the trouble. + if (!i.isEmpty()) { + vb.put(ve.getKey(), i); + } + } else { + vb.put(ve); + } + } + + final Map> v = vb.build(); + return v.isEmpty() ? null : newInstance(v); + } + + protected abstract CheckedFuture invokeRpc(final NormalizedNode input); + protected abstract AbstractDOMRpcRoutingTableEntry newInstance(final Map> impls); +} diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMRpcRouter.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMRpcRouter.java new file mode 100644 index 0000000000..d72f714a5f --- /dev/null +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMRpcRouter.java @@ -0,0 +1,177 @@ +/* + * 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.controller.md.sal.dom.broker.impl; + +import com.google.common.base.Function; +import com.google.common.base.Predicate; +import com.google.common.collect.Collections2; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableList.Builder; +import com.google.common.collect.ImmutableSet; +import com.google.common.util.concurrent.CheckedFuture; +import com.google.common.util.concurrent.ThreadFactoryBuilder; +import java.util.Collection; +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 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; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementation; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementationRegistration; +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.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> listeners = Collections.emptyList(); + private volatile DOMRpcRoutingTable routingTable = DOMRpcRoutingTable.EMPTY; + + @Override + public DOMRpcImplementationRegistration registerRpcImplementation(final T implementation, final DOMRpcIdentifier... rpcs) { + return registerRpcImplementation(implementation, ImmutableSet.copyOf(rpcs)); + } + + private static Collection notPresentRpcs(final DOMRpcRoutingTable table, final Collection candidates) { + return ImmutableSet.copyOf(Collections2.filter(candidates, new Predicate() { + @Override + public boolean apply(final DOMRpcIdentifier input) { + return !table.contains(input); + } + })); + } + + private synchronized void removeRpcImplementation(final DOMRpcImplementation implementation, final Set rpcs) { + final DOMRpcRoutingTable oldTable = routingTable; + final DOMRpcRoutingTable newTable = oldTable.remove(implementation, rpcs); + + final Collection removedRpcs = notPresentRpcs(newTable, rpcs); + final Collection> capturedListeners = listeners; + routingTable = newTable; + + listenerNotifier.execute(new Runnable() { + @Override + public void run() { + for (ListenerRegistration l : capturedListeners) { + // Need to ensure removed listeners do not get notified + synchronized (DOMRpcRouter.this) { + if (listeners.contains(l)) { + l.getInstance().onRpcUnavailable(removedRpcs); + } + } + } + } + }); + } + + @Override + public synchronized DOMRpcImplementationRegistration registerRpcImplementation(final T implementation, final Set rpcs) { + final DOMRpcRoutingTable oldTable = routingTable; + final DOMRpcRoutingTable newTable = oldTable.add(implementation, rpcs); + + final Collection addedRpcs = notPresentRpcs(oldTable, rpcs); + final Collection> capturedListeners = listeners; + routingTable = newTable; + + listenerNotifier.execute(new Runnable() { + @Override + public void run() { + for (ListenerRegistration l : capturedListeners) { + // Need to ensure removed listeners do not get notified + synchronized (DOMRpcRouter.this) { + if (listeners.contains(l)) { + l.getInstance().onRpcAvailable(addedRpcs); + } + } + } + } + }); + + return new AbstractDOMRpcImplementationRegistration(implementation) { + @Override + protected void removeRegistration() { + removeRpcImplementation(getInstance(), rpcs); + } + }; + } + + @Override + public CheckedFuture invokeRpc(final SchemaPath type, final NormalizedNode input) { + return routingTable.invokeRpc(type, input); + } + + private synchronized void removeListener(final ListenerRegistration reg) { + listeners = ImmutableList.copyOf(Collections2.filter(listeners, new Predicate() { + @Override + public boolean apply(final Object input) { + return !reg.equals(input); + } + })); + } + + @Override + public synchronized ListenerRegistration registerRpcListener(final T listener) { + final ListenerRegistration ret = new AbstractListenerRegistration(listener) { + @Override + protected void removeRegistration() { + removeListener(this); + } + }; + + final Builder> b = ImmutableList.builder(); + b.addAll(listeners); + b.add(ret); + listeners = b.build(); + final Map> capturedRpcs = routingTable.getRpcs(); + + listenerNotifier.execute(new Runnable() { + @Override + public void run() { + for (final Entry> e : capturedRpcs.entrySet()) { + listener.onRpcAvailable(Collections2.transform(e.getValue(), new Function() { + @Override + public DOMRpcIdentifier apply(final YangInstanceIdentifier input) { + return DOMRpcIdentifier.create(e.getKey(), input); + } + })); + } + } + }); + + return ret; + } + + @Override + public synchronized void onGlobalContextUpdated(final SchemaContext context) { + final DOMRpcRoutingTable oldTable = routingTable; + final DOMRpcRoutingTable newTable = oldTable.setSchemaContext(context); + routingTable = newTable; + } + + @Override + public void close() { + listenerNotifier.shutdown(); + } + +} diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMRpcRoutingTable.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMRpcRoutingTable.java new file mode 100644 index 0000000000..0e5ce271e4 --- /dev/null +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMRpcRoutingTable.java @@ -0,0 +1,198 @@ +/* + * 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.controller.md.sal.dom.broker.impl; + +import com.google.common.base.Function; +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableMap.Builder; +import com.google.common.collect.LinkedListMultimap; +import com.google.common.collect.ListMultimap; +import com.google.common.collect.Maps; +import com.google.common.util.concurrent.CheckedFuture; +import com.google.common.util.concurrent.Futures; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcException; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcIdentifier; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementation; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementationNotAvailableException; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode; +import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; +import org.opendaylight.yangtools.yang.model.api.Module; +import org.opendaylight.yangtools.yang.model.api.RpcDefinition; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.model.api.SchemaPath; +import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode; + +final class DOMRpcRoutingTable { + private static final QName CONTEXT_REFERENCE = QName.cachedReference(QName.create("urn:opendaylight:yang:extension:yang-ext", "2013-07-09", "context-reference")); + + static final DOMRpcRoutingTable EMPTY = new DOMRpcRoutingTable(); + private static final Function> EXTRACT_IDENTIFIERS = + new Function>() { + @Override + public Set apply(final AbstractDOMRpcRoutingTableEntry input) { + return input.registeredIdentifiers(); + } + }; + private final Map rpcs; + private final SchemaContext schemaContext; + + private DOMRpcRoutingTable() { + rpcs = Collections.emptyMap(); + schemaContext = null; + } + + private DOMRpcRoutingTable(final Map rpcs, final SchemaContext schemaContext) { + this.rpcs = Preconditions.checkNotNull(rpcs); + this.schemaContext = schemaContext; + } + + private static ListMultimap decomposeIdentifiers(final Set rpcs) { + final ListMultimap ret = LinkedListMultimap.create(); + for (DOMRpcIdentifier i : rpcs) { + ret.put(i.getType(), i.getContextReference()); + } + return ret; + } + + DOMRpcRoutingTable add(final DOMRpcImplementation implementation, final Set rpcs) { + if (rpcs.isEmpty()) { + return this; + } + + // First decompose the identifiers to a multimap + final ListMultimap toAdd = decomposeIdentifiers(rpcs); + + // Now iterate over existing entries, modifying them as appropriate... + final Builder mb = ImmutableMap.builder(); + for (Entry re : this.rpcs.entrySet()) { + List newRpcs = toAdd.removeAll(re.getKey()); + if (!newRpcs.isEmpty()) { + final AbstractDOMRpcRoutingTableEntry ne = re.getValue().add(implementation, newRpcs); + mb.put(re.getKey(), ne); + } else { + mb.put(re); + } + } + + // Finally add whatever is left in the decomposed multimap + for (Entry> e : toAdd.asMap().entrySet()) { + final Builder> vb = ImmutableMap.builder(); + final List v = Collections.singletonList(implementation); + for (YangInstanceIdentifier i : e.getValue()) { + vb.put(i, v); + } + + mb.put(e.getKey(), createRpcEntry(schemaContext, e.getKey(), vb.build())); + } + + return new DOMRpcRoutingTable(mb.build(), schemaContext); + } + + DOMRpcRoutingTable remove(final DOMRpcImplementation implementation, final Set rpcs) { + if (rpcs.isEmpty()) { + return this; + } + + // First decompose the identifiers to a multimap + final ListMultimap toRemove = decomposeIdentifiers(rpcs); + + // Now iterate over existing entries, modifying them as appropriate... + final Builder b = ImmutableMap.builder(); + for (Entry e : this.rpcs.entrySet()) { + final List removed = toRemove.removeAll(e.getKey()); + if (!removed.isEmpty()) { + final AbstractDOMRpcRoutingTableEntry ne = e.getValue().remove(implementation, removed); + if (ne != null) { + b.put(e.getKey(), ne); + } + } else { + b.put(e); + } + } + + // All done, whatever is in toRemove, was not there in the first place + return new DOMRpcRoutingTable(b.build(), schemaContext); + } + + boolean contains(final DOMRpcIdentifier input) { + final AbstractDOMRpcRoutingTableEntry contexts = rpcs.get(input.getType()); + return contexts != null && contexts.containsContext(input.getContextReference()); + } + + Map> getRpcs() { + return Maps.transformValues(rpcs, EXTRACT_IDENTIFIERS); + } + + private static RpcDefinition findRpcDefinition(final SchemaContext context, final SchemaPath schemaPath) { + if (context != null) { + final QName qname = schemaPath.getPathFromRoot().iterator().next(); + final Module module = context.findModuleByNamespaceAndRevision(qname.getNamespace(), qname.getRevision()); + if (module != null && module.getRpcs() != null) { + for (RpcDefinition rpc : module.getRpcs()) { + if (qname.equals(rpc.getQName())) { + return rpc; + } + } + } + } + + return null; + } + + private static AbstractDOMRpcRoutingTableEntry createRpcEntry(final SchemaContext context, final SchemaPath key, final Map> implementations) { + final RpcDefinition rpcDef = findRpcDefinition(context, key); + if (rpcDef != null) { + final ContainerSchemaNode input = rpcDef.getInput(); + if (input != null) { + for (DataSchemaNode c : input.getChildNodes()) { + for (UnknownSchemaNode extension : c.getUnknownSchemaNodes()) { + if (CONTEXT_REFERENCE.equals(extension.getNodeType())) { + final YangInstanceIdentifier keyId = YangInstanceIdentifier.builder().node(input.getQName()).node(c.getQName()).build(); + return new RoutedDOMRpcRoutingTableEntry(rpcDef, keyId, implementations); + } + } + } + } + + return new GlobalDOMRpcRoutingTableEntry(rpcDef, implementations); + } else { + return new UnknownDOMRpcRoutingTableEntry(key, implementations); + } + } + + CheckedFuture invokeRpc(final SchemaPath type, final NormalizedNode input) { + final AbstractDOMRpcRoutingTableEntry entry = rpcs.get(type); + if (entry == null) { + return Futures.immediateFailedCheckedFuture(new DOMRpcImplementationNotAvailableException("No implementation of RPC %s available", type)); + } + + return entry.invokeRpc(input); + } + + DOMRpcRoutingTable setSchemaContext(final SchemaContext context) { + final Builder b = ImmutableMap.builder(); + + for (Entry e : rpcs.entrySet()) { + b.put(e.getKey(), createRpcEntry(context, e.getKey(), e.getValue().getImplementations())); + } + + return new DOMRpcRoutingTable(b.build(), context); + } + +} diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/GlobalDOMRpcRoutingTableEntry.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/GlobalDOMRpcRoutingTableEntry.java new file mode 100644 index 0000000000..3b0d5df964 --- /dev/null +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/GlobalDOMRpcRoutingTableEntry.java @@ -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.controller.md.sal.dom.broker.impl; + +import com.google.common.base.Preconditions; +import com.google.common.util.concurrent.CheckedFuture; +import java.util.List; +import java.util.Map; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcException; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcIdentifier; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementation; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.model.api.RpcDefinition; + +final class GlobalDOMRpcRoutingTableEntry extends AbstractDOMRpcRoutingTableEntry { + private final DOMRpcIdentifier rpcId; + + private GlobalDOMRpcRoutingTableEntry(final DOMRpcIdentifier rpcId, final Map> impls) { + super(rpcId.getType(), impls); + this.rpcId = Preconditions.checkNotNull(rpcId); + } + + // We do not need the RpcDefinition, but this makes sure we do not + // forward something we don't know to be an RPC. + GlobalDOMRpcRoutingTableEntry(final RpcDefinition def, final Map> impls) { + super(def.getPath(), impls); + this.rpcId = DOMRpcIdentifier.create(def.getPath()); + } + + @Override + protected CheckedFuture invokeRpc(final NormalizedNode input) { + return getImplementations(null).get(0).invokeRpc(rpcId, input); + } + + @Override + protected GlobalDOMRpcRoutingTableEntry newInstance(final Map> impls) { + return new GlobalDOMRpcRoutingTableEntry(rpcId, impls); + } +} \ No newline at end of file diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/RoutedDOMRpcRoutingTableEntry.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/RoutedDOMRpcRoutingTableEntry.java new file mode 100644 index 0000000000..e6df966f36 --- /dev/null +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/RoutedDOMRpcRoutingTableEntry.java @@ -0,0 +1,77 @@ +/* + * 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.controller.md.sal.dom.broker.impl; + +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; +import com.google.common.util.concurrent.CheckedFuture; +import com.google.common.util.concurrent.Futures; +import java.util.List; +import java.util.Map; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcException; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcIdentifier; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementation; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementationNotAvailableException; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes; +import org.opendaylight.yangtools.yang.model.api.RpcDefinition; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +final class RoutedDOMRpcRoutingTableEntry extends AbstractDOMRpcRoutingTableEntry { + private static final Logger LOG = LoggerFactory.getLogger(RoutedDOMRpcRoutingTableEntry.class); + private final DOMRpcIdentifier globalRpcId; + private final YangInstanceIdentifier keyId; + + private RoutedDOMRpcRoutingTableEntry(final DOMRpcIdentifier globalRpcId, final YangInstanceIdentifier keyId, final Map> impls) { + super(globalRpcId.getType(), impls); + this.keyId = Preconditions.checkNotNull(keyId); + this.globalRpcId = Preconditions.checkNotNull(globalRpcId); + } + + RoutedDOMRpcRoutingTableEntry(final RpcDefinition def, final YangInstanceIdentifier keyId, final Map> impls) { + super(def.getPath(), impls); + this.keyId = Preconditions.checkNotNull(keyId); + this.globalRpcId = DOMRpcIdentifier.create(def.getPath()); + } + + @Override + protected CheckedFuture invokeRpc(final NormalizedNode input) { + final Optional> maybeKey = NormalizedNodes.findNode(input, keyId); + + // Routing key is present, attempt to deliver as a routed RPC + if (maybeKey.isPresent()) { + final NormalizedNode key = maybeKey.get(); + final Object value = key.getValue(); + if (value instanceof YangInstanceIdentifier) { + final YangInstanceIdentifier iid = (YangInstanceIdentifier) value; + final List impls = getImplementations(iid); + if (impls != null) { + return impls.get(0).invokeRpc(DOMRpcIdentifier.create(getSchemaPath(), iid), input); + } + LOG.debug("No implementation for context {} found", iid); + } else { + LOG.warn("Ignoring wrong context value {}", value); + } + } + + final List impls = getImplementations(null); + if (impls != null) { + return impls.get(0).invokeRpc(globalRpcId, input); + } else { + return Futures.immediateFailedCheckedFuture(new DOMRpcImplementationNotAvailableException("No implementation of RPC %s available", getSchemaPath())); + } + } + + @Override + protected RoutedDOMRpcRoutingTableEntry newInstance(final Map> impls) { + return new RoutedDOMRpcRoutingTableEntry(globalRpcId, keyId, impls); + } +} \ No newline at end of file diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/UnknownDOMRpcRoutingTableEntry.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/UnknownDOMRpcRoutingTableEntry.java new file mode 100644 index 0000000000..aadfa4a507 --- /dev/null +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/UnknownDOMRpcRoutingTableEntry.java @@ -0,0 +1,40 @@ +/* + * 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.controller.md.sal.dom.broker.impl; + +import com.google.common.util.concurrent.CheckedFuture; +import com.google.common.util.concurrent.Futures; +import java.util.List; +import java.util.Map; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcException; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementation; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementationNotAvailableException; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.model.api.SchemaPath; + +final class UnknownDOMRpcRoutingTableEntry extends AbstractDOMRpcRoutingTableEntry { + private final CheckedFuture unknownRpc; + + UnknownDOMRpcRoutingTableEntry(final SchemaPath schemaPath, final Map> impls) { + super(schemaPath, impls); + unknownRpc = Futures.immediateFailedCheckedFuture( + new DOMRpcImplementationNotAvailableException("SchemaPath %s is not resolved to an RPC", schemaPath)); + } + + @Override + protected CheckedFuture invokeRpc(final NormalizedNode input) { + return unknownRpc; + } + + @Override + protected UnknownDOMRpcRoutingTableEntry newInstance(final Map> impls) { + return new UnknownDOMRpcRoutingTableEntry(getSchemaPath(), impls); + } +} \ No newline at end of file -- 2.36.6