BUG-8004: handle implicit RPC input 36/54136/2
authorRobert Varga <robert.varga@pantheon.tech>
Mon, 27 Mar 2017 17:42:17 +0000 (19:42 +0200)
committerMartin Ciglan <mciglan@cisco.com>
Mon, 10 Apr 2017 06:09:27 +0000 (06:09 +0000)
RPC input is always defined implicitly, which is something we cannot
deal with in Binding Specification compatibly, as original generators
did not emit classes for such methods and implicitly map them to Void.

In order to deal with these, we need to recognize when an RPC input
is not declared and re-wire it to a null codec. This loses information,
but it is the best we can do (simply because there is no binding
representation).

Change-Id: I4edbcd0cc886396bdba79f4c0ccfd91ba3d6b2c7
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
(cherry picked from commit a69ca3a80b53cf7697b8a29029260209dea842ac)

binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/BindingCodecContext.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/BindingNormalizedNodeCodecRegistry.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/ContainerNodeCodecContext.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/RpcInputDataObjectCodec.java [new file with mode: 0644]
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/SchemaRootCodecContext.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/UnmappedRpcInputCodec.java [new file with mode: 0644]

index 31d3375336aaa3b2080ed959d121c1310c29589e..1f8f9eb79d542b0d12d75aab3095ac4b071e0f8f 100644 (file)
@@ -213,7 +213,7 @@ final class BindingCodecContext implements CodecContextFactory, BindingCodecTree
         return root.getNotification(notification);
     }
 
-    ContainerNodeCodecContext<?> getRpcDataContext(final SchemaPath path) {
+    RpcInputCodec<?> getRpcInputCodec(final SchemaPath path) {
         return root.getRpc(path);
     }
 
index 334a20415b5bc4c4c53b5b0011fc613a3596d140..3cd656398e7fa6ee0ea6602e9a20e8415abeecb8 100644 (file)
@@ -108,7 +108,7 @@ public class BindingNormalizedNodeCodecRegistry implements DataObjectSerializerR
             LOG.error("Unexpected failure while serializing path {} data {}", path, data, e);
             throw new IllegalStateException("Failed to create normalized node", e);
         }
-        return new SimpleEntry<YangInstanceIdentifier,NormalizedNode<?,?>>(writeCtx.getKey(),result.getResult());
+        return new SimpleEntry<>(writeCtx.getKey(),result.getResult());
     }
 
     @Override
@@ -189,7 +189,7 @@ public class BindingNormalizedNodeCodecRegistry implements DataObjectSerializerR
 
         final DataObject lazyObj = codec.deserialize(data);
         final InstanceIdentifier<?> bindingPath = InstanceIdentifier.create(builder);
-        return new SimpleEntry<InstanceIdentifier<?>, DataObject>(bindingPath, lazyObj);
+        return new SimpleEntry<>(bindingPath, lazyObj);
     }
 
     @Override
@@ -200,7 +200,7 @@ public class BindingNormalizedNodeCodecRegistry implements DataObjectSerializerR
 
     @Override
     public DataObject fromNormalizedNodeRpcData(final SchemaPath path, final ContainerNode data) {
-        final ContainerNodeCodecContext<?> codec = codecContext.getRpcDataContext(path);
+        final RpcInputCodec<?> codec = codecContext.getRpcInputCodec(path);
         return codec.deserialize(data);
     }
 
@@ -228,7 +228,7 @@ public class BindingNormalizedNodeCodecRegistry implements DataObjectSerializerR
 
     public <T extends DataObject> Function<Optional<NormalizedNode<?, ?>>, Optional<T>>  deserializeFunction(final InstanceIdentifier<T> path) {
         final DataObjectCodecContext<?,?> ctx = (DataObjectCodecContext<?,?>) codecContext.getCodecContextNode(path, null);
-        return new DeserializeFunction<T>(ctx);
+        return new DeserializeFunction<>(ctx);
     }
 
     @Override
index 33a65fd9fe3cdbd27a84cc5ade76a1bfd3b952a1..1c7b0e47a48bc12abc30c3c65e42e4f7637b5558 100644 (file)
@@ -13,7 +13,8 @@ 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.ContainerSchemaNode;
 
-final class ContainerNodeCodecContext<D extends DataObject> extends DataObjectCodecContext<D, ContainerSchemaNode> {
+final class ContainerNodeCodecContext<D extends DataObject> extends DataObjectCodecContext<D, ContainerSchemaNode>
+        implements RpcInputCodec<D> {
 
     ContainerNodeCodecContext(final DataContainerCodecPrototype<ContainerSchemaNode> prototype) {
         super(prototype);
diff --git a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/RpcInputDataObjectCodec.java b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/RpcInputDataObjectCodec.java
new file mode 100644 (file)
index 0000000..c2ed1ae
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2017 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.yangtools.binding.data.codec.impl;
+
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeCodec;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+
+/**
+ * Marker interface for codecs dealing with RPC input being potentially unmapped. We use this interface to mark both
+ * {@link UnmappedRpcInputCodec} and {@link ContainerNodeCodecContext}, which results in bimorphic invocation in
+ * {@link BindingNormalizedNodeCodecRegistry#fromNormalizedNodeRpcData()}.
+ *
+ * Without this interface we could end up with megamorphic invocation, as the two implementations cannot share class
+ * hierarchy.
+ *
+ * @author Robert Varga
+ *
+ * @param <T> Binding representation of data
+ */
+interface RpcInputCodec<D extends DataObject> extends BindingNormalizedNodeCodec<D> {
+
+}
index 512c10e5da3e916d0c82caff48637e0374aeaef2..ba84c18e066a84776759901c05035fa3973bbd86 100644 (file)
@@ -33,6 +33,8 @@ import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementSource;
 import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
 import org.opendaylight.yangtools.yang.model.util.SchemaNodeUtils;
 
@@ -44,7 +46,6 @@ final class SchemaRootCodecContext<D extends DataObject> extends DataContainerCo
                 public DataContainerCodecContext<?,?> load(final Class<?> key) {
                     return createDataTreeChildContext(key);
                 }
-
             });
 
     private final LoadingCache<Class<?>, ContainerNodeCodecContext<?>> rpcDataByClass = CacheBuilder.newBuilder().build(
@@ -74,23 +75,29 @@ final class SchemaRootCodecContext<D extends DataObject> extends DataContainerCo
                         @SuppressWarnings("rawtypes")
                         final Class childCls = factory().getRuntimeContext().getClassForSchema(childSchema);
                         return streamChild(childCls);
-                    } else {
-                        throw new UnsupportedOperationException("Unsupported child type " + childSchema.getClass());
                     }
+
+                    throw new UnsupportedOperationException("Unsupported child type " + childSchema.getClass());
                 }
             });
 
-    private final LoadingCache<SchemaPath, ContainerNodeCodecContext<?>> rpcDataByPath = CacheBuilder.newBuilder().build(
-            new CacheLoader<SchemaPath, ContainerNodeCodecContext<?>>() {
-
-                @SuppressWarnings({ "rawtypes", "unchecked" })
-                @Override
-                public ContainerNodeCodecContext load(final SchemaPath key) {
-                    final ContainerSchemaNode schema = SchemaContextUtil.getRpcDataSchema(getSchema(), key);
-                    final Class cls = factory().getRuntimeContext().getClassForSchema(schema);
-                    return getRpc(cls);
+    private final LoadingCache<SchemaPath, RpcInputCodec<?>> rpcDataByPath = CacheBuilder.newBuilder().build(
+        new CacheLoader<SchemaPath, RpcInputCodec<?>>() {
+            @SuppressWarnings({ "rawtypes", "unchecked" })
+            @Override
+            public RpcInputCodec load(final SchemaPath key) {
+                final ContainerSchemaNode schema = SchemaContextUtil.getRpcDataSchema(getSchema(), key);
+                if (schema instanceof EffectiveStatement &&
+                        ((EffectiveStatement) schema).getDeclared().getStatementSource() != StatementSource.DECLARATION) {
+                    // This is an implicitly-defined input or output statement. We do not have a corresponding
+                    // data representation, so we hard-wire it to null.
+                    return UnmappedRpcInputCodec.getInstance();
                 }
-            });
+
+                final Class cls = factory().getRuntimeContext().getClassForSchema(schema);
+                return getRpc(cls);
+            }
+        });
 
     private final LoadingCache<SchemaPath, NotificationCodecContext<?>> notificationsByPath = CacheBuilder.newBuilder()
             .build(new CacheLoader<SchemaPath, NotificationCodecContext<?>>() {
@@ -165,7 +172,7 @@ final class SchemaRootCodecContext<D extends DataObject> extends DataContainerCo
         return getOrRethrow(notificationsByPath, notification);
     }
 
-    ContainerNodeCodecContext<?> getRpc(final SchemaPath notification) {
+    RpcInputCodec<?> getRpc(final SchemaPath notification) {
         return getOrRethrow(rpcDataByPath, notification);
     }
 
@@ -183,7 +190,6 @@ final class SchemaRootCodecContext<D extends DataObject> extends DataContainerCo
         for (final RpcDefinition potential : getSchema().getOperations()) {
             final QName potentialQName = potential.getQName();
             /*
-             *
              * Check if rpc and class represents data from same module and then
              * checks if rpc local name produces same class name as class name
              * appended with Input/Output based on QName associated with bidning
@@ -242,7 +248,7 @@ final class SchemaRootCodecContext<D extends DataObject> extends DataContainerCo
             return cache.getUnchecked(key);
         } catch (final UncheckedExecutionException e) {
             final Throwable cause = e.getCause();
-            if(cause != null) {
+            if (cause != null) {
                 Throwables.propagateIfPossible(cause);
             }
             throw e;
diff --git a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/UnmappedRpcInputCodec.java b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/UnmappedRpcInputCodec.java
new file mode 100644 (file)
index 0000000..5825382
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2017 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.yangtools.binding.data.codec.impl;
+
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+/**
+ * Singleton codec for translating RPCs with implicit input statements, which are not mapped by binding spec v1. Since
+ * there is no equivalent, we always return null.
+ *
+ * @author Robert Varga
+ *
+ * @param <D> Data object type
+ */
+final class UnmappedRpcInputCodec<D extends DataObject> implements RpcInputCodec<D> {
+    private static final UnmappedRpcInputCodec<?> INSTANCE = new UnmappedRpcInputCodec<>();
+
+    private UnmappedRpcInputCodec() {
+
+    }
+
+    @SuppressWarnings("unchecked")
+    static <D extends DataObject> UnmappedRpcInputCodec<D> getInstance() {
+        return (UnmappedRpcInputCodec<D>) INSTANCE;
+    }
+
+    @Override
+    public D deserialize(final NormalizedNode<?, ?> data) {
+        return null;
+    }
+
+    @Override
+    public NormalizedNode<?, ?> serialize(final D data) {
+        throw new UnsupportedOperationException("Serialization of " + data + " not supported");
+    }
+}