Refactor DOM{Action,Rpc}Implementation 88/111288/1
authorRobert Varga <robert.varga@pantheon.tech>
Fri, 5 Apr 2024 15:10:46 +0000 (17:10 +0200)
committerRobert Varga <robert.varga@pantheon.tech>
Fri, 5 Apr 2024 15:46:34 +0000 (17:46 +0200)
These two interfaces share a common trait: they have an
invocationCost().

Factor this out into a common sealed super-interface,
DOMOperationImplementation.

This is nudging us towards unifying the result path, so let's take that
step by removing DOMActionResult and provide further guidance to other
API elements.

Change-Id: I9d066cadaeb6240de1340e40087abee9389ee90e
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
21 files changed:
binding/mdsal-binding-dom-adapter/src/main/java/module-info.java
binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/ActionAdapter.java
binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/ActionProviderServiceAdapter.java
binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingOperationFluentFuture.java
binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/RpcInvocationStrategy.java
binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/RpcResultUtil.java
binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/ActionServiceAdapterTest.java
dom/mdsal-dom-api/src/main/java/org/opendaylight/mdsal/dom/api/DOMActionException.java
dom/mdsal-dom-api/src/main/java/org/opendaylight/mdsal/dom/api/DOMActionImplementation.java
dom/mdsal-dom-api/src/main/java/org/opendaylight/mdsal/dom/api/DOMActionResult.java [deleted file]
dom/mdsal-dom-api/src/main/java/org/opendaylight/mdsal/dom/api/DOMActionService.java
dom/mdsal-dom-api/src/main/java/org/opendaylight/mdsal/dom/api/DOMOperationImplementation.java [new file with mode: 0644]
dom/mdsal-dom-api/src/main/java/org/opendaylight/mdsal/dom/api/DOMRpcException.java
dom/mdsal-dom-api/src/main/java/org/opendaylight/mdsal/dom/api/DOMRpcImplementation.java
dom/mdsal-dom-api/src/main/java/org/opendaylight/mdsal/dom/api/DOMRpcResult.java
dom/mdsal-dom-broker/src/main/java/org/opendaylight/mdsal/dom/broker/DOMRpcRouter.java
dom/mdsal-dom-broker/src/main/java/org/opendaylight/mdsal/dom/broker/OperationInvocation.java
dom/mdsal-dom-broker/src/test/java/org/opendaylight/mdsal/dom/broker/DOMRpcRouterTest.java
dom/mdsal-dom-spi/src/main/java/org/opendaylight/mdsal/dom/spi/DefaultDOMRpcResult.java
dom/mdsal-dom-spi/src/main/java/org/opendaylight/mdsal/dom/spi/ForwardingDOMActionService.java
dom/mdsal-dom-spi/src/main/java/org/opendaylight/mdsal/dom/spi/SimpleDOMActionResult.java [deleted file]

index d9644bd43841bd2567c3a6bc34bef396fc78ff19..a172daef3acf4a9b2fffadfb536c0a6a8d8a0353 100644 (file)
@@ -36,6 +36,7 @@ module org.opendaylight.mdsal.binding.dom.adapter {
     requires org.opendaylight.mdsal.dom.spi;
     requires org.opendaylight.yangtools.concepts;
     requires org.opendaylight.yangtools.yang.common;
+    requires org.opendaylight.yangtools.yang.data.api;
     requires org.opendaylight.yangtools.yang.data.spi;
     requires org.opendaylight.yangtools.yang.model.api;
     requires org.slf4j;
index 12a4081f3d5ac7b7493ae9e2a81fe0c5e6fb0a2f..d304ce0900814c9cb16788160ae9b944b431ba85 100644 (file)
@@ -13,14 +13,12 @@ import static org.opendaylight.mdsal.binding.dom.adapter.StaticConfiguration.ENA
 import static org.opendaylight.yangtools.yang.common.YangConstants.operationInputQName;
 
 import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
 import com.google.common.util.concurrent.MoreExecutors;
 import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.Method;
 import org.eclipse.jdt.annotation.NonNull;
 import org.opendaylight.mdsal.binding.api.ActionSpec;
 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
-import org.opendaylight.mdsal.dom.api.DOMActionResult;
 import org.opendaylight.mdsal.dom.api.DOMActionService;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
@@ -66,7 +64,7 @@ final class ActionAdapter extends AbstractBindingAdapter<DOMActionService> imple
 
                     final var input = (RpcInput) requireNonNull(args[1]);
                     final var serializer = currentSerializer();
-                    final ListenableFuture<? extends DOMActionResult> future = getDelegate().invokeAction(actionPath,
+                    final var future = getDelegate().invokeAction(actionPath,
                         DOMDataTreeIdentifier.of(LogicalDatastoreType.OPERATIONAL,
                             serializer.toYangInstanceIdentifier(path)),
                         serializer.toLazyNormalizedNodeActionInput(spec.type(), inputName, input));
@@ -76,11 +74,12 @@ final class ActionAdapter extends AbstractBindingAdapter<DOMActionService> imple
                         return bindingAware.getBindingFuture();
                     }
 
-                    return Futures.transform(future,
-                        dom -> RpcResultUtil.rpcResultFromDOM(dom.getErrors(), dom.getOutput()
-                            .map(output -> serializer.fromNormalizedNodeActionOutput(spec.type(), output))
-                            .orElse(null)),
-                        MoreExecutors.directExecutor());
+                    final var specType = spec.type();
+                    return Futures.transform(future, dom -> {
+                        final var value = dom.value();
+                        return RpcResultUtil.rpcResultFromDOM(dom.errors(), value == null ? null
+                            : serializer.fromNormalizedNodeActionOutput(specType, value));
+                    }, MoreExecutors.directExecutor());
                 }
                 break;
             default:
index a3ad6568c0eee1351db8701c92f71918d04094c9..10ef52f03ae918ea0f7980432e575d84ee6f520e 100644 (file)
@@ -13,7 +13,6 @@ import com.google.common.collect.ClassToInstanceMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
-import java.util.List;
 import java.util.Set;
 import java.util.stream.Collectors;
 import org.eclipse.jdt.annotation.NonNullByDefault;
@@ -25,10 +24,10 @@ import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
 import org.opendaylight.mdsal.dom.api.DOMActionImplementation;
 import org.opendaylight.mdsal.dom.api.DOMActionInstance;
 import org.opendaylight.mdsal.dom.api.DOMActionProviderService;
-import org.opendaylight.mdsal.dom.api.DOMActionResult;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
+import org.opendaylight.mdsal.dom.api.DOMRpcResult;
 import org.opendaylight.mdsal.dom.api.DOMService;
-import org.opendaylight.mdsal.dom.spi.SimpleDOMActionResult;
+import org.opendaylight.mdsal.dom.spi.DefaultDOMRpcResult;
 import org.opendaylight.yangtools.concepts.Registration;
 import org.opendaylight.yangtools.yang.binding.Action;
 import org.opendaylight.yangtools.yang.binding.DataObject;
@@ -110,22 +109,22 @@ public final class ActionProviderServiceAdapter extends AbstractBindingAdapter<D
 
         @Override
         @SuppressWarnings({ "rawtypes", "unchecked" })
-        public ListenableFuture<? extends DOMActionResult> invokeAction(final Absolute type,
+        public ListenableFuture<? extends DOMRpcResult> invokeAction(final Absolute type,
                 final DOMDataTreeIdentifier path, final ContainerNode input) {
             final CurrentAdapterSerializer codec = adapterContext.currentSerializer();
             final InstanceIdentifier<DataObject> instance = codec.fromYangInstanceIdentifier(path.path());
             if (instance == null) {
                 // Not representable: return an error
                 LOG.debug("Path {} is not representable in binding, rejecting invocation", path);
-                return Futures.immediateFuture(new SimpleDOMActionResult(List.of(RpcResultBuilder.newError(
-                    ErrorType.APPLICATION, ErrorTag.INVALID_VALUE, "Supplied path cannot be represented"))));
+                return Futures.immediateFuture(new DefaultDOMRpcResult(RpcResultBuilder.newError(
+                    ErrorType.APPLICATION, ErrorTag.INVALID_VALUE, "Supplied path cannot be represented")));
             }
             if (instance.isWildcarded()) {
                 // A wildcard path: return an error
                 LOG.debug("Path {} maps to a wildcard {}, rejecting invocation", path, instance);
-                return Futures.immediateFuture(new SimpleDOMActionResult(List.of(RpcResultBuilder.newError(
+                return Futures.immediateFuture(new DefaultDOMRpcResult(RpcResultBuilder.newError(
                     ErrorType.APPLICATION, ErrorTag.INVALID_VALUE,
-                    "Supplied path does not identify a concrete instance"))));
+                    "Supplied path does not identify a concrete instance")));
             }
 
             final ListenableFuture<RpcResult<?>> userFuture = implementation.invoke(instance,
index 74072ee4cfd30d36db565d302c9e6dc672e6e545..308c305efe26bf67238fd060058d1715fa58a90b 100644 (file)
@@ -14,14 +14,14 @@ import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
 import com.google.common.util.concurrent.MoreExecutors;
 import java.util.concurrent.ExecutionException;
-import org.opendaylight.mdsal.dom.api.DOMActionResult;
-import org.opendaylight.mdsal.dom.spi.SimpleDOMActionResult;
+import org.opendaylight.mdsal.dom.api.DOMRpcResult;
+import org.opendaylight.mdsal.dom.spi.DefaultDOMRpcResult;
 import org.opendaylight.yangtools.yang.binding.Action;
 import org.opendaylight.yangtools.yang.binding.RpcOutput;
 import org.opendaylight.yangtools.yang.common.RpcResult;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
 
-final class BindingOperationFluentFuture<O extends RpcOutput> extends AbstractFuture<DOMActionResult>
+final class BindingOperationFluentFuture<O extends RpcOutput> extends AbstractFuture<DOMRpcResult>
         implements BindingRpcFutureAware {
     private final ListenableFuture<RpcResult<O>> userFuture;
     private final Class<? extends Action<?, ?, O>> action;
@@ -47,17 +47,14 @@ final class BindingOperationFluentFuture<O extends RpcOutput> extends AbstractFu
 
     @SuppressWarnings("checkstyle:illegalCatch")
     private void userFutureCompleted() {
-        final DOMActionResult domResult;
+        final DOMRpcResult domResult;
 
         try {
-            final RpcResult<O> bindingResult = Futures.getDone(userFuture);
-            if (bindingResult.getResult() != null) {
-                domResult = new SimpleDOMActionResult(adapterContext.currentSerializer()
-                    .toLazyNormalizedNodeActionOutput(action, identifier, bindingResult.getResult()),
-                    bindingResult.getErrors());
-            } else {
-                domResult = new SimpleDOMActionResult(bindingResult.getErrors());
-            }
+            final var bindingResult = Futures.getDone(userFuture);
+            final var bindingValue = bindingResult.getResult();
+            domResult = new DefaultDOMRpcResult(bindingValue == null ? null
+                : adapterContext.currentSerializer().toLazyNormalizedNodeActionOutput(action, identifier, bindingValue),
+                bindingResult.getErrors());
         } catch (ExecutionException e) {
             adapterContext = null;
             setException(e.getCause());
index c58923156917d7ce13fd0be6f4b85f11001c1b32..c819ccb962b802aaa481dc1587c1c43ce375e623 100644 (file)
@@ -14,9 +14,6 @@ import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
 import com.google.common.util.concurrent.MoreExecutors;
 import org.eclipse.jdt.annotation.NonNull;
-import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer;
-import org.opendaylight.mdsal.dom.api.DOMRpcResult;
-import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.RpcInput;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.RpcResult;
@@ -64,34 +61,25 @@ sealed class RpcInvocationStrategy {
     }
 
     final ListenableFuture<RpcResult<?>> invoke(final RpcInput input) {
-        return invoke(serialize(inputIdentifier, adapter.currentSerializer(), input));
+        final var serializer = adapter.currentSerializer();
+        return invoke(serializer, serialize(inputIdentifier, serializer, input));
     }
 
-    private ListenableFuture<RpcResult<?>> invoke(final ContainerNode input) {
+    private ListenableFuture<RpcResult<?>> invoke(final @NonNull CurrentAdapterSerializer serializer,
+            final ContainerNode input) {
         final var domFuture = adapter.delegate().invokeRpc(outputPath.firstNodeIdentifier(), input);
         if (ENABLE_CODEC_SHORTCUT && domFuture instanceof BindingRpcFutureAware bindingAware) {
             return bindingAware.getBindingFuture();
         }
-        return transformFuture(domFuture, adapter.currentSerializer());
+        return Futures.transform(domFuture, dom -> {
+            final var value = dom.value();
+            return RpcResultUtil.rpcResultFromDOM(dom.errors(), value == null ? null
+                : serializer.fromNormalizedNodeRpcData(outputPath, value));
+        }, MoreExecutors.directExecutor());
     }
 
     ContainerNode serialize(final @NonNull NodeIdentifier identifier,
             final @NonNull CurrentAdapterSerializer serializer, final RpcInput input) {
         return LazySerializedContainerNode.create(inputIdentifier, input, serializer);
     }
-
-    private ListenableFuture<RpcResult<?>> transformFuture(final ListenableFuture<? extends DOMRpcResult> domFuture,
-            final BindingNormalizedNodeSerializer resultCodec) {
-        return Futures.transform(domFuture, input -> {
-            final ContainerNode domData = input.value();
-            final DataObject bindingResult;
-            if (domData != null) {
-                bindingResult = resultCodec.fromNormalizedNodeRpcData(outputPath, domData);
-            } else {
-                bindingResult = null;
-            }
-
-            return RpcResultUtil.rpcResultFromDOM(input.errors(), bindingResult);
-        }, MoreExecutors.directExecutor());
-    }
 }
\ No newline at end of file
index 179f276cd08afda147eebe9cd19a6675194db534..fc0c373c3e960b2bb9b88dc637f78c072454d210 100644 (file)
@@ -10,7 +10,6 @@ package org.opendaylight.mdsal.binding.dom.adapter;
 import java.util.Collection;
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
-import org.opendaylight.mdsal.dom.api.DOMActionResult;
 import org.opendaylight.mdsal.dom.api.DOMRpcResult;
 import org.opendaylight.yangtools.yang.common.ErrorSeverity;
 import org.opendaylight.yangtools.yang.common.RpcError;
@@ -18,12 +17,12 @@ import org.opendaylight.yangtools.yang.common.RpcResult;
 import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
 
 /**
- * Utility methods for converting {@link RpcResult} to/from {@link DOMActionResult} and {@link DOMRpcResult}.
+ * Utility methods for converting {@link RpcResult} to/from {@link DOMRpcResult}.
  */
 @NonNullByDefault
 final class RpcResultUtil {
     private RpcResultUtil() {
-
+        // Hidden on purpose
     }
 
     /**
@@ -32,6 +31,8 @@ final class RpcResultUtil {
      */
     static <T> RpcResult<T> rpcResultFromDOM(final Collection<? extends RpcError> errors, final @Nullable T result) {
         return RpcResultBuilder.<T>status(errors.stream().noneMatch(err -> err.getSeverity() == ErrorSeverity.ERROR))
-                .withResult(result).withRpcErrors(errors).build();
+            .withResult(result)
+            .withRpcErrors(errors)
+            .build();
     }
 }
index 1803e3955a19fe97714da9a1cc8efbf1811f7f86..dfa8abd9bc583e24779f10321482ee66a47fe6b0 100644 (file)
@@ -24,16 +24,15 @@ import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnitRunner;
 import org.opendaylight.mdsal.binding.api.ActionService;
 import org.opendaylight.mdsal.binding.api.ActionSpec;
-import org.opendaylight.mdsal.dom.api.DOMActionResult;
 import org.opendaylight.mdsal.dom.api.DOMActionService;
-import org.opendaylight.mdsal.dom.spi.SimpleDOMActionResult;
+import org.opendaylight.mdsal.dom.api.DOMRpcResult;
+import org.opendaylight.mdsal.dom.spi.DefaultDOMRpcResult;
 import org.opendaylight.yang.gen.v1.urn.odl.actions.norev.Cont;
 import org.opendaylight.yang.gen.v1.urn.odl.actions.norev.Lstio;
 import org.opendaylight.yang.gen.v1.urn.odl.actions.norev.LstioKey;
 import org.opendaylight.yang.gen.v1.urn.odl.actions.norev.cont.Foo;
 import org.opendaylight.yang.gen.v1.urn.odl.actions.norev.lstio.Fooio;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
 
 @RunWith(MockitoJUnitRunner.StrictStubs.class)
 public class ActionServiceAdapterTest extends AbstractActionAdapterTest {
@@ -42,7 +41,7 @@ public class ActionServiceAdapterTest extends AbstractActionAdapterTest {
 
     private ActionService service;
 
-    private SettableFuture<DOMActionResult> domResult;
+    private SettableFuture<DOMRpcResult> domResult;
 
     @Override
     @Before
@@ -61,7 +60,7 @@ public class ActionServiceAdapterTest extends AbstractActionAdapterTest {
         final var future = handle.invoke(InstanceIdentifier.create(Cont.class), BINDING_FOO_INPUT);
         assertNotNull(future);
         assertFalse(future.isDone());
-        domResult.set(new SimpleDOMActionResult(DOM_FOO_OUTPUT, List.of()));
+        domResult.set(new DefaultDOMRpcResult(DOM_FOO_OUTPUT));
         final var bindingResult = Futures.getDone(future);
 
         assertEquals(List.of(), bindingResult.getErrors());
@@ -71,13 +70,12 @@ public class ActionServiceAdapterTest extends AbstractActionAdapterTest {
     @Test
     public void testKeyedInvocation() throws ExecutionException {
         final var handle = service.getActionHandle(ActionSpec.builder(Lstio.class).build(Fooio.class));
-        final var future = handle.invoke((KeyedInstanceIdentifier<Lstio, LstioKey>)
-                InstanceIdentifier.builder(Lstio.class, new LstioKey("test")).build(),
+        final var future = handle.invoke(InstanceIdentifier.builder(Lstio.class, new LstioKey("test")).build(),
                 BINDING_LSTIO_INPUT);
         assertNotNull(future);
         assertFalse(future.isDone());
 
-        domResult.set(new SimpleDOMActionResult(DOM_FOO_OUTPUT, List.of()));
+        domResult.set(new DefaultDOMRpcResult(DOM_FOO_OUTPUT));
         final var bindingResult = Futures.getDone(future);
 
         assertEquals(List.of(), bindingResult.getErrors());
index 4ebc9e413334ed3f37e922aa008850db047fae15..70a5eb3e249556a76959b664ffdc05bd8d105d7f 100644 (file)
@@ -12,13 +12,14 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
 
 /**
  * Base class for failures that can occur during operation invocation. This covers transport and protocol-level
- * failures, not implementation-reported errors, which are part of {@link DOMActionResult}.
- *
- * @author Robert Varga
+ * failures, not implementation-reported errors, which are part of {@link DOMRpcResult}.
  */
+// FIXME: remove this exception and rename DOMRpcException to DOMOperationException
 @Beta
+@Deprecated
 @NonNullByDefault
 public abstract class DOMActionException extends Exception {
+    @java.io.Serial
     private static final long serialVersionUID = 1L;
 
     /**
index 568f424eee0d1eb4571f5eaae4fcea6fecb33afb..d2876c05f7d7c75b1e35603ae5ac7d45a01764f9 100644 (file)
@@ -7,7 +7,6 @@
  */
 package org.opendaylight.mdsal.dom.api;
 
-import com.google.common.annotations.Beta;
 import com.google.common.util.concurrent.ListenableFuture;
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
@@ -17,13 +16,10 @@ import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absol
 /**
  * Interface implemented by an individual operation implementation. This API allows for dispatch implementations, e.g.
  * an individual object handling a multitude of operations.
- *
- * @author Robert Varga
  */
-@Beta
 @FunctionalInterface
 @NonNullByDefault
-public interface DOMActionImplementation {
+public non-sealed interface DOMActionImplementation extends DOMOperationImplementation {
     /**
      * Initiate invocation of the action. Implementations of this method are expected to not block.
      *
@@ -31,18 +27,9 @@ public interface DOMActionImplementation {
      *             instantiated on top of the conceptual {@link StoreTreeNode}.
      * @param path {@link DOMDataTreeIdentifier} of parent data node which action attached to.
      * @param input Input arguments
-     * @return A FluentFuture which completes with the result of invocation
+     * @return A {@link ListenableFuture} which completes with the result of invocation
      * @throws NullPointerException if any of the arguments is null
      */
-    ListenableFuture<? extends DOMActionResult> invokeAction(Absolute type, DOMDataTreeIdentifier path,
-            ContainerNode input);
-
-    /**
-     * Return the relative invocation cost of this implementation. Default implementation returns 0.
-     *
-     * @return Non-negative cost of invoking this implementation.
-     */
-    default long invocationCost() {
-        return 0;
-    }
+    ListenableFuture<? extends DOMRpcResult> invokeAction(Absolute type, DOMDataTreeIdentifier path,
+        ContainerNode input);
 }
diff --git a/dom/mdsal-dom-api/src/main/java/org/opendaylight/mdsal/dom/api/DOMActionResult.java b/dom/mdsal-dom-api/src/main/java/org/opendaylight/mdsal/dom/api/DOMActionResult.java
deleted file mode 100644 (file)
index 2ed1a40..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.mdsal.dom.api;
-
-import com.google.common.annotations.Beta;
-import java.util.Collection;
-import java.util.Optional;
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.opendaylight.yangtools.yang.common.RpcError;
-import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
-
-/**
- * Interface defining a result of an operation invocation.
- *
- * @author Robert Varga
- */
-@Beta
-@NonNullByDefault
-public interface DOMActionResult {
-    /**
-     * Returns a set of errors and warnings which occurred during processing the call.
-     *
-     * @return a Collection of {@link RpcError}, guaranteed to be non-null. In case no errors are reported, an empty
-     *         collection is returned.
-     */
-    Collection<RpcError> getErrors();
-
-    /**
-     * Returns the value result of the call.
-     *
-     * @return Invocation result,.empty if the operation has not produced a result. This might be the case if the
-     *         operation does not produce a result, or if it failed.
-     */
-    Optional<ContainerNode> getOutput();
-}
index 90ef9e1b5deaabbecbcbc94b0bc807efb6eeb579..c6560be8d1ebd9c9a40782cf7974e0e10d4876f0 100644 (file)
@@ -7,7 +7,6 @@
  */
 package org.opendaylight.mdsal.dom.api;
 
-import com.google.common.annotations.Beta;
 import com.google.common.util.concurrent.ListenableFuture;
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
@@ -18,7 +17,6 @@ import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absol
  * A {@link DOMService} which allows clients to invoke Actions. The conceptual model of this service is that
  * of a dynamic router, where the set of available Action services can change dynamically.
  */
-@Beta
 @NonNullByDefault
 public interface DOMActionService extends DOMService<DOMActionService, DOMActionService.Extension> {
     /**
@@ -35,10 +33,10 @@ public interface DOMActionService extends DOMService<DOMActionService, DOMAction
      *             instantiated on top of the conceptual {@link StoreTreeNode}.
      * @param path {@link DOMDataTreeIdentifier} of parent data node which action attached to.
      * @param input Input argument
-     * @return A FluentFuture which completes with the result of invocation
+     * @return A {@link ListenableFuture} 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,
+    ListenableFuture<? extends DOMRpcResult> invokeAction(Absolute type, DOMDataTreeIdentifier path,
             ContainerNode input);
 }
diff --git a/dom/mdsal-dom-api/src/main/java/org/opendaylight/mdsal/dom/api/DOMOperationImplementation.java b/dom/mdsal-dom-api/src/main/java/org/opendaylight/mdsal/dom/api/DOMOperationImplementation.java
new file mode 100644 (file)
index 0000000..f21e495
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2024 PANTHEON.tech, s.r.o. 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.mdsal.dom.api;
+
+/**
+ * Common interface between {@link DOMActionImplementation} and {@link DOMRpcImplementation}.
+ */
+public sealed interface DOMOperationImplementation permits DOMActionImplementation, DOMRpcImplementation {
+    /**
+     * Return the relative invocation cost of this implementation. Default implementation returns 0.
+     *
+     * @return Non-negative cost of invoking this implementation.
+     */
+    default long invocationCost() {
+        return 0;
+    }
+}
index d62d03ba977b488267e4e9e22d50fb077d62e30c..3d4b7aa9f0444bdbcf883e8a1e8bc336690d66b3 100644 (file)
@@ -11,7 +11,9 @@ package org.opendaylight.mdsal.dom.api;
  * Base class for failures that can occur during RPC invocation. This covers
  * transport and protocol-level failures.
  */
+// FIXME: remove this exception to DOMOperationException and remove DOMActionException
 public abstract class DOMRpcException extends Exception {
+    @java.io.Serial
     private static final long serialVersionUID = 1L;
 
     /**
index 7074eef968ecae2329c154a6652ddb1a573a2287..b945e37b7f8058ec90f85f92abb0ca975118d777 100644 (file)
@@ -15,7 +15,7 @@ import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
  * Interface implemented by an individual RPC implementation. This API allows for dispatch
  * implementations, e.g. an individual object handling a multitude of RPCs.
  */
-public interface DOMRpcImplementation {
+public non-sealed interface DOMRpcImplementation extends DOMOperationImplementation {
     /**
      * Initiate invocation of the RPC. Implementations of this method are
      * expected to not block on external resources.
@@ -29,13 +29,4 @@ public interface DOMRpcImplementation {
      */
     @NonNull ListenableFuture<? extends DOMRpcResult> invokeRpc(@NonNull DOMRpcIdentifier rpc,
             @NonNull ContainerNode input);
-
-    /**
-     * Return the relative invocation cost of this implementation. Default implementation return 0.
-     *
-     * @return Non-negative cost of invoking this implementation.
-     */
-    default long invocationCost() {
-        return 0;
-    }
 }
index 1e62007b267ba226f593ae0decc9df49e794d9a7..5c638da9650b3c8917497281796857d22449d675 100644 (file)
@@ -16,6 +16,7 @@ import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
 /**
  * Interface defining a result of an RPC call.
  */
+// FIXME: rename to DOMOperationResult
 @NonNullByDefault
 public interface DOMRpcResult {
     /**
index a8daf7fe9129c47d214d7eca1818b98540569502..1e82dbb0d1695fd4adf2f6f82e0d3c6ea9b6fda9 100644 (file)
@@ -45,7 +45,6 @@ import org.opendaylight.mdsal.dom.api.DOMActionImplementation;
 import org.opendaylight.mdsal.dom.api.DOMActionInstance;
 import org.opendaylight.mdsal.dom.api.DOMActionNotAvailableException;
 import org.opendaylight.mdsal.dom.api.DOMActionProviderService;
-import org.opendaylight.mdsal.dom.api.DOMActionResult;
 import org.opendaylight.mdsal.dom.api.DOMActionService;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
 import org.opendaylight.mdsal.dom.api.DOMRpcAvailabilityListener;
@@ -396,7 +395,7 @@ public final class DOMRpcRouter extends AbstractRegistration {
         }
 
         @Override
-        public ListenableFuture<? extends DOMActionResult> invokeAction(final Absolute type,
+        public ListenableFuture<? extends DOMRpcResult> invokeAction(final Absolute type,
                 final DOMDataTreeIdentifier path, final ContainerNode input) {
             final YangInstanceIdentifier pathRoot = path.path();
             checkArgument(!pathRoot.isEmpty(), "Action path must not be empty");
index 52097d09bc286221e8d3d9439742fdad6f87d504..032a2d7752773860e5a07f5c7f1e8301b8fcf199 100644 (file)
@@ -10,7 +10,6 @@ package org.opendaylight.mdsal.dom.broker;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
 import org.opendaylight.mdsal.dom.api.DOMActionNotAvailableException;
-import org.opendaylight.mdsal.dom.api.DOMActionResult;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
 import org.opendaylight.mdsal.dom.api.DOMRpcIdentifier;
 import org.opendaylight.mdsal.dom.api.DOMRpcImplementationNotAvailableException;
@@ -29,7 +28,7 @@ final class OperationInvocation {
         // hidden on purpose
     }
 
-    static ListenableFuture<? extends DOMActionResult> invoke(final DOMActionRoutingTableEntry entry,
+    static ListenableFuture<? extends DOMRpcResult> invoke(final DOMActionRoutingTableEntry entry,
             final Absolute type, final DOMDataTreeIdentifier path, final ContainerNode input) {
         var impls = entry.getImplementations(path);
         if (impls == null) {
index 55f81585c852a9c500d90d96b379d1f8056820f7..4d859fc141063f28467d34b21827f76cf3295e33 100644 (file)
@@ -38,14 +38,14 @@ import org.opendaylight.mdsal.dom.api.DOMActionAvailabilityExtension.Availabilit
 import org.opendaylight.mdsal.dom.api.DOMActionImplementation;
 import org.opendaylight.mdsal.dom.api.DOMActionInstance;
 import org.opendaylight.mdsal.dom.api.DOMActionNotAvailableException;
-import org.opendaylight.mdsal.dom.api.DOMActionResult;
 import org.opendaylight.mdsal.dom.api.DOMActionService;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
 import org.opendaylight.mdsal.dom.api.DOMRpcAvailabilityListener;
 import org.opendaylight.mdsal.dom.api.DOMRpcIdentifier;
 import org.opendaylight.mdsal.dom.api.DOMRpcImplementationNotAvailableException;
+import org.opendaylight.mdsal.dom.api.DOMRpcResult;
 import org.opendaylight.mdsal.dom.api.DOMSchemaService;
-import org.opendaylight.mdsal.dom.spi.SimpleDOMActionResult;
+import org.opendaylight.mdsal.dom.spi.DefaultDOMRpcResult;
 import org.opendaylight.yangtools.concepts.Registration;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
@@ -61,19 +61,19 @@ public class DOMRpcRouterTest {
         new NodeIdentifier(Actions.FOO), NodeIdentifierWithPredicates.of(Actions.FOO, Actions.BAR, "good"));
 
     private static final DOMActionImplementation IMPL =
-        (type, path, input) -> Futures.immediateFuture(new SimpleDOMActionResult(
+        (type, path, input) -> Futures.immediateFuture(new DefaultDOMRpcResult(
             ImmutableNodes.newContainerBuilder().withNodeIdentifier(new NodeIdentifier(Actions.OUTPUT)).build()));
 
     @Test
     public void registerRpcImplementation() {
-        try (DOMRpcRouter rpcRouter = rpcsRouter()) {
+        try (var rpcRouter = rpcsRouter()) {
             assertOperationKeys(rpcRouter);
 
-            final Registration fooReg = rpcRouter.rpcProviderService().registerRpcImplementation(
+            final var fooReg = rpcRouter.rpcProviderService().registerRpcImplementation(
                 getTestRpcImplementation(), DOMRpcIdentifier.create(Rpcs.FOO, null));
             assertOperationKeys(rpcRouter, Rpcs.FOO);
 
-            final Registration barReg = rpcRouter.rpcProviderService().registerRpcImplementation(
+            final var barReg = rpcRouter.rpcProviderService().registerRpcImplementation(
                 getTestRpcImplementation(), DOMRpcIdentifier.create(Rpcs.BAR, null));
             assertOperationKeys(rpcRouter, Rpcs.FOO, Rpcs.BAR);
 
@@ -86,14 +86,14 @@ public class DOMRpcRouterTest {
 
     @Test
     public void registerRpcImplementations() {
-        try (DOMRpcRouter rpcRouter = rpcsRouter()) {
+        try (var rpcRouter = rpcsRouter()) {
             assertOperationKeys(rpcRouter);
 
-            final Registration fooReg = rpcRouter.rpcProviderService().registerRpcImplementations(
+            final var fooReg = rpcRouter.rpcProviderService().registerRpcImplementations(
                 Map.of(DOMRpcIdentifier.create(Rpcs.FOO, null), getTestRpcImplementation()));
             assertOperationKeys(rpcRouter, Rpcs.FOO);
 
-            final Registration barReg = rpcRouter.rpcProviderService().registerRpcImplementations(
+            final var barReg = rpcRouter.rpcProviderService().registerRpcImplementations(
                 Map.of(
                     DOMRpcIdentifier.create(Rpcs.BAR, null), getTestRpcImplementation(),
                     DOMRpcIdentifier.create(Rpcs.BAZ, null), getTestRpcImplementation()));
@@ -179,7 +179,7 @@ public class DOMRpcRouterTest {
     public void testClose() {
         final var reg = mock(Registration.class);
         doNothing().when(reg).close();
-        final DOMSchemaService schema = mock(DOMSchemaService.class);
+        final var schema = mock(DOMSchemaService.class);
         doReturn(reg).when(schema).registerSchemaContextListener(any());
 
         final var rpcRouter = new DOMRpcRouter(schema);
@@ -245,17 +245,17 @@ public class DOMRpcRouterTest {
 
     private static void assertAvailable(final DOMActionService actionService, final YangInstanceIdentifier path)
             throws ExecutionException {
-        final DOMActionResult result = Futures.getDone(invokeBaz(actionService, path));
-        assertEquals(List.of(), result.getErrors());
+        final var result = Futures.getDone(invokeBaz(actionService, path));
+        assertEquals(List.of(), result.errors());
     }
 
     private static void assertUnavailable(final DOMActionService actionService, final YangInstanceIdentifier path) {
-        final ListenableFuture<? extends DOMActionResult> future = invokeBaz(actionService, path);
-        final ExecutionException ex = assertThrows(ExecutionException.class, () -> Futures.getDone(future));
+        final var future = invokeBaz(actionService, path);
+        final var ex = assertThrows(ExecutionException.class, () -> Futures.getDone(future));
         assertThat(ex.getCause(), instanceOf(DOMActionNotAvailableException.class));
     }
 
-    private static ListenableFuture<? extends DOMActionResult> invokeBaz(final DOMActionService actionService,
+    private static ListenableFuture<? extends DOMRpcResult> invokeBaz(final DOMActionService actionService,
             final YangInstanceIdentifier path) {
         return actionService.invokeAction(Actions.BAZ_TYPE,
             DOMDataTreeIdentifier.of(LogicalDatastoreType.OPERATIONAL, path),
index 7974cc1582d66d0eee6bc2919943fb47e79d5af2..39404cbddc6d045e45fd78f96be7ac6bc20ad03b 100644 (file)
@@ -10,8 +10,8 @@ package org.opendaylight.mdsal.dom.spi;
 import static java.util.Objects.requireNonNull;
 
 import com.google.common.annotations.Beta;
+import com.google.common.base.MoreObjects;
 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-import java.io.Serial;
 import java.io.Serializable;
 import java.util.Collection;
 import java.util.List;
@@ -20,7 +20,10 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
 import org.opendaylight.mdsal.dom.api.DOMRpcResult;
 import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.yang.common.ErrorTag;
+import org.opendaylight.yangtools.yang.common.ErrorType;
 import org.opendaylight.yangtools.yang.common.RpcError;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
 
 /**
@@ -29,16 +32,16 @@ import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
 @Beta
 @NonNullByDefault
 public final class DefaultDOMRpcResult implements DOMRpcResult, Immutable, Serializable {
-    @Serial
+    @java.io.Serial
     private static final long serialVersionUID = 1L;
 
     @SuppressFBWarnings(value = "SE_BAD_FIELD", justification = "Interfaces do not specify Serializable")
-    private final @Nullable ContainerNode result;
+    private final @Nullable ContainerNode value;
     // FIXME: a plain Collection is bad for equality
     private final Collection<? extends RpcError> errors;
 
-    public DefaultDOMRpcResult(final ContainerNode value, final RpcError... errors) {
-        this(value, List.of(errors));
+    public DefaultDOMRpcResult(final RpcError error) {
+        this(null, List.of(error));
     }
 
     public DefaultDOMRpcResult(final RpcError... errors) {
@@ -49,8 +52,16 @@ public final class DefaultDOMRpcResult implements DOMRpcResult, Immutable, Seria
         this(result, List.of());
     }
 
-    public DefaultDOMRpcResult(final @Nullable ContainerNode value, final Collection<? extends RpcError> errors) {
-        result = value;
+    public DefaultDOMRpcResult(final ContainerNode value, final RpcError error) {
+        this(value, List.of(error));
+    }
+
+    public DefaultDOMRpcResult(final ContainerNode value, final RpcError... errors) {
+        this(value, List.of(errors));
+    }
+
+    public DefaultDOMRpcResult(final @Nullable ContainerNode result, final Collection<? extends RpcError> errors) {
+        value = result;
         this.errors = requireNonNull(errors);
     }
 
@@ -58,6 +69,12 @@ public final class DefaultDOMRpcResult implements DOMRpcResult, Immutable, Seria
         this(null, errors);
     }
 
+    // As per RFC7950 page 80 (top)
+    public static DOMRpcResult ofMalformedMessage(final Exception cause) {
+        return new DefaultDOMRpcResult(RpcResultBuilder.newError(ErrorType.RPC, ErrorTag.MALFORMED_MESSAGE,
+            cause.getMessage(), null, null, requireNonNull(cause)));
+    }
+
     @Override
     public Collection<? extends RpcError> errors() {
         return errors;
@@ -65,14 +82,15 @@ public final class DefaultDOMRpcResult implements DOMRpcResult, Immutable, Seria
 
     @Override
     public @Nullable ContainerNode value() {
-        return result;
+        return value;
     }
 
     @Override
     public int hashCode() {
         int ret = errors.hashCode();
-        if (result != null) {
-            ret = 31 * ret + result.hashCode();
+        final var local = value;
+        if (local != null) {
+            ret = 31 * ret + local.hashCode();
         }
         return ret;
     }
@@ -80,6 +98,14 @@ public final class DefaultDOMRpcResult implements DOMRpcResult, Immutable, Seria
     @Override
     public boolean equals(final @Nullable Object obj) {
         return this == obj || obj instanceof DefaultDOMRpcResult other && errors.equals(other.errors)
-            && Objects.equals(result, other.result);
+            && Objects.equals(value, other.value);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this).omitNullValues()
+            .add("value", value)
+            .add("errors", errors.isEmpty() ? null : errors)
+            .toString();
     }
 }
index 99d0e6e46aeb4ba291a4ba5d60682b220464d0fd..29d866cd6a44ac6812cc7992d6d126c731c34047 100644 (file)
@@ -9,9 +9,9 @@ package org.opendaylight.mdsal.dom.spi;
 
 import com.google.common.util.concurrent.ListenableFuture;
 import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.opendaylight.mdsal.dom.api.DOMActionResult;
 import org.opendaylight.mdsal.dom.api.DOMActionService;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
+import org.opendaylight.mdsal.dom.api.DOMRpcResult;
 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute;
 
@@ -19,8 +19,8 @@ import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absol
 public abstract class ForwardingDOMActionService
         extends ForwardingDOMService<DOMActionService, DOMActionService.Extension> implements DOMActionService {
     @Override
-    public ListenableFuture<? extends DOMActionResult> invokeAction(final Absolute type,
-            final DOMDataTreeIdentifier path, final ContainerNode input) {
+    public ListenableFuture<? extends DOMRpcResult> invokeAction(final Absolute type, final DOMDataTreeIdentifier path,
+            final ContainerNode input) {
         return delegate().invokeAction(type, path, input);
     }
 }
diff --git a/dom/mdsal-dom-spi/src/main/java/org/opendaylight/mdsal/dom/spi/SimpleDOMActionResult.java b/dom/mdsal-dom-spi/src/main/java/org/opendaylight/mdsal/dom/spi/SimpleDOMActionResult.java
deleted file mode 100644 (file)
index 1b74959..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.mdsal.dom.spi;
-
-import static java.util.Objects.requireNonNull;
-
-import com.google.common.annotations.Beta;
-import com.google.common.base.MoreObjects;
-import com.google.common.collect.ImmutableList;
-import java.util.Collection;
-import java.util.Optional;
-import org.eclipse.jdt.annotation.NonNull;
-import org.eclipse.jdt.annotation.Nullable;
-import org.opendaylight.mdsal.dom.api.DOMActionResult;
-import org.opendaylight.yangtools.concepts.Immutable;
-import org.opendaylight.yangtools.yang.common.ErrorTag;
-import org.opendaylight.yangtools.yang.common.ErrorType;
-import org.opendaylight.yangtools.yang.common.RpcError;
-import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
-import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
-
-@Beta
-public final class SimpleDOMActionResult implements DOMActionResult, Immutable {
-    private final @NonNull Collection<RpcError> errors;
-    private final @Nullable ContainerNode output;
-
-    private SimpleDOMActionResult(final @NonNull Collection<RpcError> errors, final @Nullable ContainerNode output) {
-        this.errors = ImmutableList.copyOf(errors);
-        this.output = output;
-    }
-
-    public SimpleDOMActionResult(final @NonNull ContainerNode output) {
-        errors = ImmutableList.of();
-        this.output = requireNonNull(output);
-    }
-
-    public SimpleDOMActionResult(final @NonNull Collection<RpcError> errors) {
-        this(errors, null);
-    }
-
-    public SimpleDOMActionResult(final @NonNull ContainerNode output, final @NonNull Collection<RpcError> errors) {
-        this(errors, requireNonNull(output));
-    }
-
-    // As per RFC7950 page 80 (top)
-    public static @NonNull SimpleDOMActionResult ofMalformedMessage(final @NonNull Exception cause) {
-        return new SimpleDOMActionResult(ImmutableList.of(RpcResultBuilder.newError(ErrorType.RPC,
-            ErrorTag.MALFORMED_MESSAGE, cause.getMessage(), null, null, requireNonNull(cause))), null);
-    }
-
-    @Override
-    public Collection<RpcError> getErrors() {
-        return errors;
-    }
-
-    @Override
-    public Optional<ContainerNode> getOutput() {
-        return Optional.ofNullable(output);
-    }
-
-    @Override
-    public String toString() {
-        final var helper = MoreObjects.toStringHelper(this).omitNullValues().add("output", output);
-        if (!errors.isEmpty()) {
-            helper.add("errors", errors);
-        }
-        return helper.toString();
-    }
-}