Add ActionService adapter 00/73900/55
authorRobert Varga <robert.varga@pantheon.tech>
Fri, 13 Jul 2018 20:49:53 +0000 (22:49 +0200)
committerTom Pantelis <tompantelis@gmail.com>
Mon, 16 Jul 2018 21:56:58 +0000 (21:56 +0000)
A mostly straightforward implementation of ActionService on top
of a DOMOperationService.

Change-Id: Ibcfd72f3446b5cad0a3b6ced4852a8e77fd7e652
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
12 files changed:
binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/ActionService.java
binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/ActionAdapter.java [new file with mode: 0644]
binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/ActionServiceAdapter.java [new file with mode: 0644]
binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingAdapterFactory.java
binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/RpcResultUtil.java [new file with mode: 0644]
binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/RpcServiceAdapter.java
binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/osgi/DynamicBindingAdapter.java
binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/spi/AdapterFactory.java
binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/AbstractAdapterTest.java [new file with mode: 0644]
binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/ActionServiceAdapterTest.java [new file with mode: 0644]
binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMDataTreeServiceAdapterTest.java
dom/mdsal-dom-spi/src/main/java/org/opendaylight/mdsal/dom/spi/SimpleDOMOperationResult.java

index a82909ecf8a642f5714b80f2b6faf0a723028226..7b272ba101f09f6f2763757bdcf0f7f5fb9c614a 100644 (file)
@@ -53,7 +53,7 @@ public interface ActionService extends BindingService {
      * @throws NullPointerException if {@code actionInterface} is null
      * @throws IllegalArgumentException when {@code actionInterface} does not conform to the Binding Specification
      */
-    <O extends DataObject, T extends Action<O, ?, ?>> T getActionHandle(Class<T> actionInterface,
+    <O extends DataObject, T extends Action<?, ?, ?>> T getActionHandle(Class<T> actionInterface,
             Set<DataTreeIdentifier<O>> validNodes);
 
     default <O extends DataObject, T extends Action<O, ?, ?>> T getActionHandle(final Class<T> actionInterface) {
diff --git a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/ActionAdapter.java b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/ActionAdapter.java
new file mode 100644 (file)
index 0000000..ce3703f
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * 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.binding.dom.adapter;
+
+import static java.util.Objects.requireNonNull;
+import static org.opendaylight.yangtools.yang.common.YangConstants.operationInputQName;
+
+import com.google.common.util.concurrent.FluentFuture;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.MoreExecutors;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
+import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
+import org.opendaylight.mdsal.dom.api.DOMOperationResult;
+import org.opendaylight.mdsal.dom.api.DOMOperationService;
+import org.opendaylight.yangtools.yang.binding.Action;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.RpcInput;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+
+@NonNullByDefault
+final class ActionAdapter extends AbstractBindingAdapter<DOMOperationService> implements InvocationHandler {
+    private final Class<? extends Action<?, ?, ?>> type;
+    private final NodeIdentifier inputName;
+    private final SchemaPath schemaPath;
+
+    ActionAdapter(final BindingToNormalizedNodeCodec codec, final DOMOperationService delegate,
+            final Class<? extends Action<?, ?, ?>> type) {
+        super(codec, delegate);
+        this.type = requireNonNull(type);
+        this.schemaPath = getCodec().getActionPath(type);
+        this.inputName = NodeIdentifier.create(operationInputQName(schemaPath.getLastComponent().getModule()));
+    }
+
+    @Override public @Nullable Object invoke(final @Nullable Object proxy, final @Nullable Method method,
+            final Object @Nullable [] args) throws Throwable {
+        switch (method.getName()) {
+            case "equals":
+                if (args.length == 1) {
+                    return proxy == args[0];
+                }
+                break;
+            case "hashCode":
+                if (args.length == 0) {
+                    return System.identityHashCode(proxy);
+                }
+                break;
+            case "toString":
+                if (args.length == 0) {
+                    return type.getName() + "$Adapter{delegate=" + getDelegate() + "}";
+                }
+                break;
+            case "invoke":
+                if (args.length == 2) {
+                    final InstanceIdentifier<?> path = (InstanceIdentifier<?>) requireNonNull(args[0]);
+                    final RpcInput input = (RpcInput) requireNonNull(args[1]);
+                    final FluentFuture<DOMOperationResult> future = getDelegate().invokeAction(schemaPath,
+                        new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, getCodec().toNormalized(path)),
+                        getCodec().toLazyNormalizedNodeActionInput(type, inputName, input));
+
+                    // Invocation returned a future we know about -- return that future instead
+                    if (future instanceof BindingRpcFutureAware) {
+                        return ((BindingRpcFutureAware) future).getBindingFuture();
+                    }
+
+                    return Futures.transform(future,
+                        dom -> RpcResultUtil.rpcResultFromDOM(dom.getErrors(), dom.getOutput()
+                            .map(output -> getCodec().fromNormalizedNodeActionOutput(type, output))
+                            .orElse(null)), MoreExecutors.directExecutor());
+                }
+                break;
+            default:
+                break;
+        }
+
+        throw new NoSuchMethodError("Method " + method.toString() + "is unsupported.");
+    }
+}
diff --git a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/ActionServiceAdapter.java b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/ActionServiceAdapter.java
new file mode 100644 (file)
index 0000000..ad85ca1
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * 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.binding.dom.adapter;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.util.concurrent.FluentFuture;
+import java.lang.reflect.Proxy;
+import java.util.Set;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.mdsal.binding.api.ActionService;
+import org.opendaylight.mdsal.binding.api.DataTreeIdentifier;
+import org.opendaylight.mdsal.dom.api.DOMOperationService;
+import org.opendaylight.yangtools.concepts.Delegator;
+import org.opendaylight.yangtools.yang.binding.Action;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.RpcInput;
+import org.opendaylight.yangtools.yang.binding.RpcOutput;
+import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+
+@Beta
+@NonNullByDefault
+final class ActionServiceAdapter
+        extends AbstractBindingLoadingAdapter<DOMOperationService, Class<? extends Action<?, ?, ?>>, ActionAdapter>
+        implements ActionService {
+    private static final class ConstrainedAction implements Delegator<Action<?, ?, ?>>,
+            Action<DataObject, RpcInput, RpcOutput> {
+        private final Action<DataObject, RpcInput, RpcOutput> delegate;
+        private final Set<? extends DataTreeIdentifier<?>> nodes;
+
+        ConstrainedAction(final Action<?, ?, ?> delegate, final Set<? extends DataTreeIdentifier<?>> nodes) {
+            this.delegate = requireNonNull((Action) delegate);
+            this.nodes = requireNonNull(nodes);
+        }
+
+        @Override
+        public FluentFuture<RpcResult<RpcOutput>> invoke(final InstanceIdentifier<DataObject> path,
+                final RpcInput input) {
+            checkState(nodes.contains(path), "Cannot service %s", path);
+            return delegate.invoke(path, input);
+        }
+
+        @Override
+        public Action<?, ?, ?> getDelegate() {
+            return delegate;
+        }
+    }
+
+    ActionServiceAdapter(final BindingToNormalizedNodeCodec codec, final DOMOperationService delegate) {
+        super(codec, delegate);
+    }
+
+    @Override
+    public <O extends DataObject, T extends Action<?, ?, ?>> T getActionHandle(final Class<T> actionInterface,
+            final Set<DataTreeIdentifier<O>> nodes) {
+        return !nodes.isEmpty() ? (T) new ConstrainedAction(getActionHandle(actionInterface, ImmutableSet.of()), nodes)
+                : (T) Proxy.newProxyInstance(actionInterface.getClassLoader(), new Class[] { actionInterface },
+                    getAdapter(actionInterface));
+    }
+
+    @Override
+    ActionAdapter loadAdapter(final Class<? extends Action<?, ?, ?>> key) {
+        checkArgument(BindingReflections.isBindingClass(key));
+        checkArgument(key.isInterface(), "Supplied Action type must be an interface.");
+        return new ActionAdapter(getCodec(), getDelegate(), key);
+    }
+}
index 79ac49860bdaa1ff3ae3c15725958de8dcdfd175..374758be102e7e71253b2152054fc26c87276182 100644 (file)
@@ -12,6 +12,7 @@ import static java.util.Objects.requireNonNull;
 import com.google.common.annotations.Beta;
 import javax.annotation.concurrent.ThreadSafe;
 import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.mdsal.binding.api.ActionService;
 import org.opendaylight.mdsal.binding.api.DataBroker;
 import org.opendaylight.mdsal.binding.api.DataTreeService;
 import org.opendaylight.mdsal.binding.api.MountPointService;
@@ -25,6 +26,7 @@ import org.opendaylight.mdsal.dom.api.DOMDataTreeService;
 import org.opendaylight.mdsal.dom.api.DOMMountPointService;
 import org.opendaylight.mdsal.dom.api.DOMNotificationPublishService;
 import org.opendaylight.mdsal.dom.api.DOMNotificationService;
+import org.opendaylight.mdsal.dom.api.DOMOperationService;
 import org.opendaylight.mdsal.dom.api.DOMRpcProviderService;
 import org.opendaylight.mdsal.dom.api.DOMRpcService;
 
@@ -126,4 +128,9 @@ public final class BindingAdapterFactory implements AdapterFactory {
     public RpcProviderService createRpcProviderService(final DOMRpcProviderService domService) {
         return new BindingDOMRpcProviderServiceAdapter(domService, codec);
     }
+
+    @Override
+    public ActionService createActionService(final DOMOperationService domService) {
+        return new ActionServiceAdapter(codec, domService);
+    }
 }
diff --git a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/RpcResultUtil.java b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/RpcResultUtil.java
new file mode 100644 (file)
index 0000000..c29e3f0
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * 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.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.DOMOperationResult;
+import org.opendaylight.mdsal.dom.api.DOMRpcResult;
+import org.opendaylight.yangtools.yang.common.RpcError;
+import org.opendaylight.yangtools.yang.common.RpcError.ErrorSeverity;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
+
+/**
+ * Utility methods for converting {@link RpcResult} to/from {@link DOMOperationResult} and {@link DOMRpcResult}.
+ */
+@NonNullByDefault
+final class RpcResultUtil {
+    private RpcResultUtil() {
+
+    }
+
+    /**
+     * DOMRpcResult does not have a notion of success, hence we have to reverse-engineer it by looking at reported
+     * errors and checking whether they are just warnings.
+     */
+    static <T> RpcResult<T> rpcResultFromDOM(final Collection<RpcError> errors, final @Nullable T result) {
+        return RpcResultBuilder.<T>status(errors.stream().noneMatch(err -> err.getSeverity() == ErrorSeverity.ERROR))
+                .withResult(result).withRpcErrors(errors).build();
+    }
+}
index f7ec742788d882d3b078adc6917f4786ce506028..e83573599681cd8447840d5e7b6eccd529fde0be 100644 (file)
@@ -16,7 +16,6 @@ import com.google.common.util.concurrent.MoreExecutors;
 import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.Method;
 import java.lang.reflect.Proxy;
-import java.util.Collection;
 import java.util.Map.Entry;
 import org.opendaylight.mdsal.binding.dom.codec.impl.BindingNormalizedNodeCodecRegistry;
 import org.opendaylight.mdsal.dom.api.DOMRpcResult;
@@ -28,10 +27,7 @@ import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.binding.RpcService;
 import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
 import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.common.RpcError;
-import org.opendaylight.yangtools.yang.common.RpcError.ErrorSeverity;
 import org.opendaylight.yangtools.yang.common.RpcResult;
-import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
 import org.opendaylight.yangtools.yang.common.YangConstants;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
@@ -157,12 +153,7 @@ class RpcServiceAdapter implements InvocationHandler {
                     bindingResult = null;
                 }
 
-                // DOMRpcResult does not have a notion of success, hence we have to reverse-engineer it by looking
-                // at reported errors and checking whether they are just warnings.
-                final Collection<RpcError> errors = input.getErrors();
-                return RpcResult.class.cast(RpcResultBuilder.status(errors.stream()
-                    .noneMatch(error -> error.getSeverity() == ErrorSeverity.ERROR))
-                    .withResult(bindingResult).withRpcErrors(errors).build());
+                return RpcResultUtil.rpcResultFromDOM(input.getErrors(), bindingResult);
             }, MoreExecutors.directExecutor());
         }
     }
index 2991ac1ab3e52dcdb5468e0f5a053632731cd7e8..66e45245fff315909f1cc2d961b36ea3bc63c330 100644 (file)
@@ -10,6 +10,7 @@ package org.opendaylight.mdsal.binding.dom.adapter.osgi;
 import com.google.common.collect.ImmutableList;
 import java.util.List;
 import javax.annotation.concurrent.GuardedBy;
+import org.opendaylight.mdsal.binding.api.ActionService;
 import org.opendaylight.mdsal.binding.api.BindingService;
 import org.opendaylight.mdsal.binding.api.DataBroker;
 import org.opendaylight.mdsal.binding.api.DataTreeService;
@@ -24,6 +25,7 @@ import org.opendaylight.mdsal.dom.api.DOMDataTreeService;
 import org.opendaylight.mdsal.dom.api.DOMMountPointService;
 import org.opendaylight.mdsal.dom.api.DOMNotificationPublishService;
 import org.opendaylight.mdsal.dom.api.DOMNotificationService;
+import org.opendaylight.mdsal.dom.api.DOMOperationService;
 import org.opendaylight.mdsal.dom.api.DOMRpcProviderService;
 import org.opendaylight.mdsal.dom.api.DOMRpcService;
 import org.opendaylight.mdsal.dom.api.DOMService;
@@ -57,7 +59,8 @@ public final class DynamicBindingAdapter implements AutoCloseable {
             new AdaptingTracker<>(ctx, DOMRpcService.class, RpcConsumerRegistry.class,
                     factory::createRpcConsumerRegistry),
             new AdaptingTracker<>(ctx, DOMRpcProviderService.class, RpcProviderService.class,
-                    factory::createRpcProviderService));
+                    factory::createRpcProviderService),
+            new AdaptingTracker<>(ctx, DOMOperationService.class, ActionService.class, factory::createActionService));
 
         LOG.debug("Starting {} DOMService trackers", trackers.size());
         trackers.forEach(ServiceTracker::open);
index b96ad2646323436d426a537f63391dc03a435441..8845be6d18964363efea00687d9a93a672adead5 100644 (file)
@@ -9,6 +9,7 @@ package org.opendaylight.mdsal.binding.dom.adapter.spi;
 
 import com.google.common.annotations.Beta;
 import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.mdsal.binding.api.ActionService;
 import org.opendaylight.mdsal.binding.api.BindingService;
 import org.opendaylight.mdsal.binding.api.DataBroker;
 import org.opendaylight.mdsal.binding.api.DataTreeService;
@@ -22,6 +23,7 @@ import org.opendaylight.mdsal.dom.api.DOMDataTreeService;
 import org.opendaylight.mdsal.dom.api.DOMMountPointService;
 import org.opendaylight.mdsal.dom.api.DOMNotificationPublishService;
 import org.opendaylight.mdsal.dom.api.DOMNotificationService;
+import org.opendaylight.mdsal.dom.api.DOMOperationService;
 import org.opendaylight.mdsal.dom.api.DOMRpcProviderService;
 import org.opendaylight.mdsal.dom.api.DOMRpcService;
 import org.opendaylight.mdsal.dom.api.DOMService;
@@ -96,4 +98,13 @@ public interface AdapterFactory {
      * @throws NullPointerException if {@code domService} is null
      */
     RpcProviderService createRpcProviderService(DOMRpcProviderService domService);
+
+    /**
+     * Create a {@link ActionService} backed by a {@link DOMOperationService}.
+     *
+     * @param domService Backing DOMOperationService
+     * @return A ActionService
+     * @throws NullPointerException if {@code domService} is null
+     */
+    ActionService createActionService(DOMOperationService domService);
 }
diff --git a/binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/AbstractAdapterTest.java b/binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/AbstractAdapterTest.java
new file mode 100644 (file)
index 0000000..34f8062
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * 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.binding.dom.adapter;
+
+import com.google.common.util.concurrent.MoreExecutors;
+import org.junit.Before;
+import org.opendaylight.mdsal.binding.dom.adapter.test.util.BindingBrokerTestFactory;
+import org.opendaylight.mdsal.binding.dom.adapter.test.util.BindingTestContext;
+
+public abstract class AbstractAdapterTest {
+    protected BindingToNormalizedNodeCodec codec;
+
+    @Before
+    public void before() {
+        final BindingBrokerTestFactory testFactory = new BindingBrokerTestFactory();
+        testFactory.setExecutor(MoreExecutors.newDirectExecutorService());
+        final BindingTestContext testContext = testFactory.getTestContext();
+        testContext.start();
+        codec = testContext.getCodec();
+    }
+}
diff --git a/binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/ActionServiceAdapterTest.java b/binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/ActionServiceAdapterTest.java
new file mode 100644 (file)
index 0000000..ffe84cf
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * 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.binding.dom.adapter;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doReturn;
+import static org.opendaylight.yangtools.yang.common.YangConstants.operationInputQName;
+import static org.opendaylight.yangtools.yang.common.YangConstants.operationOutputQName;
+import static org.opendaylight.yangtools.yang.data.impl.schema.Builders.containerBuilder;
+import static org.opendaylight.yangtools.yang.data.impl.schema.Builders.leafBuilder;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.util.concurrent.FluentFuture;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.SettableFuture;
+import java.util.concurrent.ExecutionException;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.mdsal.binding.api.ActionService;
+import org.opendaylight.mdsal.dom.api.DOMOperationResult;
+import org.opendaylight.mdsal.dom.api.DOMOperationService;
+import org.opendaylight.mdsal.dom.spi.SimpleDOMOperationResult;
+import org.opendaylight.yang.gen.v1.urn.odl.actions.norev.Cont;
+import org.opendaylight.yang.gen.v1.urn.odl.actions.norev.cont.Foo;
+import org.opendaylight.yang.gen.v1.urn.odl.actions.norev.cont.foo.Input;
+import org.opendaylight.yang.gen.v1.urn.odl.actions.norev.cont.foo.InputBuilder;
+import org.opendaylight.yang.gen.v1.urn.odl.actions.norev.cont.foo.Output;
+import org.opendaylight.yang.gen.v1.urn.odl.actions.norev.cont.foo.OutputBuilder;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.RpcOutput;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+
+public class ActionServiceAdapterTest extends AbstractAdapterTest {
+    private static final NodeIdentifier FOO_INPUT = NodeIdentifier.create(operationInputQName(Foo.QNAME.getModule()));
+    private static final NodeIdentifier FOO_OUTPUT = NodeIdentifier.create(operationOutputQName(Foo.QNAME.getModule()));
+    private static final NodeIdentifier FOO_XYZZY = NodeIdentifier.create(QName.create(Foo.QNAME, "xyzzy"));
+    private static final ContainerNode DOM_FOO_INPUT = containerBuilder().withNodeIdentifier(FOO_INPUT)
+            .withChild(leafBuilder().withNodeIdentifier(FOO_XYZZY).withValue("xyzzy").build())
+            .build();
+    private static final ContainerNode DOM_FOO_OUTPUT = containerBuilder().withNodeIdentifier(FOO_OUTPUT).build();
+    private static final Input BINDING_FOO_INPUT = new InputBuilder().setXyzzy("xyzzy").build();
+    private static final RpcOutput BINDING_FOO_OUTPUT = new OutputBuilder().build();
+
+    @Mock
+    private DOMOperationService delegate;
+
+    private ActionService service;
+
+    private SettableFuture<DOMOperationResult> domResult;
+
+    @Override
+    @Before
+    public void before() {
+        MockitoAnnotations.initMocks(this);
+        super.before();
+
+        domResult = SettableFuture.create();
+        doReturn(domResult).when(delegate).invokeAction(any(), any(), any());
+
+        service = new ActionServiceAdapter(codec, delegate);
+    }
+
+    @Test
+    public void testInvocation() throws ExecutionException {
+        final Foo handle = service.getActionHandle(Foo.class, ImmutableSet.of());
+        final FluentFuture<RpcResult<Output>> future = handle.invoke(InstanceIdentifier.create(Cont.class),
+            BINDING_FOO_INPUT);
+        assertNotNull(future);
+        assertFalse(future.isDone());
+        domResult.set(new SimpleDOMOperationResult(DOM_FOO_OUTPUT, ImmutableList.of()));
+        final RpcResult<Output> bindingResult = Futures.getDone(future);
+
+        assertEquals(ImmutableList.of(), bindingResult.getErrors());
+        assertEquals(BINDING_FOO_OUTPUT, bindingResult.getResult());
+    }
+
+}
index 152f484f3b016241edcd6c57c0980d4ca850d31d..c13646041619940fadd31a778b78d2102f51a536 100644 (file)
@@ -15,44 +15,39 @@ import static org.mockito.Mockito.verify;
 import static org.mockito.MockitoAnnotations.initMocks;
 
 import com.google.common.collect.ImmutableSet;
-import com.google.common.util.concurrent.MoreExecutors;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mock;
 import org.opendaylight.mdsal.binding.api.DataTreeListener;
-import org.opendaylight.mdsal.binding.dom.adapter.test.util.BindingBrokerTestFactory;
-import org.opendaylight.mdsal.binding.dom.adapter.test.util.BindingTestContext;
+import org.opendaylight.mdsal.binding.api.DataTreeLoopException;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeProducer;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeService;
 
-public class BindingDOMDataTreeServiceAdapterTest {
+public class BindingDOMDataTreeServiceAdapterTest extends AbstractAdapterTest {
 
     private BindingDOMDataTreeServiceAdapter bindingDOMDataTreeServiceAdapter;
-    private BindingToNormalizedNodeCodec codec;
 
     @Mock
     private DOMDataTreeService delegate;
 
+    @Override
     @Before
-    public void setUp() throws Exception {
+    public void before() {
         initMocks(this);
-        final BindingBrokerTestFactory testFactory = new BindingBrokerTestFactory();
-        testFactory.setExecutor(MoreExecutors.newDirectExecutorService());
-        final BindingTestContext testContext = testFactory.getTestContext();
-        testContext.start();
-        codec = testContext.getCodec();
+        super.before();
+
         bindingDOMDataTreeServiceAdapter = BindingDOMDataTreeServiceAdapter.create(delegate, codec);
     }
 
     @Test
-    public void createProducerTest() throws Exception {
+    public void createProducerTest() {
         doReturn(mock(DOMDataTreeProducer.class)).when(delegate).createProducer(any());
         assertNotNull(bindingDOMDataTreeServiceAdapter.createProducer(ImmutableSet.of()));
         verify(delegate).createProducer(any());
     }
 
     @Test(expected = UnsupportedOperationException.class)
-    public void registerListenerTest() throws Exception {
+    public void registerListenerTest() throws DataTreeLoopException {
         bindingDOMDataTreeServiceAdapter.registerListener(mock(DataTreeListener.class), ImmutableSet.of(), false,
                 ImmutableSet.of());
     }
index 67b449fe368563e920f05fd9672d8982f194a8fd..0ec10c47b8e0a13d6824815d02bf2bdab5b02572 100644 (file)
@@ -32,7 +32,7 @@ public final class SimpleDOMOperationResult implements DOMOperationResult, Immut
 
     private SimpleDOMOperationResult(final Collection<RpcError> errors, final @Nullable ContainerNode output) {
         this.errors = ImmutableList.copyOf(errors);
-        this.output = null;
+        this.output = output;
     }
 
     public SimpleDOMOperationResult(final Collection<RpcError> errors) {