Added support for list, leaf-list and container in output under rpc node in yang... 14/2214/4
authorMaros Marsalek <mmarsale@cisco.com>
Mon, 28 Oct 2013 10:37:25 +0000 (11:37 +0100)
committerGerrit Code Review <gerrit@opendaylight.org>
Wed, 30 Oct 2013 03:29:59 +0000 (03:29 +0000)
Added support for leaf-list in state data definition.

Updated config subsystem as well as netconf mapping.

Change-Id: I4bdfbed352e29d6d3dc569f38c07022383d565c8
Signed-off-by: Maros Marsalek <mmarsale@cisco.com>
12 files changed:
opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/TemplateFactory.java
opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/RuntimeBeanEntry.java
opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/JavaAttribute.java
opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/VoidAttribute.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/RuntimeBeanEntryTest.java
opendaylight/config/yang-test/src/main/yang/config-test-impl.yang
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/mapping/ObjectMapper.java
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/toxml/ObjectXmlWriter.java
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/rpc/InstanceRuntimeRpc.java
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/runtimerpc/RuntimeRpc.java
opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/NetconfMappingTest.java
opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/rpcInnerInner_complex_output.xml [new file with mode: 0644]

index 641fb25c1a350e74cbe9135d93f1a61dc24a3e1a..a10f62be79027dbe01e9a6e4992eb76309981939 100644 (file)
@@ -7,15 +7,11 @@
  */
 package org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl;
 
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-
+import com.google.common.base.Function;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
 import org.opendaylight.controller.config.api.RuntimeBeanRegistratorAwareModule;
 import org.opendaylight.controller.config.api.annotations.AbstractServiceInterface;
 import org.opendaylight.controller.config.api.runtime.RuntimeBean;
@@ -25,29 +21,17 @@ import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry;
 import org.opendaylight.controller.config.yangjmxgenerator.RuntimeBeanEntry;
 import org.opendaylight.controller.config.yangjmxgenerator.RuntimeBeanEntry.Rpc;
 import org.opendaylight.controller.config.yangjmxgenerator.ServiceInterfaceEntry;
-import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc;
-import org.opendaylight.controller.config.yangjmxgenerator.attribute.DependencyAttribute;
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.*;
 import org.opendaylight.controller.config.yangjmxgenerator.attribute.DependencyAttribute.Dependency;
-import org.opendaylight.controller.config.yangjmxgenerator.attribute.JavaAttribute;
-import org.opendaylight.controller.config.yangjmxgenerator.attribute.ListAttribute;
-import org.opendaylight.controller.config.yangjmxgenerator.attribute.TOAttribute;
-import org.opendaylight.controller.config.yangjmxgenerator.attribute.TypedAttribute;
-import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Annotation;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.*;
 import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Annotation.Parameter;
-import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Constructor;
-import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Field;
-import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Header;
-import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.MethodDeclaration;
-import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.MethodDefinition;
-import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.ModuleField;
 import org.opendaylight.controller.config.yangjmxgenerator.plugin.util.FullyQualifiedNameHelper;
 import org.opendaylight.yangtools.binding.generator.util.BindingGeneratorUtil;
 import org.opendaylight.yangtools.sal.binding.model.api.Type;
 
-import com.google.common.base.Function;
-import com.google.common.collect.Collections2;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
+import javax.management.openmbean.SimpleType;
+import java.util.*;
+import java.util.Map.Entry;
 
 public class TemplateFactory {
 
@@ -107,40 +91,18 @@ public class TemplateFactory {
             List<String> extendedInterfaces = Arrays.asList(RuntimeBean.class
                     .getCanonicalName());
             List<MethodDeclaration> methods = new ArrayList<>();
+
             // convert attributes to getters
             for (AttributeIfc attributeIfc : entry.getAttributes()) {
                 String returnType = null;
-                if (attributeIfc instanceof TypedAttribute) {
-                    returnType = ((TypedAttribute) attributeIfc).getType()
-                            .getFullyQualifiedName();
-                } else if (attributeIfc instanceof TOAttribute) {
-                    String fullyQualifiedName = FullyQualifiedNameHelper
-                            .getFullyQualifiedName(entry.getPackageName(),
-                                    attributeIfc.getUpperCaseCammelCase());
-
-                    returnType = fullyQualifiedName;
-                } else if (attributeIfc instanceof ListAttribute) {
-                    AttributeIfc innerAttr = ((ListAttribute) attributeIfc)
-                            .getInnerAttribute();
-
-                    String innerTpe = innerAttr instanceof TypedAttribute ? ((TypedAttribute) innerAttr)
-                            .getType().getFullyQualifiedName()
-                            : FullyQualifiedNameHelper.getFullyQualifiedName(
-                                    entry.getPackageName(),
-                                    attributeIfc.getUpperCaseCammelCase());
-
-                    returnType = "java.util.List<" + innerTpe + ">";
-                } else {
-                    throw new UnsupportedOperationException(
-                            "Attribute not supported: "
-                                    + attributeIfc.getClass());
-                }
+                returnType = getReturnType(entry, attributeIfc);
                 String getterName = "get"
                         + attributeIfc.getUpperCaseCammelCase();
                 MethodDeclaration getter = new MethodDeclaration(returnType,
                         getterName, Collections.<Field> emptyList());
                 methods.add(getter);
             }
+
             // add rpc methods
             for (Rpc rpc : entry.getRpcs()) {
                 // convert JavaAttribute parameters into fields
@@ -152,7 +114,7 @@ public class TemplateFactory {
                     fields.add(field);
                 }
                 MethodDeclaration operation = new MethodDeclaration(
-                        rpc.getReturnType(), rpc.getName(), fields);
+                        getReturnType(entry, rpc.getReturnType()), rpc.getName(), fields);
                 methods.add(operation);
             }
 
@@ -170,6 +132,38 @@ public class TemplateFactory {
         return result;
     }
 
+    private static String getReturnType(RuntimeBeanEntry entry, AttributeIfc attributeIfc) {
+        String returnType;
+        if (attributeIfc instanceof TypedAttribute) {
+            returnType = ((TypedAttribute) attributeIfc).getType()
+                    .getFullyQualifiedName();
+        } else if (attributeIfc instanceof TOAttribute) {
+            String fullyQualifiedName = FullyQualifiedNameHelper
+                    .getFullyQualifiedName(entry.getPackageName(),
+                            attributeIfc.getUpperCaseCammelCase());
+
+            returnType = fullyQualifiedName;
+        } else if (attributeIfc instanceof ListAttribute) {
+            AttributeIfc innerAttr = ((ListAttribute) attributeIfc)
+                    .getInnerAttribute();
+
+            String innerTpe = innerAttr instanceof TypedAttribute ? ((TypedAttribute) innerAttr)
+                    .getType().getFullyQualifiedName()
+                    : FullyQualifiedNameHelper.getFullyQualifiedName(
+                            entry.getPackageName(),
+                            attributeIfc.getUpperCaseCammelCase());
+
+            returnType = "java.util.List<" + innerTpe + ">";
+        } else if (attributeIfc == VoidAttribute.getInstance()) {
+            return "void";
+        } else {
+            throw new UnsupportedOperationException(
+                    "Attribute not supported: "
+                            + attributeIfc.getClass());
+        }
+        return returnType;
+    }
+
     public static GeneralInterfaceTemplate serviceInterfaceFromSie(
             ServiceInterfaceEntry sie) {
 
@@ -315,8 +309,25 @@ public class TemplateFactory {
             RuntimeBeanEntry rbe) {
         Map<String, GeneralClassTemplate> retVal = Maps.newHashMap();
         TOAttributesProcessor processor = new TOAttributesProcessor();
-        processor.processAttributes(rbe.getYangPropertiesToTypesMap(),
-                rbe.getPackageName());
+        Map<String, AttributeIfc> yangPropertiesToTypesMap = Maps.newHashMap(rbe.getYangPropertiesToTypesMap());
+
+        // Add TOs from output parameters
+        for (Rpc rpc : rbe.getRpcs()) {
+            AttributeIfc returnType = rpc.getReturnType();
+
+            if (returnType == VoidAttribute.getInstance())
+                continue;
+            if (returnType instanceof JavaAttribute)
+                continue;
+            if (returnType instanceof ListAttribute && returnType.getOpenType() instanceof SimpleType)
+                continue;
+
+            Preconditions.checkState(yangPropertiesToTypesMap.containsKey(returnType.getAttributeYangName()) == false,
+                    "Duplicate TO %s for %s", returnType.getAttributeYangName(), rbe);
+            yangPropertiesToTypesMap.put(returnType.getAttributeYangName(), returnType);
+        }
+
+        processor.processAttributes(yangPropertiesToTypesMap, rbe.getPackageName());
         for (org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.TemplateFactory.TOAttributesProcessor.TOInternal to : processor
                 .getTOs()) {
             List<Constructor> constructors = Lists.newArrayList();
index d07e20bebba013c82e32ab0ba1b3c988550f0ecb..22a2b13b4a3c667682cbf5bc2049b225fd5ca480 100644 (file)
@@ -10,10 +10,7 @@ package org.opendaylight.controller.config.yangjmxgenerator;
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Optional;
 import com.google.common.collect.Lists;
-import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc;
-import org.opendaylight.controller.config.yangjmxgenerator.attribute.JavaAttribute;
-import org.opendaylight.controller.config.yangjmxgenerator.attribute.ListAttribute;
-import org.opendaylight.controller.config.yangjmxgenerator.attribute.TOAttribute;
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.*;
 import org.opendaylight.controller.config.yangjmxgenerator.plugin.util.FullyQualifiedNameHelper;
 import org.opendaylight.controller.config.yangjmxgenerator.plugin.util.NameConflictException;
 import org.opendaylight.yangtools.yang.common.QName;
@@ -219,9 +216,13 @@ public class RuntimeBeanEntry {
                     attributes.add(listAttribute);
                 }
 
+            } else if (child instanceof LeafListSchemaNode) {
+                ListAttribute listAttribute = ListAttribute.create(
+                        (LeafListSchemaNode) child, typeProviderWrapper);
+                attributes.add(listAttribute);
             } else {
-                throw new IllegalStateException("Unknown running-data node "
-                        + child + " , " + "" + "expected leaf or container");
+                throw new IllegalStateException("Unexpected running-data node "
+                        + child);
             }
         }
         Set<Rpc> rpcs = new HashSet<>();
@@ -245,29 +246,15 @@ public class RuntimeBeanEntry {
                 for (RpcDefinition rpcDefinition : rpcDefinitions) {
                     String name = ModuleMXBeanEntry
                             .findJavaParameter(rpcDefinition);
-                    String returnType;
+                    AttributeIfc returnType;
                     if (rpcDefinition.getOutput() == null
                             || rpcDefinition.getOutput().getChildNodes().size() == 0) {
-                        returnType = "void";
+                        returnType = VoidAttribute.getInstance();
                     } else if (rpcDefinition.getOutput().getChildNodes().size() == 1) {
                         DataSchemaNode returnDSN = rpcDefinition.getOutput()
                                 .getChildNodes().iterator().next();
-                        checkArgument(
-                                returnDSN instanceof LeafSchemaNode,
-                                "Unexpected type of rpc return type. "
-                                        + "Currently only leafs and empty output nodes are supported, got "
-                                        + returnDSN);
-                        LeafSchemaNode returnLeaf = (LeafSchemaNode) returnDSN;
-                        // We currently expect leaf defined in output element in yang to be named result
-                        // FIXME: value of result is fully qualified name - should be extended to accept TOs
-                        String localName = returnLeaf.getQName().getLocalName();
-                        checkArgument(
-                                localName.equals("result"),
-                                "Unexpected name of leaf in output element, expected leaf named result, was %s at %s",
-                                localName, currentModule.getName());
-
-                        returnType = typeProviderWrapper.getType(returnLeaf)
-                                .getFullyQualifiedName();
+                        returnType = getReturnTypeAttribute(returnDSN, typeProviderWrapper);
+
                     } else {
                         throw new IllegalArgumentException(
                                 "More than one child node in rpc output is not supported. "
@@ -297,6 +284,23 @@ public class RuntimeBeanEntry {
                 attributes, rpcs);
     }
 
+    private static AttributeIfc getReturnTypeAttribute(DataSchemaNode child, TypeProviderWrapper typeProviderWrapper) {
+        if (child instanceof LeafSchemaNode) {
+            LeafSchemaNode leaf = (LeafSchemaNode) child;
+            return new JavaAttribute(leaf, typeProviderWrapper);
+        } else if (child instanceof ContainerSchemaNode) {
+            ContainerSchemaNode container = (ContainerSchemaNode) child;
+            TOAttribute toAttribute = TOAttribute.create(container, typeProviderWrapper);
+            return toAttribute;
+        } else if (child instanceof ListSchemaNode) {
+            return ListAttribute.create((ListSchemaNode) child, typeProviderWrapper);
+        } else if (child instanceof LeafListSchemaNode) {
+            return ListAttribute.create((LeafListSchemaNode) child, typeProviderWrapper);
+        } else {
+            throw new IllegalStateException("Unknown output data node " + child + " for rpc");
+        }
+    }
+
     private static Collection<DataSchemaNode> sortAttributes(Set<DataSchemaNode> childNodes) {
         final TreeSet<DataSchemaNode> dataSchemaNodes = new TreeSet<>(new Comparator<DataSchemaNode>() {
             @Override
@@ -438,10 +442,10 @@ public class RuntimeBeanEntry {
     public static class Rpc {
         private final String name;
         private final List<JavaAttribute> parameters;
-        private final String returnType;
+        private final AttributeIfc returnType;
         private final String yangName;
 
-        Rpc(String returnType, String name, String yangName,
+        Rpc(AttributeIfc returnType, String name, String yangName,
                 List<JavaAttribute> parameters) {
             this.returnType = returnType;
             this.name = name;
@@ -461,7 +465,7 @@ public class RuntimeBeanEntry {
             return parameters;
         }
 
-        public String getReturnType() {
+        public AttributeIfc getReturnType() {
             return returnType;
         }
     }
index 8f516ef1812f0a929e73dc42708850fd026fe9e9..59ab0ff68ea6efa133c2cee357724ed21ac40db8 100644 (file)
@@ -7,17 +7,18 @@
  */
 package org.opendaylight.controller.config.yangjmxgenerator.attribute;
 
-import javax.management.openmbean.ArrayType;
-import javax.management.openmbean.OpenDataException;
-import javax.management.openmbean.OpenType;
-import javax.management.openmbean.SimpleType;
-
 import org.opendaylight.controller.config.yangjmxgenerator.TypeProviderWrapper;
 import org.opendaylight.yangtools.sal.binding.model.api.Type;
 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
 
+import javax.management.openmbean.ArrayType;
+import javax.management.openmbean.OpenDataException;
+import javax.management.openmbean.OpenType;
+import javax.management.openmbean.SimpleType;
+
 public class JavaAttribute extends AbstractAttribute implements TypedAttribute {
+
     private final Type type;
     private final String nullableDescription, nullableDefault;
 
diff --git a/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/VoidAttribute.java b/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/VoidAttribute.java
new file mode 100644 (file)
index 0000000..1e590a5
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. 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.controller.config.yangjmxgenerator.attribute;
+
+import javax.management.openmbean.OpenType;
+
+public class VoidAttribute implements AttributeIfc {
+
+    private static VoidAttribute instance = new VoidAttribute();
+
+    public static VoidAttribute getInstance() {
+        return instance;
+    }
+
+    private VoidAttribute() {
+    }
+
+    @Override
+    public String getAttributeYangName() {
+        return null;
+    }
+
+    @Override
+    public String getNullableDescription() {
+        return null;
+    }
+
+    @Override
+    public String getNullableDefault() {
+        return null;
+    }
+
+    @Override
+    public String getUpperCaseCammelCase() {
+        return null;
+    }
+
+    @Override
+    public String getLowerCaseCammelCase() {
+        return null;
+    }
+
+    @Override
+    public OpenType<?> getOpenType() {
+        return null;
+    }
+}
index 597cf4ccc9d1cf033cb6ae919662c69ec89008f0..a06d2caa271d626c4c791f230cdb54aa5d61b5e2 100644 (file)
@@ -7,20 +7,6 @@
  */
 package org.opendaylight.controller.config.yangjmxgenerator;
 
-import static org.hamcrest.CoreMatchers.is;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertThat;
-import static org.mockito.Mockito.doReturn;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-
-import javax.management.openmbean.SimpleType;
-
 import org.junit.Test;
 import org.mockito.Mockito;
 import org.opendaylight.controller.config.yangjmxgenerator.attribute.JavaAttribute;
@@ -30,6 +16,13 @@ import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
 
+import javax.management.openmbean.SimpleType;
+import java.util.*;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.doReturn;
+
 public class RuntimeBeanEntryTest extends AbstractYangTest {
 
     public static final String PACKAGE_NAME = "packages.sis";
@@ -111,7 +104,8 @@ public class RuntimeBeanEntryTest extends AbstractYangTest {
         RuntimeBeanEntry.Rpc rpc = getRpcByName(rpcs, SLEEP_RPC_NAME);
         assertNotNull(rpc);
         assertThat(rpc.getYangName(), is(SLEEP_RPC_NAME));
-        assertThat(rpc.getReturnType().endsWith(SLEEP_RPC_OUTPUT), is(true));
+
+        assertThat(((JavaAttribute)rpc.getReturnType()).getType().getFullyQualifiedName().endsWith(SLEEP_RPC_OUTPUT),  is(true));
 
         // get sleep rpc input attribute and test it
         List<JavaAttribute> attributes = rpc.getParameters();
index 6245e6197a422e7b6b8496740383fcb1cfda5f61..291e3ebd948159d2a6191e6ca31d8452987486da 100644 (file)
@@ -291,11 +291,16 @@ module config-test-impl {
                     config:inner-state-bean;
 
                     rpcx:rpc-context-instance "inner-inner-test-rpc";
+                    rpcx:rpc-context-instance "complex-output-rpc";
 
                     key "simple-int3";
 
                     leaf simple-int3 {
-                            type uint16;
+                        type uint16;
+                    }
+
+                    leaf-list list-of-strings {
+                        type string;
                     }
 
                     list not-state-bean {
@@ -327,6 +332,8 @@ module config-test-impl {
     identity inner-test-rpc;
     identity inner-inner-test-rpc;
 
+    identity complex-output-rpc;
+
     rpc no-arg {
         input {
             uses rpcx:rpc-context-ref {
@@ -346,6 +353,66 @@ module config-test-impl {
         }
     }
 
+    rpc container-output {
+        input {
+            uses rpcx:rpc-context-ref {
+                refine context-instance {
+                    rpcx:rpc-context-instance complex-output-rpc;
+                }
+            }
+        }
+
+        output {
+            container retValContainer {
+                leaf v1 {
+                    type string;
+                }
+
+                leaf v2 {
+                    type uint32;
+                }
+            }
+        }
+    }
+
+    rpc leaf-list-output {
+        input {
+            uses rpcx:rpc-context-ref {
+                refine context-instance {
+                    rpcx:rpc-context-instance complex-output-rpc;
+                }
+            }
+        }
+
+        output {
+            leaf-list result {
+                type string;
+            }
+        }
+    }
+
+    rpc list-output {
+        input {
+            uses rpcx:rpc-context-ref {
+                refine context-instance {
+                    rpcx:rpc-context-instance complex-output-rpc;
+                }
+            }
+        }
+
+        output {
+            list ret-val-list {
+                leaf v2 {
+                    type uint32;
+                }
+
+                leaf v1 {
+                    type boolean;
+                }
+            }
+        }
+    }
+
     rpc noArgInner {
         input {
             uses rpcx:rpc-context-ref {
index 52b6801cc08a190ebd6da5b058dbfdc940bf0901..1c212e9d4b57656ae65aaca9a85d3e6699542cf7 100644 (file)
@@ -8,19 +8,17 @@
 
 package org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.mapping;
 
-import java.util.Map;
-import java.util.Map.Entry;
+import com.google.common.collect.Maps;
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.*;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.AttributeIfcSwitchStatement;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.Services;
 
 import javax.management.openmbean.ArrayType;
 import javax.management.openmbean.CompositeType;
 import javax.management.openmbean.OpenType;
 import javax.management.openmbean.SimpleType;
-
-import org.opendaylight.controller.config.yangjmxgenerator.attribute.*;
-import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.AttributeIfcSwitchStatement;
-import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.Services;
-
-import com.google.common.collect.Maps;
+import java.util.Map;
+import java.util.Map.Entry;
 
 public class ObjectMapper extends AttributeIfcSwitchStatement<AttributeMappingStrategy<?, ? extends OpenType<?>>> {
 
@@ -41,7 +39,7 @@ public class ObjectMapper extends AttributeIfcSwitchStatement<AttributeMappingSt
         return strategies;
     }
 
-    private AttributeMappingStrategy<?, ? extends OpenType<?>> prepareStrategy(AttributeIfc attributeIfc) {
+    public AttributeMappingStrategy<?, ? extends OpenType<?>> prepareStrategy(AttributeIfc attributeIfc) {
         return switchAttribute(attributeIfc);
     }
 
index b49d20ed3582bbe83d29a83df9ea04ec4852abf8..43d8db61783d0fc3d9aabaed1ddc6ee2fe7d0653 100644 (file)
@@ -39,7 +39,7 @@ public class ObjectXmlWriter extends AttributeIfcSwitchStatement<AttributeWritin
         return preparedWriting;
     }
 
-    private AttributeWritingStrategy prepareWritingStrategy(String key, AttributeIfc expectedAttr, Document document) {
+    public AttributeWritingStrategy prepareWritingStrategy(String key, AttributeIfc expectedAttr, Document document) {
         Preconditions.checkNotNull(expectedAttr, "Mbean attributes mismatch, unable to find expected attribute for %s",
                 key);
         this.document = document;
index 7bdfa277a00418d39b5724267947fcf7e4446a81..f838c6f9f5e69160b4c889d799f7e42ed2c7338c 100644 (file)
@@ -16,10 +16,13 @@ import org.opendaylight.controller.config.yang.store.api.YangStoreSnapshot;
 import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry;
 import org.opendaylight.controller.config.yangjmxgenerator.RuntimeBeanEntry;
 import org.opendaylight.controller.config.yangjmxgenerator.RuntimeBeanEntry.Rpc;
-import org.opendaylight.controller.config.yangjmxgenerator.attribute.SimpleTypeResolver;
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc;
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.VoidAttribute;
 import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
 import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml.AttributeConfigElement;
-import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.mapping.SimpleAttributeMappingStrategy;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.mapping.AttributeMappingStrategy;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.mapping.ObjectMapper;
+import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.toxml.ObjectXmlWriter;
 import org.opendaylight.controller.netconf.confignetconfconnector.mapping.rpc.InstanceRuntimeRpc;
 import org.opendaylight.controller.netconf.confignetconfconnector.mapping.rpc.ModuleRpcs;
 import org.opendaylight.controller.netconf.confignetconfconnector.mapping.rpc.Rpcs;
@@ -27,14 +30,14 @@ import org.opendaylight.controller.netconf.confignetconfconnector.operations.Abs
 import org.opendaylight.controller.netconf.confignetconfconnector.operations.Commit;
 import org.opendaylight.controller.netconf.mapping.api.HandlingPriority;
 import org.opendaylight.controller.netconf.util.xml.XmlElement;
-import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 
 import javax.management.ObjectName;
-import javax.management.openmbean.SimpleType;
+import javax.management.openmbean.OpenType;
 import java.util.Map;
 
 public class RuntimeRpc extends AbstractConfigNetconfOperation {
@@ -50,10 +53,22 @@ public class RuntimeRpc extends AbstractConfigNetconfOperation {
         this.yangStoreSnapshot = yangStoreSnapshot;
     }
 
-    private String getStringRepresentation(final Object result) {
-        final SimpleType<?> simpleType = SimpleTypeResolver.getSimpleType(result.getClass().getName());
-        final Optional<String> mappedAttributeOpt = new SimpleAttributeMappingStrategy(simpleType).mapAttribute(result);
-        return mappedAttributeOpt.isPresent() ? mappedAttributeOpt.get() : "";
+    private Element toXml(Document doc, Object result, AttributeIfc returnType, String namespace, String elementName) {
+        AttributeMappingStrategy<?, ? extends OpenType<?>> mappingStrategy = new ObjectMapper(null).prepareStrategy(returnType);
+        Optional<?> mappedAttributeOpt = mappingStrategy.mapAttribute(result);
+        Preconditions.checkState(mappedAttributeOpt.isPresent(), "Unable to map return value %s as %s", result, returnType.getOpenType());
+
+        // FIXME: multiple return values defined as leaf-list and list in yang should not be wrapped in output xml element,
+        // they need to be appended directly under rpc-reply element
+        //
+        // Either allow List of Elements to be returned from NetconfOperation or
+        // pass reference to parent output xml element for netconf operations to
+        // append result(s) on their own
+        Element tempParent = doc.createElementNS(XmlNetconfConstants.RFC4741_TARGET_NAMESPACE, "output");
+        new ObjectXmlWriter().prepareWritingStrategy(elementName, returnType, doc).writeElement(tempParent, namespace, mappedAttributeOpt.get());
+
+        XmlElement xmlElement = XmlElement.fromDomElement(tempParent);
+        return xmlElement.getChildElements().size() > 1 ? tempParent : xmlElement.getOnlyChildElement().getDomElement();
     }
 
     private Object executeOperation(final ConfigRegistryClient configRegistryClient, final ObjectName on,
@@ -161,12 +176,11 @@ public class RuntimeRpc extends AbstractConfigNetconfOperation {
         logger.info("Operation {} called successfully on {} with arguments {} with result {}", execution.operationName,
                 execution.on, execution.attributes, result);
 
-        if (execution.returnType.equals("void")) {
+        if (execution.isVoid()) {
             return document.createElement("ok");
         } else {
-            final Element output = XmlUtil.createTextElement(document, "result", getStringRepresentation(result));
-            XmlUtil.addNamespaceAttr(output, execution.namespace);
-            return output;
+            return toXml(document, result, execution.returnType, execution.namespace,
+                    execution.returnType.getAttributeYangName());
         }
     }
 
@@ -175,11 +189,11 @@ public class RuntimeRpc extends AbstractConfigNetconfOperation {
         private final ObjectName on;
         private final String operationName;
         private final Map<String, AttributeConfigElement> attributes;
-        private final String returnType;
+        private final AttributeIfc returnType;
         private final String namespace;
 
         public NetconfOperationExecution(final ObjectName on, final String name,
-                final Map<String, AttributeConfigElement> attributes, final String returnType, final String namespace) {
+                final Map<String, AttributeConfigElement> attributes, final AttributeIfc returnType, final String namespace) {
             this.on = on;
             this.operationName = name;
             this.attributes = attributes;
@@ -187,6 +201,10 @@ public class RuntimeRpc extends AbstractConfigNetconfOperation {
             this.namespace = namespace;
         }
 
+        boolean isVoid() {
+            return returnType == VoidAttribute.getInstance();
+        }
+
     }
 
     private static Map<String, AttributeConfigElement> sortAttributes(
index c72cb7498b709c2a2da727e0b5959fd8fb0ac0c9..62e1273e39e5193d28dca18ca8b4b91a5c4b990a 100644 (file)
@@ -407,12 +407,15 @@ public class NetconfMappingTest extends AbstractConfigTest {
 
         Element response = get();
 
+        System.err.println(XmlUtil.toString(response));
+
         assertEquals(2, getElementsSize(response, "instance"));
         assertEquals(2, getElementsSize(response, "asdf"));
         assertEquals(5, getElementsSize(response, "inner-running-data"));
         assertEquals(5, getElementsSize(response, "deep2"));
         assertEquals(11, getElementsSize(response, "inner-inner-running-data"));
         assertEquals(11, getElementsSize(response, "deep3"));
+        assertEquals(11 * 2, getElementsSize(response, "list-of-strings"));
         assertEquals(4, getElementsSize(response, "inner-running-data-additional"));
         assertEquals(4, getElementsSize(response, "deep4"));
         // TODO assert keys
@@ -427,6 +430,10 @@ public class NetconfMappingTest extends AbstractConfigTest {
 
         response = executeOp(netconf, "netconfMessages/rpcInnerInner.xml");
         assertThat(XmlUtil.toString(response), JUnitMatchers.containsString("true"));
+
+        response = executeOp(netconf, "netconfMessages/rpcInnerInner_complex_output.xml");
+        assertThat(XmlUtil.toString(response), JUnitMatchers.containsString("1"));
+        assertThat(XmlUtil.toString(response), JUnitMatchers.containsString("2"));
     }
 
     private Element get() throws NetconfDocumentedException, ParserConfigurationException, SAXException, IOException {
@@ -503,11 +510,31 @@ public class NetconfMappingTest extends AbstractConfigTest {
                 return new Deep3();
             }
 
+            @Override
+            public List<String> getListOfStrings() {
+                return Lists.newArrayList("l1", "l2");
+            }
+
+            @Override
+            public List<RetValList> listOutput() {
+                return Lists.newArrayList(new RetValList());
+            }
+
             @Override
             public Boolean noArgInnerInner(Integer integer, Boolean aBoolean) {
                 return aBoolean;
             }
 
+            @Override
+            public RetValContainer containerOutput() {
+                return new RetValContainer();
+            }
+
+            @Override
+            public List<String> leafListOutput() {
+                return Lists.newArrayList("1", "2");
+            }
+
         }
 
         reg.register(new InnerInnerRunningDataRuntimeMXBeanTest(counter++));
diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/rpcInnerInner_complex_output.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/rpcInnerInner_complex_output.xml
new file mode 100644 (file)
index 0000000..209c382
--- /dev/null
@@ -0,0 +1,8 @@
+<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <leaf-list-output
+            xmlns="urn:opendaylight:params:xml:ns:yang:controller:test:impl">
+        <context-instance>
+            /data/modules/module[name='impl-netconf']/instance[name='instance2']/inner-running-data[key='1015']/inner-inner-running-data[key='1017']
+        </context-instance>
+    </leaf-list-output>
+</rpc>