Teach binding-dom-codec about actions 95/73995/32
authorRobert Varga <robert.varga@pantheon.tech>
Thu, 12 Jul 2018 17:32:20 +0000 (19:32 +0200)
committerRobert Varga <robert.varga@pantheon.tech>
Sat, 14 Jul 2018 16:22:23 +0000 (18:22 +0200)
Serialization of actions is slightly different than serialization
of RPCs or notifications. Teach BindingCodecContext how to deal
with them.

Change-Id: Id763d4034af394cf97d5497635f7188d8743fdeb
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingToNormalizedNodeCodec.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/api/BindingNormalizedNodeSerializer.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/api/BindingNormalizedNodeWriterFactory.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/ActionCodecContext.java [new file with mode: 0644]
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/BindingCodecContext.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/BindingNormalizedNodeCodecRegistry.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/SchemaRootCodecContext.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/util/AbstractBindingLazyContainerNode.java
binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/test/ActionSerializeDeserializeTest.java [new file with mode: 0644]
binding/mdsal-binding-test-model/src/main/yang/actions.yang
dom/mdsal-dom-api/src/main/java/org/opendaylight/mdsal/dom/api/DOMOperationImplementation.java

index 80af970f5cf56b1d0f338ff1a2c6103e99c3b74a..04c510e3d3a7b9f084396d3d9acd8dfbdb242a74 100644 (file)
@@ -33,6 +33,7 @@ import org.opendaylight.mdsal.binding.api.DataTreeIdentifier;
 import org.opendaylight.mdsal.binding.dom.codec.api.BindingCodecTree;
 import org.opendaylight.mdsal.binding.dom.codec.api.BindingCodecTreeFactory;
 import org.opendaylight.mdsal.binding.dom.codec.api.BindingCodecTreeNode;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingLazyContainerNode;
 import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer;
 import org.opendaylight.mdsal.binding.dom.codec.gen.impl.StreamWriterGenerator;
 import org.opendaylight.mdsal.binding.dom.codec.impl.BindingNormalizedNodeCodecRegistry;
@@ -50,10 +51,13 @@ import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument;
 import org.opendaylight.yangtools.yang.binding.Notification;
+import org.opendaylight.yangtools.yang.binding.RpcInput;
+import org.opendaylight.yangtools.yang.binding.RpcOutput;
 import org.opendaylight.yangtools.yang.binding.RpcService;
 import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
 import org.opendaylight.yangtools.yang.common.QNameModule;
 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.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.data.impl.codec.DeserializationException;
@@ -196,6 +200,18 @@ public class BindingToNormalizedNodeCodec implements BindingCodecTreeFactory,
         return codecRegistry.fromNormalizedNodeRpcData(path, data);
     }
 
+    @Override
+    public <T extends RpcInput> T fromNormalizedNodeActionInput(final Class<? extends Action<?, ?, ?>> action,
+            final ContainerNode input) {
+        return codecRegistry.fromNormalizedNodeActionInput(action, input);
+    }
+
+    @Override
+    public <T extends RpcOutput> T fromNormalizedNodeActionOutput(final Class<? extends Action<?, ?, ?>> action,
+            final ContainerNode output) {
+        return codecRegistry.fromNormalizedNodeActionOutput(action, output);
+    }
+
     @Override
     public final InstanceIdentifier<?> fromYangInstanceIdentifier(@Nonnull final YangInstanceIdentifier dom) {
         return codecRegistry.fromYangInstanceIdentifier(dom);
@@ -211,6 +227,30 @@ public class BindingToNormalizedNodeCodec implements BindingCodecTreeFactory,
         return codecRegistry.toNormalizedNodeRpcData(data);
     }
 
+    @Override
+    public ContainerNode toNormalizedNodeActionInput(final Class<? extends Action<?, ?, ?>> action,
+            final RpcInput input) {
+        return codecRegistry.toNormalizedNodeActionInput(action, input);
+    }
+
+    @Override
+    public ContainerNode toNormalizedNodeActionOutput(final Class<? extends Action<?, ?, ?>> action,
+            final RpcOutput output) {
+        return codecRegistry.toNormalizedNodeActionOutput(action, output);
+    }
+
+    @Override
+    public BindingLazyContainerNode<RpcInput> toLazyNormalizedNodeActionInput(
+            final Class<? extends Action<?, ?, ?>> action, final NodeIdentifier identifier, final RpcInput input) {
+        return codecRegistry.toLazyNormalizedNodeActionInput(action, identifier, input);
+    }
+
+    @Override
+    public BindingLazyContainerNode<RpcOutput> toLazyNormalizedNodeActionOutput(
+            final Class<? extends Action<?, ?, ?>> action, final NodeIdentifier identifier, final RpcOutput output) {
+        return codecRegistry.toLazyNormalizedNodeActionOutput(action, identifier, output);
+    }
+
     /**
      * Returns a Binding-Aware instance identifier from normalized
      * instance-identifier if it is possible to create representation.
index d710933c088eebf6b7ca9a06b118c0531c8e9a48..a78a2964723408b99ccb0bbb6f252d995b918fdd 100644 (file)
@@ -7,14 +7,22 @@
  */
 package org.opendaylight.mdsal.binding.dom.codec.api;
 
+import com.google.common.annotations.Beta;
 import java.util.Map.Entry;
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.binding.Action;
 import org.opendaylight.yangtools.yang.binding.DataContainer;
 import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.binding.Notification;
+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.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.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
@@ -24,49 +32,37 @@ import org.opendaylight.yangtools.yang.model.api.SchemaPath;
  * Binding Data representation and NormalizedNode representation.
  */
 public interface BindingNormalizedNodeSerializer {
-
     /**
-     * Translates supplied Binding Instance Identifier into NormalizedNode
-     * instance identifier.
+     * Translates supplied Binding Instance Identifier into NormalizedNode instance identifier.
      *
-     * @param binding
-     *            Binding Instance Identifier
+     * @param binding Binding Instance Identifier
      * @return DOM Instance Identifier
-     * @throws IllegalArgumentException
-     *             If supplied Instance Identifier is not valid.
+     * @throws IllegalArgumentException If supplied Instance Identifier is not valid.
      */
     YangInstanceIdentifier toYangInstanceIdentifier(@Nonnull InstanceIdentifier<?> binding);
 
     /**
-     * Translates supplied YANG Instance Identifier into Binding instance
-     * identifier.
+     * Translates supplied YANG Instance Identifier into Binding instance identifier.
      *
-     * @param dom
-     *            YANG Instance Identifier
-     * @return Binding Instance Identifier, or null if the instance identifier
-     *         is not representable.
+     * @param dom YANG Instance Identifier
+     * @return Binding Instance Identifier, or null if the instance identifier is not representable.
      */
     @Nullable
-    InstanceIdentifier<?> fromYangInstanceIdentifier(@Nonnull YangInstanceIdentifier dom);
+    <T extends DataObject> InstanceIdentifier<T> fromYangInstanceIdentifier(@Nonnull YangInstanceIdentifier dom);
 
     /**
-     * Translates supplied Binding Instance Identifier and data into
-     * NormalizedNode representation.
+     * Translates supplied Binding Instance Identifier and data into NormalizedNode representation.
      *
-     * @param path
-     *            Binding Instance Identifier pointing to data
-     * @param data
-     *            Data object representing data
+     * @param path Binding Instance Identifier pointing to data
+     * @param data Data object representing data
      * @return NormalizedNode representation
-     * @throws IllegalArgumentException
-     *             If supplied Instance Identifier is not valid.
+     * @throws IllegalArgumentException If supplied Instance Identifier is not valid.
      */
     <T extends DataObject> Entry<YangInstanceIdentifier, NormalizedNode<?, ?>> toNormalizedNode(
             InstanceIdentifier<T> path, T data);
 
     /**
-     * Translates supplied YANG Instance Identifier and NormalizedNode into
-     * Binding data.
+     * Translates supplied YANG Instance Identifier and NormalizedNode into Binding data.
      *
      * @param path Binding Instance Identifier
      * @param data NormalizedNode representing data
@@ -94,6 +90,30 @@ public interface BindingNormalizedNodeSerializer {
      */
     @Nullable DataObject fromNormalizedNodeRpcData(@Nonnull SchemaPath path,@Nonnull ContainerNode data);
 
+    /**
+     * Translates supplied ContainerNode action input.
+     *
+     * @param action Binding action class
+     * @param input ContainerNode representing data
+     * @return Binding representation of action input
+     * @throws NullPointerException if any of the arguments is null
+     */
+    @Beta
+    <T extends RpcInput> @NonNull T fromNormalizedNodeActionInput(
+            @NonNull Class<? extends Action<?, ?, ?>> action, @NonNull ContainerNode input);
+
+    /**
+     * Translates supplied ContainerNode action output.
+     *
+     * @param action Binding action class
+     * @param output ContainerNode representing data
+     * @return Binding representation of action output
+     * @throws NullPointerException if any of the arguments is null
+     */
+    @Beta
+    <T extends RpcOutput> @NonNull T fromNormalizedNodeActionOutput(
+            @NonNull Class<? extends Action<?, ?, ?>> action, @NonNull ContainerNode output);
+
     /**
      * Translates supplied Binding Notification or output into NormalizedNode notification.
      *
@@ -109,4 +129,84 @@ public interface BindingNormalizedNodeSerializer {
      * @return NormalizedNode representation of rpc data
      */
     @Nonnull ContainerNode toNormalizedNodeRpcData(@Nonnull DataContainer data);
+
+    /**
+     * Lazily translates supplied Binding action input into NormalizedNode data.
+     *
+     * @param action Binding action class
+     * @param input Binding action input
+     * @return NormalizedNode representation of action input
+     * @throws NullPointerException if any of the arguments is null
+     */
+    @Beta
+    @NonNull BindingLazyContainerNode<RpcInput> toLazyNormalizedNodeActionInput(
+            @NonNull Class<? extends Action<?, ?, ?>> action, @NonNull NodeIdentifier identifier,
+                    @NonNull RpcInput input);
+
+    /**
+     * Lazily translates supplied Binding action input into NormalizedNode data.
+     *
+     * @param action Binding action class
+     * @param input Binding action input
+     * @return NormalizedNode representation of action input
+     * @throws NullPointerException if any of the arguments is null
+     */
+    @Beta default @NonNull BindingLazyContainerNode<RpcInput> toLazyNormalizedNodeActionInput(
+            @NonNull final Class<? extends Action<?, ?, ?>> action, @NonNull final RpcInput input) {
+        return toLazyNormalizedNodeActionInput(action,
+            new NodeIdentifier(YangConstants.operationInputQName(BindingReflections.getQNameModule(action))), input);
+    }
+
+    /**
+     * Translates supplied Binding action input into NormalizedNode data.
+     *
+     * @param action Binding action class
+     * @param input Binding action input
+     * @return NormalizedNode representation of action input
+     * @throws NullPointerException if any of the arguments is null
+     */
+    @Beta default @NonNull ContainerNode toNormalizedNodeActionInput(
+            @NonNull final Class<? extends Action<?, ?, ?>> action, @NonNull final RpcInput input) {
+        return toLazyNormalizedNodeActionInput(action,
+            new NodeIdentifier(YangConstants.operationInputQName(BindingReflections.getQNameModule(action))), input)
+                .getDelegate();
+    }
+
+    /**
+     * Lazily translates supplied Binding action output into NormalizedNode data.
+     *
+     * @param action Binding action class
+     * @param output Binding action output
+     * @return NormalizedNode representation of action output
+     */
+    @Beta
+    @NonNull BindingLazyContainerNode<RpcOutput> toLazyNormalizedNodeActionOutput(
+            @NonNull Class<? extends Action<?, ?, ?>> action, @NonNull NodeIdentifier identifier,
+                    @NonNull RpcOutput output);
+
+    /**
+     * Lazily translates supplied Binding action output into NormalizedNode data.
+     *
+     * @param action Binding action class
+     * @param output Binding action output
+     * @return NormalizedNode representation of action output
+     */
+    @Beta default @NonNull BindingLazyContainerNode<RpcOutput> toLazyNormalizedNodeActionOutput(
+            @NonNull final Class<? extends Action<?, ?, ?>> action, @NonNull final RpcOutput output) {
+        return toLazyNormalizedNodeActionOutput(action,
+            new NodeIdentifier(YangConstants.operationInputQName(BindingReflections.getQNameModule(action))), output);
+    }
+
+    /**
+     * Translates supplied Binding action output into NormalizedNode data.
+     *
+     * @param output Binding action output
+     * @return NormalizedNode representation of action output
+     */
+    @Beta default @NonNull ContainerNode toNormalizedNodeActionOutput(
+            @NonNull final Class<? extends Action<?, ?, ?>> action, @NonNull final RpcOutput output) {
+        return toLazyNormalizedNodeActionOutput(action,
+            new NodeIdentifier(YangConstants.operationInputQName(BindingReflections.getQNameModule(action))), output)
+                .getDelegate();
+    }
 }
index 3c277115f110360fa53d6e44413bc642344b642d..691ca3eb3da094574c292b507a6cfe419439c713 100644 (file)
@@ -9,6 +9,7 @@ package org.opendaylight.mdsal.binding.dom.codec.api;
 
 import java.util.Map.Entry;
 import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.yang.binding.Action;
 import org.opendaylight.yangtools.yang.binding.BindingStreamEventWriter;
 import org.opendaylight.yangtools.yang.binding.DataContainer;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
@@ -94,4 +95,28 @@ public interface BindingNormalizedNodeWriterFactory {
     @Nonnull
     BindingStreamEventWriter newNotificationWriter(@Nonnull Class<? extends Notification> notification,
             @Nonnull NormalizedNodeStreamWriter domWriter);
+
+    /**
+     * Creates a {@link BindingStreamEventWriter} for action input which will translate to NormalizedNode model
+     * and invoke proper events on supplied {@link NormalizedNodeStreamWriter}.
+     *
+     * @param action Binding class representing action for which writer should be instantiated
+     * @param domWriter Stream writer on which events will be invoked.
+     * @return {@link BindingStreamEventWriter} which will write to supplied {@link NormalizedNodeStreamWriter}.
+     */
+    @Nonnull
+    BindingStreamEventWriter newActionInputWriter(@Nonnull Class<? extends Action<?, ?, ?>> action,
+            @Nonnull NormalizedNodeStreamWriter domWriter);
+
+    /**
+     * Creates a {@link BindingStreamEventWriter} for action output which will translate to NormalizedNode model
+     * and invoke proper events on supplied {@link NormalizedNodeStreamWriter}.
+     *
+     * @param action Binding class representing action for which writer should be instantiated
+     * @param domWriter Stream writer on which events will be invoked.
+     * @return {@link BindingStreamEventWriter} which will write to supplied {@link NormalizedNodeStreamWriter}.
+     */
+    @Nonnull
+    BindingStreamEventWriter newActionOutputWriter(@Nonnull Class<? extends Action<?, ?, ?>> action,
+            @Nonnull NormalizedNodeStreamWriter domWriter);
 }
diff --git a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/ActionCodecContext.java b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/ActionCodecContext.java
new file mode 100644 (file)
index 0000000..c400a14
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * 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.codec.impl;
+
+import static java.util.Objects.requireNonNull;
+
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+
+/**
+ * This is not really a codec context, but rather a holder of input and output codec contexts.
+ */
+final class ActionCodecContext {
+    private final DataContainerCodecContext<?, ContainerSchemaNode> input;
+    private final DataContainerCodecContext<?, ContainerSchemaNode> output;
+
+    ActionCodecContext(final DataContainerCodecContext<?, ContainerSchemaNode> input,
+        final DataContainerCodecContext<?, ContainerSchemaNode> output) {
+        this.input = requireNonNull(input);
+        this.output = requireNonNull(output);
+    }
+
+    DataContainerCodecContext<?, ContainerSchemaNode> input() {
+        return input;
+    }
+
+    DataContainerCodecContext<?, ContainerSchemaNode> output() {
+        return output;
+    }
+}
index 5704e006623ddc2873e81db026e6a574368bc124..177a511a2bb565c5d71307971293f02bb2b5f871 100644 (file)
@@ -30,6 +30,7 @@ import org.opendaylight.mdsal.binding.model.api.GeneratedType;
 import org.opendaylight.yangtools.concepts.Codec;
 import org.opendaylight.yangtools.concepts.Immutable;
 import org.opendaylight.yangtools.util.ClassLoaderUtils;
+import org.opendaylight.yangtools.yang.binding.Action;
 import org.opendaylight.yangtools.yang.binding.BindingMapping;
 import org.opendaylight.yangtools.yang.binding.BindingStreamEventWriter;
 import org.opendaylight.yangtools.yang.binding.DataContainer;
@@ -221,6 +222,10 @@ final class BindingCodecContext implements CodecContextFactory, BindingCodecTree
         return root.getRpc(path);
     }
 
+    ActionCodecContext getActionCodec(final Class<? extends Action<?, ?, ?>> action) {
+        return root.getAction(action);
+    }
+
     @Override
     public ImmutableMap<String, LeafNodeCodecContext<?>> getLeafNodes(final Class<?> parentClass,
             final DataNodeContainer childSchema) {
index efc44e253d2b2380965f984879fb0a01b9b448f4..0df1e1ad013f4324a0f7d23af067ec85b9137745 100644 (file)
@@ -7,6 +7,8 @@
  */
 package org.opendaylight.mdsal.binding.dom.codec.impl;
 
+import static java.util.Objects.requireNonNull;
+
 import com.google.common.base.Function;
 import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
@@ -18,14 +20,19 @@ import java.util.AbstractMap.SimpleEntry;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map.Entry;
+import java.util.function.BiFunction;
+import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.opendaylight.mdsal.binding.dom.codec.api.BindingCodecTree;
 import org.opendaylight.mdsal.binding.dom.codec.api.BindingCodecTreeFactory;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingLazyContainerNode;
 import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer;
 import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeWriterFactory;
 import org.opendaylight.mdsal.binding.dom.codec.gen.impl.DataObjectSerializerGenerator;
+import org.opendaylight.mdsal.binding.dom.codec.util.AbstractBindingLazyContainerNode;
 import org.opendaylight.mdsal.binding.generator.impl.ModuleInfoBackedContext;
 import org.opendaylight.mdsal.binding.generator.util.BindingRuntimeContext;
 import org.opendaylight.yangtools.concepts.Delegator;
+import org.opendaylight.yangtools.yang.binding.Action;
 import org.opendaylight.yangtools.yang.binding.BindingStreamEventWriter;
 import org.opendaylight.yangtools.yang.binding.DataContainer;
 import org.opendaylight.yangtools.yang.binding.DataObject;
@@ -35,8 +42,11 @@ import org.opendaylight.yangtools.yang.binding.DataObjectSerializerRegistry;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument;
 import org.opendaylight.yangtools.yang.binding.Notification;
+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.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
@@ -114,35 +124,52 @@ public class BindingNormalizedNodeCodecRegistry implements DataObjectSerializerR
 
     @Override
     public ContainerNode toNormalizedNodeNotification(final Notification data) {
-        final NormalizedNodeResult result = new NormalizedNodeResult();
-        // We create DOM stream writer which produces normalized nodes
-        final NormalizedNodeStreamWriter domWriter = ImmutableNormalizedNodeStreamWriter.from(result);
-        @SuppressWarnings({ "unchecked", "rawtypes" })
-        final Class<? extends DataObject> type = (Class) data.getImplementedInterface();
-        @SuppressWarnings({ "unchecked", "rawtypes" })
-        final BindingStreamEventWriter writer = newNotificationWriter((Class) type, domWriter);
-        try {
-            // FIXME: Should be cast to DataObject necessary?
-            getSerializer(type).serialize((DataObject) data, writer);
-        } catch (final IOException e) {
-            LOG.error("Unexpected failure while serializing data {}", data, e);
-            throw new IllegalStateException("Failed to create normalized node", e);
-        }
-        return (ContainerNode) result.getResult();
-
+        // FIXME: Should the cast to DataObject be necessary?
+        return serializeDataObject((DataObject) data,
+            // javac does not like a methodhandle here
+            (iface, domWriter) -> newNotificationWriter(iface.asSubclass(Notification.class), domWriter));
     }
 
     @Override
     public ContainerNode toNormalizedNodeRpcData(final DataContainer data) {
+        // FIXME: Should the cast to DataObject be necessary?
+        return serializeDataObject((DataObject) data, this::newRpcWriter);
+    }
+
+    @Override
+    public ContainerNode toNormalizedNodeActionInput(final Class<? extends Action<?, ?, ?>> action,
+            final RpcInput input) {
+        return serializeDataObject(input, (iface, domWriter) -> newActionInputWriter(action, domWriter));
+    }
+
+    @Override
+    public ContainerNode toNormalizedNodeActionOutput(final Class<? extends Action<?, ?, ?>> action,
+            final RpcOutput output) {
+        return serializeDataObject(output, (iface, domWriter) -> newActionOutputWriter(action, domWriter));
+    }
+
+    @Override
+    public BindingLazyContainerNode<RpcInput> toLazyNormalizedNodeActionInput(
+            final Class<? extends Action<?, ?, ?>> action, final NodeIdentifier identifier, final RpcInput input) {
+        return new LazyActionInputContainerNode(identifier, input, this, action);
+    }
+
+    @Override
+    public BindingLazyContainerNode<RpcOutput> toLazyNormalizedNodeActionOutput(
+            final Class<? extends Action<?, ?, ?>> action, final NodeIdentifier identifier, final RpcOutput output) {
+        return new LazyActionOutputContainerNode(identifier, output, this, action);
+    }
+
+    private <T extends DataContainer> ContainerNode serializeDataObject(final DataObject data,
+            final BiFunction<Class<? extends T>, NormalizedNodeStreamWriter, BindingStreamEventWriter> newWriter) {
         final NormalizedNodeResult result = new NormalizedNodeResult();
         // We create DOM stream writer which produces normalized nodes
         final NormalizedNodeStreamWriter domWriter = ImmutableNormalizedNodeStreamWriter.from(result);
-        @SuppressWarnings({ "unchecked", "rawtypes" })
-        final Class<? extends DataObject> type = (Class) data.getImplementedInterface();
-        final BindingStreamEventWriter writer = newRpcWriter(type, domWriter);
+        @SuppressWarnings("unchecked")
+        final Class<? extends DataObject> type = (Class<? extends DataObject>) data.getImplementedInterface();
+        final BindingStreamEventWriter writer = newWriter.apply((Class<T>)type, domWriter);
         try {
-            // FIXME: Should be cast to DataObject necessary?
-            getSerializer(type).serialize((DataObject) data, writer);
+            getSerializer(type).serialize(data, writer);
         } catch (final IOException e) {
             LOG.error("Unexpected failure while serializing data {}", data, e);
             throw new IllegalStateException("Failed to create normalized node", e);
@@ -207,6 +234,18 @@ public class BindingNormalizedNodeCodecRegistry implements DataObjectSerializerR
         return codec.deserialize(data);
     }
 
+    @Override
+    public <T extends RpcInput> T fromNormalizedNodeActionInput(final Class<? extends Action<?, ?, ?>> action,
+            final ContainerNode input) {
+        return (T) requireNonNull(codecContext.getActionCodec(action).input().deserialize(requireNonNull(input)));
+    }
+
+    @Override
+    public <T extends RpcOutput> T fromNormalizedNodeActionOutput(final Class<? extends Action<?, ?, ?>> action,
+            final ContainerNode output) {
+        return (T) requireNonNull(codecContext.getActionCodec(action).output().deserialize(requireNonNull(output)));
+    }
+
     @Override
     public Entry<YangInstanceIdentifier, BindingStreamEventWriter> newWriterAndIdentifier(
             final InstanceIdentifier<?> path, final NormalizedNodeStreamWriter domWriter) {
@@ -225,6 +264,18 @@ public class BindingNormalizedNodeCodecRegistry implements DataObjectSerializerR
         return codecContext.newNotificationWriter(notification, streamWriter);
     }
 
+    @Override
+    public BindingStreamEventWriter newActionInputWriter(final Class<? extends Action<?, ?, ?>> action,
+            final NormalizedNodeStreamWriter domWriter) {
+        return codecContext.getActionCodec(action).input().createWriter(domWriter);
+    }
+
+    @Override
+    public BindingStreamEventWriter newActionOutputWriter(final Class<? extends Action<?, ?, ?>> action,
+            final NormalizedNodeStreamWriter domWriter) {
+        return codecContext.getActionCodec(action).output().createWriter(domWriter);
+    }
+
     @Override
     public BindingStreamEventWriter newRpcWriter(final Class<? extends DataContainer> rpcInputOrOutput,
             final NormalizedNodeStreamWriter streamWriter) {
@@ -270,16 +321,13 @@ public class BindingNormalizedNodeCodecRegistry implements DataObjectSerializerR
         @SuppressWarnings("unchecked")
         @Override
         public Optional<T> apply(final Optional<NormalizedNode<?, ?>> input) {
-            if (input.isPresent()) {
-                return Optional.of((T) ctx.deserialize(input.get()));
-            }
-            return Optional.absent();
+            return input.transform(data -> (T) ctx.deserialize(data));
         }
     }
 
     private final class GeneratorLoader extends CacheLoader<Class<? extends DataContainer>, DataObjectSerializer> {
         @Override
-        public DataObjectSerializer load(final Class<? extends DataContainer> key) throws Exception {
+        public DataObjectSerializer load(final Class<? extends DataContainer> key) {
             final DataObjectSerializerImplementation prototype = generator.getSerializer(key);
             return new DataObjectSerializerProxy(prototype);
         }
@@ -303,4 +351,42 @@ public class BindingNormalizedNodeCodecRegistry implements DataObjectSerializerR
             delegate.serialize(BindingNormalizedNodeCodecRegistry.this, obj, stream);
         }
     }
+
+    @NonNullByDefault
+    private abstract static class AbstractLazyActionContainerNode<T extends DataObject>
+            extends AbstractBindingLazyContainerNode<T, BindingNormalizedNodeCodecRegistry> {
+        protected final Class<? extends Action<?, ?, ?>> action;
+
+        AbstractLazyActionContainerNode(final NodeIdentifier identifier, final T bindingData,
+            final BindingNormalizedNodeCodecRegistry context, final Class<? extends Action<?, ?, ?>> action) {
+            super(identifier, bindingData, context);
+            this.action = requireNonNull(action);
+        }
+    }
+
+    @NonNullByDefault
+    private static final class LazyActionInputContainerNode extends AbstractLazyActionContainerNode<RpcInput> {
+        LazyActionInputContainerNode(final NodeIdentifier identifier, final RpcInput bindingData,
+                final BindingNormalizedNodeCodecRegistry context, final Class<? extends Action<?, ?, ?>> action) {
+            super(identifier, bindingData, context, action);
+        }
+
+        @Override
+        protected ContainerNode computeContainerNode(final BindingNormalizedNodeCodecRegistry context) {
+            return context.toNormalizedNodeActionInput(action, getDataObject());
+        }
+    }
+
+    @NonNullByDefault
+    private static final class LazyActionOutputContainerNode extends AbstractLazyActionContainerNode<RpcOutput> {
+        LazyActionOutputContainerNode(final NodeIdentifier identifier, final RpcOutput bindingData,
+                final BindingNormalizedNodeCodecRegistry context, final Class<? extends Action<?, ?, ?>> action) {
+            super(identifier, bindingData, context, action);
+        }
+
+        @Override
+        protected ContainerNode computeContainerNode(final BindingNormalizedNodeCodecRegistry context) {
+            return context.toNormalizedNodeActionOutput(action, getDataObject());
+        }
+    }
 }
index b342d0e214cb4957a194effb0197b9ed353b44af..4eb21e2f667842b3bbbef95b39780c2a3190d075 100644 (file)
@@ -7,8 +7,10 @@
  */
 package org.opendaylight.mdsal.binding.dom.codec.impl;
 
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Verify.verify;
+
 import com.google.common.base.Optional;
-import com.google.common.base.Preconditions;
 import com.google.common.base.Throwables;
 import com.google.common.base.Verify;
 import com.google.common.cache.CacheBuilder;
@@ -17,18 +19,23 @@ import com.google.common.cache.LoadingCache;
 import com.google.common.util.concurrent.UncheckedExecutionException;
 import java.lang.reflect.Type;
 import java.util.List;
+import org.opendaylight.yangtools.util.ClassLoaderUtils;
+import org.opendaylight.yangtools.yang.binding.Action;
 import org.opendaylight.yangtools.yang.binding.BindingMapping;
 import org.opendaylight.yangtools.yang.binding.ChoiceIn;
 import org.opendaylight.yangtools.yang.binding.DataContainer;
 import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.binding.Notification;
+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.QName;
 import org.opendaylight.yangtools.yang.common.QNameModule;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.model.api.ActionDefinition;
 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
@@ -51,6 +58,14 @@ final class SchemaRootCodecContext<D extends DataObject> extends DataContainerCo
                 }
             });
 
+    private final LoadingCache<Class<? extends Action<?, ?, ?>>, ActionCodecContext> actionsByClass = CacheBuilder
+            .newBuilder().build(new CacheLoader<Class<? extends Action<?, ?, ?>>, ActionCodecContext>() {
+                @Override
+                public ActionCodecContext load(final Class<? extends Action<?, ?, ?>> key) {
+                    return createActionContext(key);
+                }
+            });
+
     private final LoadingCache<Class<?>, ContainerNodeCodecContext<?>> rpcDataByClass = CacheBuilder.newBuilder().build(
             new CacheLoader<Class<?>, ContainerNodeCodecContext<?>>() {
                 @Override
@@ -163,6 +178,10 @@ final class SchemaRootCodecContext<D extends DataObject> extends DataContainerCo
         throw new UnsupportedOperationException("Could not create Binding data representation for root");
     }
 
+    ActionCodecContext getAction(final Class<? extends Action<?, ?, ?>> action) {
+        return getOrRethrow(actionsByClass, action);
+    }
+
     NotificationCodecContext<?> getNotification(final Class<? extends Notification> notification) {
         return getOrRethrow(notificationsByClass, notification);
     }
@@ -186,8 +205,23 @@ final class SchemaRootCodecContext<D extends DataObject> extends DataContainerCo
         return DataContainerCodecPrototype.from(key, childSchema, factory()).get();
     }
 
+    ActionCodecContext createActionContext(final Class<? extends Action<?, ?, ?>> action) {
+        final Type[] args = ClassLoaderUtils.findParameterizedType(action, Action.class).getActualTypeArguments();
+        checkArgument(args.length == 3, "Unexpected (%s) Action generatic arguments", args.length);
+
+        final ActionDefinition schema = factory().getRuntimeContext().getActionDefinition(action);
+        return new ActionCodecContext(
+            DataContainerCodecPrototype.from(asClass(args[1], RpcInput.class), schema.getInput(), factory()).get(),
+            DataContainerCodecPrototype.from(asClass(args[2], RpcOutput.class), schema.getOutput(), factory()).get());
+    }
+
+    private static <T extends DataObject> Class<? extends T> asClass(final Type type, final Class<T> target) {
+        verify(type instanceof Class, "Type %s is not a class", type);
+        return ((Class<?>) type).asSubclass(target);
+    }
+
     ContainerNodeCodecContext<?> createRpcDataContext(final Class<?> key) {
-        Preconditions.checkArgument(DataContainer.class.isAssignableFrom(key));
+        checkArgument(DataContainer.class.isAssignableFrom(key));
         final QName qname = BindingReflections.findQName(key);
         final QNameModule qnameModule = qname.getModule();
         final Module module = getSchema().findModule(qnameModule)
@@ -211,15 +245,15 @@ final class SchemaRootCodecContext<D extends DataObject> extends DataContainerCo
                 break;
             }
         }
-        Preconditions.checkArgument(rpc != null, "Supplied class %s is not valid RPC class.", key);
+        checkArgument(rpc != null, "Supplied class %s is not valid RPC class.", key);
         final ContainerSchemaNode schema = SchemaNodeUtils.getRpcDataSchema(rpc, qname);
-        Preconditions.checkArgument(schema != null, "Schema for %s does not define input / output.", rpc.getQName());
+        checkArgument(schema != null, "Schema for %s does not define input / output.", rpc.getQName());
         return (ContainerNodeCodecContext<?>) DataContainerCodecPrototype.from(key, schema, factory()).get();
     }
 
     NotificationCodecContext<?> createNotificationDataContext(final Class<?> notificationType) {
-        Preconditions.checkArgument(Notification.class.isAssignableFrom(notificationType));
-        Preconditions.checkArgument(notificationType.isInterface(), "Supplied class must be interface.");
+        checkArgument(Notification.class.isAssignableFrom(notificationType));
+        checkArgument(notificationType.isInterface(), "Supplied class must be interface.");
         final QName qname = BindingReflections.findQName(notificationType);
         /**
          *  FIXME: After Lithium cleanup of yang-model-api, use direct call on schema context
@@ -227,17 +261,16 @@ final class SchemaRootCodecContext<D extends DataObject> extends DataContainerCo
          */
         final NotificationDefinition schema = SchemaContextUtil.getNotificationSchema(getSchema(),
                 SchemaPath.create(true, qname));
-        Preconditions.checkArgument(schema != null, "Supplied %s is not valid notification", notificationType);
+        checkArgument(schema != null, "Supplied %s is not valid notification", notificationType);
 
         return new NotificationCodecContext<>(notificationType, schema, factory());
     }
 
     ChoiceNodeCodecContext<?> createChoiceDataContext(final Class<? extends DataObject> caseType) {
         final Class<?> choiceClass = findCaseChoice(caseType);
-        Preconditions.checkArgument(choiceClass != null, "Class %s is not a valid case representation", caseType);
+        checkArgument(choiceClass != null, "Class %s is not a valid case representation", caseType);
         final DataSchemaNode schema = factory().getRuntimeContext().getSchemaDefinition(choiceClass);
-        Preconditions.checkArgument(schema instanceof ChoiceSchemaNode, "Class %s does not refer to a choice",
-            caseType);
+        checkArgument(schema instanceof ChoiceSchemaNode, "Class %s does not refer to a choice", caseType);
 
         final DataContainerCodecContext<?, ChoiceSchemaNode> choice = DataContainerCodecPrototype.from(choiceClass,
             (ChoiceSchemaNode)schema, factory()).get();
@@ -252,13 +285,13 @@ final class SchemaRootCodecContext<D extends DataObject> extends DataContainerCo
 
     @Override
     public InstanceIdentifier.PathArgument deserializePathArgument(final YangInstanceIdentifier.PathArgument arg) {
-        Preconditions.checkArgument(arg == null);
+        checkArgument(arg == null);
         return null;
     }
 
     @Override
     public YangInstanceIdentifier.PathArgument serializePathArgument(final InstanceIdentifier.PathArgument arg) {
-        Preconditions.checkArgument(arg == null);
+        checkArgument(arg == null);
         return null;
     }
 
index 7864267d725c44df080bf5f7137a98503196bd05..0527de2b0e94a4115ff4207fd4f83f674cf4c846 100644 (file)
@@ -50,6 +50,7 @@ public abstract class AbstractBindingLazyContainerNode<T extends DataObject, C>
         this.context = context;
     }
 
+    @Override
     public final @NonNull T getDataObject() {
         return bindingData;
     }
@@ -89,6 +90,23 @@ public abstract class AbstractBindingLazyContainerNode<T extends DataObject, C>
         return delegate().getChild(child);
     }
 
+    @Override
+    public int hashCode() {
+        return delegate().hashCode();
+    }
+
+    @Override
+    public boolean equals(final @Nullable Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof ContainerNode)) {
+            return false;
+        }
+        final ContainerNode other = (ContainerNode) obj;
+        return delegate().equals(obj);
+    }
+
     @Override
     protected final @NonNull ContainerNode delegate() {
         ContainerNode local = delegate;
diff --git a/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/test/ActionSerializeDeserializeTest.java b/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/test/ActionSerializeDeserializeTest.java
new file mode 100644 (file)
index 0000000..fb3783d
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * 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.codec.test;
+
+import static org.junit.Assert.assertEquals;
+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 org.junit.Test;
+import org.opendaylight.yang.gen.v1.urn.odl.actions.norev.cont.Foo;
+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.OutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.odl.actions.norev.grpcont.Bar;
+import org.opendaylight.yangtools.yang.binding.RpcInput;
+import org.opendaylight.yangtools.yang.binding.RpcOutput;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+
+public class ActionSerializeDeserializeTest extends AbstractBindingCodecTest {
+    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 RpcInput BINDING_FOO_INPUT = new InputBuilder().setXyzzy("xyzzy").build();
+    private static final RpcOutput BINDING_FOO_OUTPUT = new OutputBuilder().build();
+
+    private static final NodeIdentifier BAR_INPUT = NodeIdentifier.create(operationInputQName(Foo.QNAME.getModule()));
+    private static final NodeIdentifier BAR_OUTPUT = NodeIdentifier.create(operationOutputQName(Foo.QNAME.getModule()));
+    private static final NodeIdentifier BAR_XYZZY = NodeIdentifier.create(QName.create(Bar.QNAME, "xyzzy"));
+    private static final ContainerNode DOM_BAR_INPUT = containerBuilder().withNodeIdentifier(BAR_INPUT).build();
+    private static final ContainerNode DOM_BAR_OUTPUT = containerBuilder().withNodeIdentifier(BAR_OUTPUT)
+            .withChild(leafBuilder().withNodeIdentifier(BAR_XYZZY).withValue("xyzzy").build())
+            .build();
+    private static final RpcInput BINDING_BAR_INPUT =
+            new org.opendaylight.yang.gen.v1.urn.odl.actions.norev.grp.bar.InputBuilder().build();
+    private static final RpcOutput BINDING_BAR_OUTPUT =
+            new org.opendaylight.yang.gen.v1.urn.odl.actions.norev.grp.bar.OutputBuilder().setXyzzy("xyzzy").build();
+
+    @Test
+    public void testSerialization() {
+        assertEquals(DOM_FOO_INPUT, registry.toLazyNormalizedNodeActionInput(Foo.class, BINDING_FOO_INPUT)
+            .getDelegate());
+        assertEquals(DOM_BAR_INPUT, registry.toLazyNormalizedNodeActionInput(Bar.class, BINDING_BAR_INPUT)
+                .getDelegate());
+        assertEquals(DOM_FOO_OUTPUT, registry.toLazyNormalizedNodeActionOutput(Foo.class, BINDING_FOO_OUTPUT)
+                .getDelegate());
+        assertEquals(DOM_BAR_OUTPUT, registry.toLazyNormalizedNodeActionOutput(Bar.class, BINDING_BAR_OUTPUT)
+                .getDelegate());
+    }
+
+    @Test
+    public void testDeserialization() {
+        assertEquals(BINDING_FOO_INPUT, registry.fromNormalizedNodeActionInput(Foo.class, DOM_FOO_INPUT));
+        assertEquals(BINDING_BAR_INPUT, registry.fromNormalizedNodeActionInput(Bar.class, DOM_FOO_INPUT));
+        assertEquals(BINDING_FOO_OUTPUT, registry.fromNormalizedNodeActionOutput(Foo.class, DOM_FOO_OUTPUT));
+        assertEquals(BINDING_BAR_OUTPUT, registry.fromNormalizedNodeActionOutput(Bar.class, DOM_FOO_INPUT));
+    }
+}
index 4ea4a546dc7f0c6396350bba9cc4821819b1cece..32210d9fb2b36e9c74c41321ba1422d1e8bc36ba 100644 (file)
@@ -5,13 +5,21 @@ module actions {
 
     container cont {
         action foo {
-        
+            input {
+                leaf xyzzy {
+                    type string;
+                }
+            }
         }
     }
 
     grouping grp {
         action bar {
-
+            output {
+                leaf xyzzy {
+                    type string;
+                }
+            }
         }
     }
 
index 18a5f83caa19a3a375147e4116fae7fcdb3ce62a..39c75d72d346c7c128add7d6b9e18603f1b3e857 100644 (file)
@@ -39,7 +39,8 @@ public interface DOMOperationImplementation {
          * @return A FluentFuture which completes with the result of invocation
          * @throws NullPointerException if any of the arguments is null
          */
-        FluentFuture<DOMOperationResult> invokeAction(SchemaPath type, DOMDataTreeIdentifier path, ContainerNode input);
+        FluentFuture<? extends DOMOperationResult> invokeAction(SchemaPath type, DOMDataTreeIdentifier path,
+                ContainerNode input);
     }
 
     /**
@@ -55,7 +56,7 @@ public interface DOMOperationImplementation {
          * @return A FluentFuture which completes with the result of invocation
          * @throws NullPointerException if any of the arguments is null
          */
-        FluentFuture<DOMOperationResult> invokeRpc(QName type, ContainerNode input);
+        FluentFuture<? extends DOMOperationResult> invokeRpc(QName type, ContainerNode input);
     }
 
     /**