import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.common.RpcResult;
import org.opendaylight.yangtools.yang.common.YangConstants;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute;
final CurrentAdapterSerializer serializer = currentSerializer();
final Absolute actionPath = serializer.getActionPath(actionInterface);
final Impl impl = new Impl(adapterContext(), actionPath, actionInterface, implementation);
+ final DOMActionInstance instance = validNodes.isEmpty()
+ // Register on the entire datastore
+ ? DOMActionInstance.of(actionPath, new DOMDataTreeIdentifier(datastore, YangInstanceIdentifier.empty()))
+ // Register on specific instances
+ : DOMActionInstance.of(actionPath, validNodes.stream()
+ .map(node -> serializer.toDOMDataTreeIdentifier(DataTreeIdentifier.create(datastore, node)))
+ .collect(Collectors.toUnmodifiableSet()));
- final ObjectRegistration<DOMActionImplementation> reg = getDelegate().registerActionImplementation(impl,
- DOMActionInstance.of(actionPath, validNodes.stream()
- .map(instance -> serializer.toDOMDataTreeIdentifier(DataTreeIdentifier.create(datastore, instance)))
- .collect(Collectors.toUnmodifiableSet())));
+
+ final ObjectRegistration<?> reg = getDelegate().registerActionImplementation(impl, instance);
return new AbstractObjectRegistration<>(implementation) {
@Override
new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL,
YangInstanceIdentifier.create(new NodeIdentifier(Cont.QNAME))))));
}
+
+ @Test
+ public void testWildcardRegistration() {
+ adapter.registerImplementation(Foo.class, FOO);
+ verify(actionProvider).registerActionImplementation(any(), eq(DOMActionInstance.of(FOO_PATH,
+ LogicalDatastoreType.OPERATIONAL, YangInstanceIdentifier.empty())));
+ }
}
private final Set<DOMDataTreeIdentifier> dataTrees;
private final Absolute type;
- DOMActionInstance(final Absolute type, final Set<DOMDataTreeIdentifier> dataTrees) {
+ private DOMActionInstance(final Absolute type, final ImmutableSet<DOMDataTreeIdentifier> dataTrees) {
this.type = requireNonNull(type);
- this.dataTrees = ImmutableSet.copyOf(dataTrees);
+ this.dataTrees = requireNonNull(dataTrees);
checkArgument(!dataTrees.isEmpty());
}
public static DOMActionInstance of(final Absolute type, final Set<DOMDataTreeIdentifier> dataTrees) {
- return new DOMActionInstance(type, dataTrees);
+ return new DOMActionInstance(type, ImmutableSet.copyOf(dataTrees));
}
public static DOMActionInstance of(final Absolute type, final DOMDataTreeIdentifier... dataTrees) {
- return of(type, ImmutableSet.copyOf(dataTrees));
+ return new DOMActionInstance(type, ImmutableSet.copyOf(dataTrees));
}
public static DOMActionInstance of(final Absolute type, final LogicalDatastoreType datastore,
final YangInstanceIdentifier path) {
- return of(type, ImmutableSet.of(new DOMDataTreeIdentifier(datastore, path)));
+ return new DOMActionInstance(type, ImmutableSet.of(new DOMDataTreeIdentifier(datastore, path)));
}
/**
* Return the set of data trees on which this action is available. These identifiers are required to point
- * to concrete items, i.e. they may not be wildcards.
+ * to concrete items, i.e. they may not be wildcards. Identifiers which return an empty
+ * {@link DOMDataTreeIdentifier#getRootIdentifier()} are considered to match all items in that particular datastore
+ * and are expected to be treated as lower-priority alternatives to exact matches.
*
* @return Set of trees on which this action is available.
*/
}
/**
- * Return the operation type.
+ * Return the action type, i.e. the absolute schema node identifier of the action.
*
- * @return operation type.
+ * @return action type.
*/
public Absolute getType() {
return type;
public interface DOMActionProviderService
extends DOMExtensibleService<DOMActionProviderService, DOMActionProviderServiceExtension> {
/**
- * Register an {@link DOMActionImplementation} object with this service.
+ * Register an {@link DOMActionImplementation} object with this service, servicing specified action instances.
*
* @param implementation action implementation, must not be null
* @param instances Set of supported operation identifiers. Must not be null, empty, or contain a null element.
* @return A {@link ObjectRegistration} object, guaranteed to be non-null.
- * @throws NullPointerException if implementation or types is null
+ * @throws NullPointerException if {@code implementation} or {@code instances} is null, or if {@code instances}
+ * contains a null element.
* @throws IllegalArgumentException if {@code instances} is empty
*/
<T extends DOMActionImplementation> ObjectRegistration<T> registerActionImplementation(T implementation,
- Set<DOMActionInstance> instances);
+ Set<DOMActionInstance> instances);
+ /**
+ * Register an {@link DOMActionImplementation} object with this service, servicing specified action instance.
+ *
+ * @param implementation action implementation, must not be null
+ * @param instance supported operation identifier. Must not be null.
+ * @return A {@link ObjectRegistration} object, guaranteed to be non-null.
+ * @throws NullPointerException if any argument is null
+ */
default <T extends DOMActionImplementation> ObjectRegistration<T> registerActionImplementation(
final T implementation, final DOMActionInstance instance) {
return registerActionImplementation(implementation, ImmutableSet.of(instance));
}
+ /**
+ * Register an {@link DOMActionImplementation} object with this service, servicing specified action instances.
+ *
+ * @param implementation action implementation, must not be null
+ * @param instances Set of supported operation identifiers. Must not be null, empty, or contain a null element.
+ * @return A {@link ObjectRegistration} object, guaranteed to be non-null.
+ * @throws NullPointerException if {@code implementation} or {@code instances} is null, or if {@code instances}
+ * contains a null element.
+ * @throws IllegalArgumentException if {@code instances} is empty
+ */
default <T extends DOMActionImplementation> ObjectRegistration<T> registerActionImplementation(
final T implementation, final DOMActionInstance... instances) {
return registerActionImplementation(implementation, ImmutableSet.copyOf(instances));
* @param input Input argument
* @return A FluentFuture which completes with the result of invocation
* @throws NullPointerException if any of the arguments is null
+ * @throws IllegalArgumentException if {@code path} is empty
*/
ListenableFuture<? extends DOMActionResult> invokeAction(Absolute type, DOMDataTreeIdentifier path,
ContainerNode input);
@Override
public ListenableFuture<? extends DOMActionResult> invokeAction(final Absolute type,
final DOMDataTreeIdentifier path, final ContainerNode input) {
+ final YangInstanceIdentifier pathRoot = path.getRootIdentifier();
+ checkArgument(!pathRoot.isEmpty(), "Action path must not be empty");
+
final DOMActionRoutingTableEntry entry = (DOMActionRoutingTableEntry) actionRoutingTable.getEntry(type);
- if (entry == null) {
- return Futures.immediateFailedFuture(
+ return entry != null ? OperationInvocation.invoke(entry, type, path, requireNonNull(input))
+ : Futures.immediateFailedFuture(
new DOMActionNotAvailableException("No implementation of Action %s available", type));
- }
-
- return OperationInvocation.invoke(entry, type, path, requireNonNull(input));
}
}
private final class ActionProviderServiceFacade implements DOMActionProviderService {
@Override
public <T extends DOMActionImplementation> ObjectRegistration<T> registerActionImplementation(
- final T implementation, final Set<DOMActionInstance> instances) {
+ final T implementation, final Set<DOMActionInstance> instances) {
synchronized (DOMRpcRouter.this) {
final DOMActionRoutingTable oldTable = actionRoutingTable;
static ListenableFuture<? extends DOMActionResult> invoke(final DOMActionRoutingTableEntry entry,
final Absolute type, final DOMDataTreeIdentifier path, final ContainerNode input) {
- final List<DOMActionImplementation> impls = entry.getImplementations(path);
+ List<DOMActionImplementation> impls = entry.getImplementations(path);
if (impls == null) {
- return Futures.immediateFailedFuture(
- new DOMActionNotAvailableException("No implementation of Action %s available for %s", type, path));
+ impls = entry.getImplementations(
+ new DOMDataTreeIdentifier(path.getDatastoreType(), YangInstanceIdentifier.empty()));
+ if (impls == null) {
+ return Futures.immediateFailedFuture(new DOMActionNotAvailableException(
+ "No implementation of Action %s available for %s", type, path));
+ }
}
return impls.get(0).invokeAction(type, path, input);
}
}
+ @Test
+ public void testActionDatastoreRouting() throws ExecutionException {
+ try (DOMRpcRouter rpcRouter = new DOMRpcRouter()) {
+ rpcRouter.onModelContextUpdated(ACTIONS_CONTEXT);
+
+ final DOMActionProviderService actionProvider = rpcRouter.getActionProviderService();
+ assertNotNull(actionProvider);
+ final DOMActionService actionConsumer = rpcRouter.getActionService();
+ assertNotNull(actionConsumer);
+
+ try (ObjectRegistration<?> reg = actionProvider.registerActionImplementation(IMPL,
+ DOMActionInstance.of(BAZ_TYPE, LogicalDatastoreType.OPERATIONAL, YangInstanceIdentifier.empty()))) {
+
+ assertAvailable(actionConsumer, BAZ_PATH_GOOD);
+ assertAvailable(actionConsumer, BAZ_PATH_BAD);
+ }
+
+ assertUnavailable(actionConsumer, BAZ_PATH_BAD);
+ assertUnavailable(actionConsumer, BAZ_PATH_GOOD);
+ }
+ }
+
private static void assertAvailable(final DOMActionService actionService, final YangInstanceIdentifier path) {
final DOMActionResult result;
try {