Merge "Fixing an unsorted pom change"
authorGiovanni Meo <gmeo@cisco.com>
Tue, 29 Apr 2014 14:18:45 +0000 (14:18 +0000)
committerGerrit Code Review <gerrit@opendaylight.org>
Tue, 29 Apr 2014 14:18:45 +0000 (14:18 +0000)
22 files changed:
opendaylight/config/yang-jmx-generator-plugin/pom.xml
opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/gofactory/AbsFactoryGeneratedObjectFactory.groovy [deleted file]
opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/gofactory/AbsFactoryGeneratedObjectFactory.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/gofactory/AbsModuleGeneratedObjectFactory.groovy [deleted file]
opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/gofactory/AbsModuleGeneratedObjectFactory.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/gofactory/GenericGeneratedObjectFactory.groovy [deleted file]
opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/gofactory/GenericGeneratedObjectFactory.java [new file with mode: 0644]
opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientSession.java
opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientSessionNegotiator.java
opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientSessionNegotiatorFactory.java
opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSessionNegotiator.java
opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSessionNegotiatorFactory.java
opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/ConcurrentClientsTest.java
opendaylight/netconf/netconf-it/pom.xml
opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/pax/IdentityRefNetconfTest.java
opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/AbstractNetconfSession.java
opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/AbstractNetconfSessionNegotiator.java
opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/NetconfEXIToMessageDecoder.java
opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/NetconfXMLToHelloMessageDecoder.java
opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/NetconfXMLToMessageDecoder.java
opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/messages/NetconfHelloMessage.java
opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/messages/SendErrorExceptionUtil.java

index 2d49ed7fab1bb21b595d68d35683c8d9e7d7f85a..b8831f6979ec3205e496c13ee1df15217d3d9feb 100644 (file)
 
   </dependencies>
 
-  <build>
-    <plugins>
-      <plugin>
-        <artifactId>maven-compiler-plugin</artifactId>
-        <version>3.1</version>
-        <configuration>
-          <compilerId>groovy-eclipse-compiler</compilerId>
-          <verbose>false</verbose>
-        </configuration>
-        <dependencies>
-
-          <dependency>
-            <groupId>org.codehaus.groovy</groupId>
-            <artifactId>groovy-eclipse-batch</artifactId>
-            <version>2.1.8-01</version>
-          </dependency>
-          <dependency>
-            <groupId>org.codehaus.groovy</groupId>
-            <artifactId>groovy-eclipse-compiler</artifactId>
-            <version>2.8.0-01</version>
-          </dependency>
-        </dependencies>
-      </plugin>
-
-    </plugins>
-  </build>
 </project>
diff --git a/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/gofactory/AbsFactoryGeneratedObjectFactory.groovy b/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/gofactory/AbsFactoryGeneratedObjectFactory.groovy
deleted file mode 100644 (file)
index baff88c..0000000
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
- * 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.plugin.gofactory
-import com.google.common.base.Optional
-import org.opendaylight.controller.config.api.DependencyResolver
-import org.opendaylight.controller.config.api.DynamicMBeanWithInstance
-import org.opendaylight.controller.config.api.ModuleIdentifier
-import org.opendaylight.controller.config.api.annotations.AbstractServiceInterface
-import org.opendaylight.controller.config.api.annotations.Description
-import org.opendaylight.controller.config.spi.Module
-import org.opendaylight.controller.config.spi.ModuleFactory
-import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry
-import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.AbstractFactoryTemplate
-import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.TemplateFactory
-import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Annotation
-import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Field
-import org.opendaylight.controller.config.yangjmxgenerator.plugin.java.*
-import org.opendaylight.yangtools.yang.common.QName
-import org.osgi.framework.BundleContext
-
-public class AbsFactoryGeneratedObjectFactory {
-
-    public GeneratedObject toGeneratedObject(ModuleMXBeanEntry mbe, Optional<String> copyright) {
-        FullyQualifiedName absFactoryFQN = new FullyQualifiedName(mbe.packageName, mbe.abstractFactoryName)
-        FullyQualifiedName moduleFQN = new FullyQualifiedName(mbe.packageName, mbe.stubModuleName)
-        Optional<String> classJavaDoc = Optional.fromNullable(mbe.getNullableDescription())
-
-        AbstractFactoryTemplate abstractFactoryTemplate = TemplateFactory.abstractFactoryTemplateFromMbe(mbe)
-        Optional<String> header = abstractFactoryTemplate.headerString;
-        List<FullyQualifiedName> providedServices = mbe.providedServices.keySet().collect {
-            FullyQualifiedName.fromString(it)
-        }
-
-
-        return toGeneratedObject(absFactoryFQN, copyright,
-                header, classJavaDoc, mbe.yangModuleQName,
-                mbe.globallyUniqueName,
-                providedServices,
-                moduleFQN,
-                abstractFactoryTemplate.fields)
-    }
-
-    public GeneratedObject toGeneratedObject(FullyQualifiedName absFactoryFQN, Optional<String> copyright,
-                                             Optional<String> header, Optional<String> classJavaDoc, QName yangModuleQName,
-                                             String globallyUniqueName,
-                                             List<FullyQualifiedName> providedServices,
-                                             FullyQualifiedName moduleFQN,
-                                             List<Field> moduleFields) {
-        JavaFileInputBuilder b = new JavaFileInputBuilder()
-        Annotation moduleQNameAnnotation = Annotation.createModuleQNameANnotation(yangModuleQName)
-        b.addClassAnnotation(moduleQNameAnnotation)
-
-        b.setFqn(absFactoryFQN)
-        b.setTypeName(TypeName.absClassType)
-
-        b.setCopyright(copyright);
-        b.setHeader(header);
-        b.setClassJavaDoc(classJavaDoc);
-        b.addImplementsFQN(new FullyQualifiedName(ModuleFactory))
-        if (classJavaDoc.isPresent()) {
-            b.addClassAnnotation("@${Description.canonicalName}(value=\"${classJavaDoc.get()}\")")
-        }
-
-        b.addToBody("public static final java.lang.String NAME = \"${globallyUniqueName}\";")
-        b.addToBody("private static final java.util.Set<Class<? extends ${AbstractServiceInterface.canonicalName}>> serviceIfcs;")
-
-        b.addToBody("@Override\n public final String getImplementationName() { \n return NAME; \n}")
-
-        b.addToBody(getServiceIfcsInitialization(providedServices))
-
-        // createModule
-        b.addToBody("""
-            @Override
-            public ${Module.canonicalName} createModule(String instanceName, ${DependencyResolver.canonicalName} dependencyResolver, ${BundleContext.canonicalName} bundleContext) {
-                return instantiateModule(instanceName, dependencyResolver, bundleContext);
-            }
-            """)
-
-        b.addToBody(getCreateModule(moduleFQN, moduleFields))
-
-        b.addToBody("""
-            public ${moduleFQN} instantiateModule(String instanceName, ${DependencyResolver.canonicalName} dependencyResolver, ${moduleFQN} oldModule, ${AutoCloseable.canonicalName} oldInstance, ${BundleContext.canonicalName} bundleContext) {
-                return new ${moduleFQN}(new ${ModuleIdentifier.canonicalName}(NAME, instanceName), dependencyResolver, oldModule, oldInstance);
-            }
-            """)
-
-        b.addToBody("""
-            public ${moduleFQN} instantiateModule(String instanceName, ${DependencyResolver.canonicalName} dependencyResolver, ${BundleContext.canonicalName} bundleContext) {
-                return new ${moduleFQN}(new ${ModuleIdentifier.canonicalName}(NAME, instanceName), dependencyResolver);
-            }
-            """)
-
-        b.addToBody("""
-            public ${moduleFQN} handleChangedClass(${DynamicMBeanWithInstance.canonicalName} old) throws Exception {
-                throw new UnsupportedOperationException("Class reloading is not supported");
-            }
-            """)
-
-        b.addToBody("""
-            @Override
-            public java.util.Set<${moduleFQN}> getDefaultModules(org.opendaylight.controller.config.api.DependencyResolverFactory dependencyResolverFactory, ${BundleContext.canonicalName} bundleContext) {
-                return new java.util.HashSet<${moduleFQN}>();
-            }
-            """)
-
-        return new GeneratedObjectBuilder(b.build()).toGeneratedObject()
-    }
-
-    private static String getCreateModule(FullyQualifiedName moduleFQN, List<Field> moduleFields) {
-        String result = """
-            @Override
-            public ${Module.canonicalName} createModule(String instanceName, ${DependencyResolver.canonicalName} dependencyResolver, ${DynamicMBeanWithInstance.canonicalName} old, ${BundleContext.canonicalName} bundleContext) throws Exception {
-                ${moduleFQN} oldModule = null;
-                try {
-                    oldModule = (${moduleFQN}) old.getModule();
-                } catch(Exception e) {
-                    return handleChangedClass(old);
-                }
-                ${moduleFQN} module = instantiateModule(instanceName, dependencyResolver, oldModule, old.getInstance(), bundleContext);
-            """
-        result += moduleFields.collect{"module.set${it.name}(oldModule.get${it.name}());"}.join("\n")
-        result += """
-                return module;
-            }
-            """
-        return result
-    }
-
-    private static String getServiceIfcsInitialization(List<FullyQualifiedName> providedServices) {
-        String generic = "Class<? extends ${AbstractServiceInterface.canonicalName}>"
-
-        String result = """static {
-            java.util.Set<${generic}> serviceIfcs2 = new java.util.HashSet<${generic}>();
-            """
-        result += providedServices.collect{"serviceIfcs2.add(${it}.class);"}.join("\n")
-        result += """serviceIfcs = java.util.Collections.unmodifiableSet(serviceIfcs2);
-            }
-            """
-
-        // add isModuleImplementingServiceInterface and getImplementedServiceIntefaces methods
-
-        result += """
-            @Override
-            public final boolean isModuleImplementingServiceInterface(Class<? extends ${AbstractServiceInterface.canonicalName}> serviceInterface) {
-                for (Class<?> ifc: serviceIfcs) {
-                    if (serviceInterface.isAssignableFrom(ifc)){
-                        return true;
-                    }
-                }
-                return false;
-            }
-
-            @Override
-            public java.util.Set<Class<? extends ${AbstractServiceInterface.canonicalName}>> getImplementedServiceIntefaces() {
-                return serviceIfcs;
-            }
-            """
-
-        return result
-    }
-
-}
diff --git a/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/gofactory/AbsFactoryGeneratedObjectFactory.java b/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/gofactory/AbsFactoryGeneratedObjectFactory.java
new file mode 100644 (file)
index 0000000..48a6c15
--- /dev/null
@@ -0,0 +1,183 @@
+/*
+ * 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.plugin.gofactory;
+
+import static java.lang.String.format;
+
+import com.google.common.base.Optional;
+import java.util.ArrayList;
+import java.util.List;
+import org.opendaylight.controller.config.api.DependencyResolver;
+import org.opendaylight.controller.config.api.DynamicMBeanWithInstance;
+import org.opendaylight.controller.config.api.ModuleIdentifier;
+import org.opendaylight.controller.config.api.annotations.AbstractServiceInterface;
+import org.opendaylight.controller.config.api.annotations.Description;
+import org.opendaylight.controller.config.spi.Module;
+import org.opendaylight.controller.config.spi.ModuleFactory;
+import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.AbstractFactoryTemplate;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.TemplateFactory;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Annotation;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Field;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.java.FullyQualifiedName;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.java.GeneratedObject;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.java.GeneratedObjectBuilder;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.java.JavaFileInputBuilder;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.java.TypeName;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.osgi.framework.BundleContext;
+
+public class AbsFactoryGeneratedObjectFactory {
+
+    public GeneratedObject toGeneratedObject(ModuleMXBeanEntry mbe, Optional<String> copyright) {
+        FullyQualifiedName absFactoryFQN = new FullyQualifiedName(mbe.getPackageName(), mbe.getAbstractFactoryName());
+        FullyQualifiedName moduleFQN = new FullyQualifiedName(mbe.getPackageName(), mbe.getStubModuleName());
+        Optional<String> classJavaDoc = Optional.fromNullable(mbe.getNullableDescription());
+
+        AbstractFactoryTemplate abstractFactoryTemplate = TemplateFactory.abstractFactoryTemplateFromMbe(mbe);
+        Optional<String> header = abstractFactoryTemplate.getHeaderString();
+
+        List<FullyQualifiedName> providedServices = new ArrayList<>();
+        for(String providedService: mbe.getProvidedServices().keySet()) {
+            providedServices.add(FullyQualifiedName.fromString(providedService));
+        }
+
+        return toGeneratedObject(absFactoryFQN, copyright,
+                header, classJavaDoc, mbe.getYangModuleQName(),
+                mbe.getGloballyUniqueName(),
+                providedServices,
+                moduleFQN,
+                abstractFactoryTemplate.getFields());
+    }
+
+    public GeneratedObject toGeneratedObject(FullyQualifiedName absFactoryFQN, Optional<String> copyright,
+                                             Optional<String> header, Optional<String> classJavaDoc, QName yangModuleQName,
+                                             String globallyUniqueName,
+                                             List<FullyQualifiedName> providedServices,
+                                             FullyQualifiedName moduleFQN,
+                                             List<Field> moduleFields) {
+        JavaFileInputBuilder b = new JavaFileInputBuilder();
+        Annotation moduleQNameAnnotation = Annotation.createModuleQNameANnotation(yangModuleQName);
+        b.addClassAnnotation(moduleQNameAnnotation);
+
+        b.setFqn(absFactoryFQN);
+        b.setTypeName(TypeName.absClassType);
+
+        b.setCopyright(copyright);
+        b.setHeader(header);
+        b.setClassJavaDoc(classJavaDoc);
+        b.addImplementsFQN(new FullyQualifiedName(ModuleFactory.class));
+        if (classJavaDoc.isPresent()) {
+            b.addClassAnnotation(format("@%s(value=\"%s\")", Description.class.getCanonicalName(), classJavaDoc.get()));
+        }
+
+        b.addToBody(format("public static final java.lang.String NAME = \"%s\";", globallyUniqueName));
+        b.addToBody(format("private static final java.util.Set<Class<? extends %s>> serviceIfcs;",
+                AbstractServiceInterface.class.getCanonicalName()));
+
+        b.addToBody("@Override\n public final String getImplementationName() { \n return NAME; \n}");
+
+        b.addToBody(getServiceIfcsInitialization(providedServices));
+
+        // createModule
+        b.addToBody(format("\n"+
+            "@Override\n"+
+            "public %s createModule(String instanceName, %s dependencyResolver, %s bundleContext) {\n"+
+                "return instantiateModule(instanceName, dependencyResolver, bundleContext);\n"+
+            "}\n",
+                Module.class.getCanonicalName(), DependencyResolver.class.getCanonicalName(), BundleContext.class.getCanonicalName()));
+
+        b.addToBody(getCreateModule(moduleFQN, moduleFields));
+
+        b.addToBody(format("\n"+
+            "public %s instantiateModule(String instanceName, %s dependencyResolver, %s oldModule, %s oldInstance, %s bundleContext) {\n"+
+                "return new %s(new %s(NAME, instanceName), dependencyResolver, oldModule, oldInstance);\n"+
+            "}\n",
+                moduleFQN, DependencyResolver.class.getCanonicalName(), moduleFQN, AutoCloseable.class.getCanonicalName(),
+                BundleContext.class.getCanonicalName(), moduleFQN, ModuleIdentifier.class.getCanonicalName()));
+
+        b.addToBody(format("\n"+
+            "public %s instantiateModule(String instanceName, %s dependencyResolver, %s bundleContext) {\n"+
+                "return new %s(new %s(NAME, instanceName), dependencyResolver);\n"+
+            "}\n", moduleFQN, DependencyResolver.class.getCanonicalName(), BundleContext.class.getCanonicalName(),
+                moduleFQN, ModuleIdentifier.class.getCanonicalName()
+        ));
+
+        b.addToBody(format("\n"+
+            "public %s handleChangedClass(%s old) throws Exception {\n"+
+                "throw new UnsupportedOperationException(\"Class reloading is not supported\");\n"+
+            "}\n", moduleFQN, DynamicMBeanWithInstance.class.getCanonicalName()));
+
+        b.addToBody(format("\n"+
+            "@Override\n"+
+            "public java.util.Set<%s> getDefaultModules(org.opendaylight.controller.config.api.DependencyResolverFactory dependencyResolverFactory, %s bundleContext) {\n"+
+                "return new java.util.HashSet<%s>();\n"+
+            "}\n", moduleFQN, BundleContext.class.getCanonicalName(), moduleFQN));
+
+        return new GeneratedObjectBuilder(b.build()).toGeneratedObject();
+    }
+
+    private static String getCreateModule(FullyQualifiedName moduleFQN, List<Field> moduleFields) {
+        String result = "\n"+
+            "@Override\n"+
+            format("public %s createModule(String instanceName, %s dependencyResolver, %s old, %s bundleContext) throws Exception {\n",
+                                Module.class.getCanonicalName(),DependencyResolver.class.getCanonicalName(),
+                                DynamicMBeanWithInstance.class.getCanonicalName(),BundleContext.class.getCanonicalName())+
+                format("%s oldModule = null;\n",moduleFQN)+
+                "try {\n"+
+                    format("oldModule = (%s) old.getModule();\n",moduleFQN)+
+                "} catch(Exception e) {\n"+
+                    "return handleChangedClass(old);\n"+
+                "}\n"+
+            format("%s module = instantiateModule(instanceName, dependencyResolver, oldModule, old.getInstance(), bundleContext);\n", moduleFQN);
+
+        for(Field field: moduleFields) {
+            result += format("module.set%s(oldModule.get%1$s());\n", field.getName());
+        }
+
+        result += "\n"+
+                "return module;\n"+
+            "}\n";
+        return result;
+    }
+
+    private static String getServiceIfcsInitialization(List<FullyQualifiedName> providedServices) {
+        String generic = format("Class<? extends %s>", AbstractServiceInterface.class.getCanonicalName());
+
+        String result = format("static {\n"+
+            "java.util.Set<%1$s> serviceIfcs2 = new java.util.HashSet<%1$s>();\n", generic);
+
+        for(FullyQualifiedName fqn: providedServices) {
+            result += format("serviceIfcs2.add(%s.class);\n", fqn);
+        }
+        result += "serviceIfcs = java.util.Collections.unmodifiableSet(serviceIfcs2);\n"+
+            "}\n";
+
+        // add isModuleImplementingServiceInterface and getImplementedServiceIntefaces methods
+
+        result += format("\n"+
+            "@Override\n"+
+            "public final boolean isModuleImplementingServiceInterface(Class<? extends %1$s> serviceInterface) {\n"+
+                "for (Class<?> ifc: serviceIfcs) {\n"+
+                    "if (serviceInterface.isAssignableFrom(ifc)){\n"+
+                        "return true;\n"+
+                    "}\n"+
+                "}\n"+
+                "return false;\n"+
+            "}\n"+
+            "\n"+
+            "@Override\n"+
+            "public java.util.Set<Class<? extends %1$s>> getImplementedServiceIntefaces() {\n"+
+                "return serviceIfcs;\n"+
+            "}\n", AbstractServiceInterface.class.getCanonicalName());
+
+        return result;
+    }
+
+}
diff --git a/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/gofactory/AbsModuleGeneratedObjectFactory.groovy b/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/gofactory/AbsModuleGeneratedObjectFactory.groovy
deleted file mode 100644 (file)
index 930acff..0000000
+++ /dev/null
@@ -1,400 +0,0 @@
-/*
- * 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.plugin.gofactory
-import com.google.common.base.Optional
-import org.opendaylight.controller.config.api.DependencyResolver
-import org.opendaylight.controller.config.api.ModuleIdentifier
-import org.opendaylight.controller.config.api.annotations.Description
-import org.opendaylight.controller.config.api.runtime.RootRuntimeBeanRegistrator
-import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry
-import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.AbstractModuleTemplate
-import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.TemplateFactory
-import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Annotation
-import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.IdentityRefModuleField
-import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Method
-import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.ModuleField
-import org.opendaylight.controller.config.yangjmxgenerator.plugin.java.*
-import org.opendaylight.yangtools.yang.common.QName
-import org.slf4j.Logger
-import org.slf4j.LoggerFactory
-
-public class AbsModuleGeneratedObjectFactory {
-
-    public GeneratedObject toGeneratedObject(ModuleMXBeanEntry mbe, Optional<String> copyright) {
-        FullyQualifiedName abstractFQN = new FullyQualifiedName(mbe.getPackageName(), mbe.getAbstractModuleName())
-        Optional<String> classJavaDoc = Optional.fromNullable(mbe.getNullableDescription())
-        AbstractModuleTemplate abstractModuleTemplate = TemplateFactory.abstractModuleTemplateFromMbe(mbe)
-        Optional<String> header = abstractModuleTemplate.headerString;
-        List<FullyQualifiedName> implementedInterfaces = abstractModuleTemplate.getTypeDeclaration().getImplemented().collect {
-            FullyQualifiedName.fromString(it)
-        }
-        Optional<FullyQualifiedName> maybeRegistratorType
-        if (abstractModuleTemplate.isRuntime()) {
-            maybeRegistratorType = Optional.of(FullyQualifiedName.fromString(abstractModuleTemplate.getRegistratorType()))
-        } else {
-            maybeRegistratorType = Optional.absent()
-        }
-
-        return toGeneratedObject(abstractFQN, copyright, header, classJavaDoc, implementedInterfaces,
-                abstractModuleTemplate.getModuleFields(), maybeRegistratorType, abstractModuleTemplate.getMethods(),
-                mbe.yangModuleQName
-        )
-    }
-
-    public GeneratedObject toGeneratedObject(FullyQualifiedName abstractFQN,
-                                             Optional<String> copyright,
-                                             Optional<String> header,
-                                             Optional<String> classJavaDoc,
-                                             List<FullyQualifiedName> implementedInterfaces,
-                                             List<ModuleField> moduleFields,
-                                             Optional<FullyQualifiedName> maybeRegistratorType,
-                                             List<Method> methods,
-                                             QName yangModuleQName) {
-        JavaFileInputBuilder b = new JavaFileInputBuilder()
-
-        Annotation moduleQNameAnnotation = Annotation.createModuleQNameANnotation(yangModuleQName)
-        b.addClassAnnotation(moduleQNameAnnotation)
-
-        b.setFqn(abstractFQN)
-        b.setTypeName(TypeName.absClassType)
-
-        b.setCopyright(copyright);
-        b.setHeader(header);
-        b.setClassJavaDoc(classJavaDoc);
-        implementedInterfaces.each { b.addImplementsFQN(it) }
-        if (classJavaDoc.isPresent()) {
-            b.addClassAnnotation("@${Description.canonicalName}(value=\"${classJavaDoc.get()}\")")
-        }
-
-        // add logger:
-        b.addToBody(getLogger(abstractFQN));
-
-        b.addToBody("//attributes start");
-
-        b.addToBody(moduleFields.collect { it.toString() }.join("\n"))
-
-        b.addToBody("//attributes end");
-
-
-        b.addToBody(getCommonFields(abstractFQN));
-
-
-        b.addToBody(getNewConstructor(abstractFQN))
-        b.addToBody(getCopyFromOldConstructor(abstractFQN))
-
-        b.addToBody(getRuntimeRegistratorCode(maybeRegistratorType))
-        b.addToBody(getValidationMethods(moduleFields))
-
-        b.addToBody(getCachesOfResolvedDependencies(moduleFields))
-        b.addToBody(getCachesOfResolvedIdentityRefs(moduleFields))
-        b.addToBody(getGetInstance(moduleFields))
-        b.addToBody(getReuseLogic(moduleFields, abstractFQN))
-        b.addToBody(getEqualsAndHashCode(abstractFQN))
-
-        b.addToBody(getMethods(methods))
-
-        return new GeneratedObjectBuilder(b.build()).toGeneratedObject()
-    }
-
-    private static String getMethods(List<Method> methods) {
-        String result = """
-            // getters and setters
-        """
-        result += methods.collect{it.toString()}.join("\n")
-        return result
-    }
-
-    private static String getEqualsAndHashCode(FullyQualifiedName abstractFQN) {
-        return """
-            @Override
-            public boolean equals(Object o) {
-                if (this == o) return true;
-                if (o == null || getClass() != o.getClass()) return false;
-                ${abstractFQN.typeName} that = (${abstractFQN.typeName}) o;
-                return identifier.equals(that.identifier);
-            }
-
-            @Override
-            public int hashCode() {
-                return identifier.hashCode();
-            }
-        """
-    }
-
-    private static String getReuseLogic(List<ModuleField> moduleFields, FullyQualifiedName abstractFQN) {
-        String result = """
-            public boolean canReuseInstance(${abstractFQN.typeName} oldModule){
-                // allow reusing of old instance if no parameters was changed
-                return isSame(oldModule);
-            }
-
-            public ${AutoCloseable.canonicalName} reuseInstance(${AutoCloseable.canonicalName} oldInstance){
-                // implement if instance reuse should be supported. Override canReuseInstance to change the criteria.
-                return oldInstance;
-            }
-            """
-        // isSame method that detects changed fields
-        result += """
-            public boolean isSame(${abstractFQN.typeName} other) {
-                if (other == null) {
-                    throw new IllegalArgumentException("Parameter 'other' is null");
-                }
-            """
-        // loop through fields, do deep equals on each field
-        result += moduleFields.collect { field ->
-            if (field.isListOfDependencies()) {
-                return """
-                    if (${field.name}Dependency.equals(other.${field.name}Dependency) == false) {
-                        return false;
-                    }
-                    for (int idx = 0; idx < ${field.name}Dependency.size(); idx++) {
-                        if (${field.name}Dependency.get(idx) != other.${field.name}Dependency.get(idx)) {
-                            return false;
-                        }
-                    }
-                """
-            } else if (field.isDependent()) {
-                return """
-                    if (${field.name}Dependency != other.${field.name}Dependency) { // reference to dependency must be same
-                        return false;
-                    }
-                """
-            } else {
-                return """
-                    if (java.util.Objects.deepEquals(${field.name}, other.${field.name}) == false) {
-                        return false;
-                    }
-                """
-            }
-        }.join("\n")
-
-
-        result += """
-                return true;
-            }
-            """
-
-        return result
-    }
-
-    private static String getGetInstance(List<ModuleField> moduleFields) {
-        String result = """
-            @Override
-            public final ${AutoCloseable.canonicalName} getInstance() {
-                if(instance==null) {
-            """
-        // create instance start
-
-        // loop through dependent fields, use dependency resolver to instantiate dependencies. Do it in loop in case field represents list of dependencies.
-        Map<ModuleField, String> resolveDependenciesMap = moduleFields.findAll {
-            it.isDependent()
-        }.collectEntries { ModuleField field ->
-            [field, field.isList() ?
-                    """
-                ${field.name}Dependency = new java.util.ArrayList<${field.dependency.sie.exportedOsgiClassName}>();
-                for(javax.management.ObjectName dep : ${field.name}) {
-                    ${field.name}Dependency.add(dependencyResolver.resolveInstance(${
-                        field.dependency.sie.exportedOsgiClassName
-                    }.class, dep, ${field.name}JmxAttribute));
-                }
-                """
-                    :
-                    """
-                ${field.name}Dependency = dependencyResolver.resolveInstance(${
-                        field.dependency.sie.exportedOsgiClassName
-                    }.class, ${field.name}, ${field.name}JmxAttribute);
-                """
-            ]
-        }
-        // wrap each field resolvation statement with if !=null when dependency is not mandatory
-        def wrapWithNullCheckClosure = {Map<ModuleField, String> map, predicate -> map.collect { ModuleField key, String value ->
-            predicate(key) ? """
-                if(${key.name}!=null) {
-                    ${value}
-                }
-                """ : value
-            }.join("\n")
-        }
-
-        result += wrapWithNullCheckClosure(resolveDependenciesMap, {ModuleField key ->
-            key.getDependency().isMandatory() == false} )
-
-        // add code to inject dependency resolver to fields that support it
-        Map<ModuleField, String> injectDepsMap = moduleFields.findAll { it.needsDepResolver }.collectEntries { field ->
-            if (field.isList()) {
-                return [field,"""
-                for(${field.genericInnerType} candidate : ${field.name}) {
-                    candidate.injectDependencyResolver(dependencyResolver);
-                }
-                """]
-            } else {
-                return [field, "${field.name}.injectDependencyResolver(dependencyResolver);"]
-            }
-        }
-
-        result += wrapWithNullCheckClosure(injectDepsMap, {true})
-
-        // identity refs need to be injected with dependencyResolver and base class
-        Map<ModuleField, String> resolveIdentityMap = moduleFields.findAll { it.isIdentityRef() }.collectEntries { IdentityRefModuleField field ->
-            [field,
-            "set${field.attributeName}(${field.name}.resolveIdentity(dependencyResolver, ${field.identityBaseClass}.class));"]
-        }
-
-        result += wrapWithNullCheckClosure(resolveIdentityMap, {true})
-
-        // create instance end: reuse and recreate logic
-        result += """
-                    if(oldInstance!=null && canReuseInstance(oldModule)) {
-                        instance = reuseInstance(oldInstance);
-                    } else {
-                        if(oldInstance!=null) {
-                            try {
-                                oldInstance.close();
-                            } catch(Exception e) {
-                                logger.error("An error occurred while closing old instance " + oldInstance, e);
-                            }
-                        }
-                        instance = createInstance();
-                        if (instance == null) {
-                            throw new IllegalStateException("Error in createInstance - null is not allowed as return value");
-                        }
-                    }
-                }
-                return instance;
-            }
-            public abstract ${AutoCloseable.canonicalName} createInstance();
-            """
-        return result
-    }
-
-    private static String getCommonFields(FullyQualifiedName abstractFQN) {
-        return """
-            private final ${abstractFQN.typeName} oldModule;
-            private final ${AutoCloseable.canonicalName} oldInstance;
-            private ${AutoCloseable.canonicalName} instance;
-            private final ${DependencyResolver.canonicalName} dependencyResolver;
-            private final ${ModuleIdentifier.canonicalName} identifier;
-            @Override
-            public ${ModuleIdentifier.canonicalName} getIdentifier() {
-                return identifier;
-            }
-            """
-    }
-
-    private static String getCachesOfResolvedIdentityRefs(List<ModuleField> moduleFields) {
-        return moduleFields.findAll { it.isIdentityRef() }.collect { IdentityRefModuleField field ->
-            "private ${field.identityClassType} ${field.identityClassName};"
-        }.join("\n")
-    }
-
-    private static String getCachesOfResolvedDependencies(List<ModuleField> moduleFields) {
-        return moduleFields.findAll { it.dependent }.collect { field ->
-            if (field.isList()) {
-                return """
-                    private java.util.List<${field.dependency.sie.exportedOsgiClassName}> ${
-                    field.name
-                }Dependency = new java.util.ArrayList<${field.dependency.sie.exportedOsgiClassName}>();
-                    protected final java.util.List<${field.dependency.sie.exportedOsgiClassName}> get${
-                    field.attributeName
-                }Dependency(){
-                        return ${field.name}Dependency;
-                    }
-                    """
-            } else {
-                return """
-                    private ${field.dependency.sie.exportedOsgiClassName} ${field.name}Dependency;
-                    protected final ${field.dependency.sie.exportedOsgiClassName} get${field.attributeName}Dependency(){
-                        return ${field.name}Dependency;
-                    }
-                    """
-            }
-        }.join("\n")
-    }
-
-    private static String getRuntimeRegistratorCode(Optional<FullyQualifiedName> maybeRegistratorType) {
-        if (maybeRegistratorType.isPresent()) {
-            String registratorType = maybeRegistratorType.get()
-
-            return """
-                private ${registratorType} rootRuntimeBeanRegistratorWrapper;
-
-                public ${registratorType} getRootRuntimeBeanRegistratorWrapper(){
-                    return rootRuntimeBeanRegistratorWrapper;
-                }
-
-                @Override
-                public void setRuntimeBeanRegistrator(${RootRuntimeBeanRegistrator.canonicalName} rootRuntimeRegistrator){
-                    this.rootRuntimeBeanRegistratorWrapper = new ${registratorType}(rootRuntimeRegistrator);
-                }
-                """
-        } else {
-            return ""
-        }
-    }
-
-    private static String getValidationMethods(List<ModuleField> moduleFields) {
-        String result = """
-            @Override
-            public void validate() {
-            """
-        // validate each mandatory dependency
-        List<String> lines = moduleFields.findAll{(it.dependent && it.dependency.mandatory)}.collect { field ->
-            if (field.isList()) {
-                return "" +
-                        "for(javax.management.ObjectName dep : ${field.name}) {\n" +
-                        "    dependencyResolver.validateDependency(${field.dependency.sie.fullyQualifiedName}.class, dep, ${field.name}JmxAttribute);\n" +
-                        "}\n"
-            } else {
-                return "dependencyResolver.validateDependency(${field.dependency.sie.fullyQualifiedName}.class, ${field.name}, ${field.name}JmxAttribute);"
-            }
-        }
-        result += lines.findAll { it.isEmpty() == false }.join("\n")
-        result += """
-                customValidation();
-            }
-
-            protected void customValidation(){
-            }
-        """
-        return result
-    }
-
-    private static String getLogger(FullyQualifiedName fqn) {
-        return "private static final ${Logger.canonicalName} logger = ${LoggerFactory.canonicalName}.getLogger(${fqn.toString()}.class);"
-    }
-
-    // assumes that each parameter name corresponds to an field in this class, constructs lines setting this.field = field;
-    private static String getConstructorStart(FullyQualifiedName fqn,
-                                              LinkedHashMap<String, String> parameters, String after) {
-        return "public ${fqn.typeName}(" +
-                parameters.collect { it.key + " " + it.value }.join(",") +
-                ") {\n" +
-                parameters.values().collect { "this.${it}=${it};\n" }.join() +
-                after +
-                "}\n"
-    }
-
-    private static String getNewConstructor(FullyQualifiedName abstractFQN) {
-        LinkedHashMap<String, String> parameters = [
-                (ModuleIdentifier.canonicalName): "identifier",
-                (DependencyResolver.canonicalName): "dependencyResolver"
-        ]
-        String setToNulls = ["oldInstance", "oldModule"].collect { "this.${it}=null;\n" }.join()
-        return getConstructorStart(abstractFQN, parameters, setToNulls)
-    }
-
-    private static String getCopyFromOldConstructor(FullyQualifiedName abstractFQN) {
-        LinkedHashMap<String, String> parameters = [
-                (ModuleIdentifier.canonicalName): "identifier",
-                (DependencyResolver.canonicalName): "dependencyResolver",
-                (abstractFQN.typeName): "oldModule",
-                (AutoCloseable.canonicalName): "oldInstance"
-        ]
-        return getConstructorStart(abstractFQN, parameters, "")
-    }
-}
diff --git a/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/gofactory/AbsModuleGeneratedObjectFactory.java b/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/gofactory/AbsModuleGeneratedObjectFactory.java
new file mode 100644 (file)
index 0000000..aa06cb9
--- /dev/null
@@ -0,0 +1,417 @@
+/*
+ * 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.plugin.gofactory;
+
+import static java.lang.String.format;
+
+import com.google.common.base.Joiner;
+import com.google.common.base.Optional;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import org.opendaylight.controller.config.api.DependencyResolver;
+import org.opendaylight.controller.config.api.ModuleIdentifier;
+import org.opendaylight.controller.config.api.annotations.Description;
+import org.opendaylight.controller.config.api.runtime.RootRuntimeBeanRegistrator;
+import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.AbstractModuleTemplate;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.TemplateFactory;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Annotation;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.IdentityRefModuleField;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Method;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.ModuleField;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.java.FullyQualifiedName;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.java.GeneratedObject;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.java.GeneratedObjectBuilder;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.java.JavaFileInputBuilder;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.java.TypeName;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class AbsModuleGeneratedObjectFactory {
+
+    public GeneratedObject toGeneratedObject(ModuleMXBeanEntry mbe, Optional<String> copyright) {
+        FullyQualifiedName abstractFQN = new FullyQualifiedName(mbe.getPackageName(), mbe.getAbstractModuleName());
+        Optional<String> classJavaDoc = Optional.fromNullable(mbe.getNullableDescription());
+        AbstractModuleTemplate abstractModuleTemplate = TemplateFactory.abstractModuleTemplateFromMbe(mbe);
+        Optional<String> header = abstractModuleTemplate.getHeaderString();
+
+        List<FullyQualifiedName> implementedInterfaces = new ArrayList<>();
+        for(String implemented: abstractModuleTemplate.getTypeDeclaration().getImplemented()) {
+            implementedInterfaces.add(FullyQualifiedName.fromString(implemented));
+        }
+        Optional<FullyQualifiedName> maybeRegistratorType;
+        if (abstractModuleTemplate.isRuntime()) {
+            maybeRegistratorType = Optional.of(FullyQualifiedName.fromString(abstractModuleTemplate.getRegistratorType()));
+        } else {
+            maybeRegistratorType = Optional.absent();
+        }
+
+        return toGeneratedObject(abstractFQN, copyright, header, classJavaDoc, implementedInterfaces,
+                abstractModuleTemplate.getModuleFields(), maybeRegistratorType, abstractModuleTemplate.getMethods(),
+                mbe.getYangModuleQName());
+    }
+
+    public GeneratedObject toGeneratedObject(FullyQualifiedName abstractFQN,
+                                             Optional<String> copyright,
+                                             Optional<String> header,
+                                             Optional<String> classJavaDoc,
+                                             List<FullyQualifiedName> implementedInterfaces,
+                                             List<ModuleField> moduleFields,
+                                             Optional<FullyQualifiedName> maybeRegistratorType,
+                                             List<? extends Method> methods,
+                                             QName yangModuleQName) {
+        JavaFileInputBuilder b = new JavaFileInputBuilder();
+
+        Annotation moduleQNameAnnotation = Annotation.createModuleQNameANnotation(yangModuleQName);
+        b.addClassAnnotation(moduleQNameAnnotation);
+
+        b.setFqn(abstractFQN);
+        b.setTypeName(TypeName.absClassType);
+
+        b.setCopyright(copyright);
+        b.setHeader(header);
+        b.setClassJavaDoc(classJavaDoc);
+        for(FullyQualifiedName implemented: implementedInterfaces) {
+            b.addImplementsFQN(implemented);
+        }
+        if (classJavaDoc.isPresent()) {
+            b.addClassAnnotation(format("@%s(value=\"%s\")", Description.class.getCanonicalName(), classJavaDoc.get()));
+        }
+
+        // add logger:
+        b.addToBody(getLogger(abstractFQN));
+
+        b.addToBody("//attributes start");
+        for(ModuleField moduleField: moduleFields) {
+            b.addToBody(moduleField.toString() +"\n");
+        }
+
+        b.addToBody("//attributes end");
+
+
+        b.addToBody(getCommonFields(abstractFQN));
+
+
+        b.addToBody(getNewConstructor(abstractFQN));
+        b.addToBody(getCopyFromOldConstructor(abstractFQN));
+
+        b.addToBody(getRuntimeRegistratorCode(maybeRegistratorType));
+        b.addToBody(getValidationMethods(moduleFields));
+
+        b.addToBody(getCachesOfResolvedDependencies(moduleFields));
+        b.addToBody(getCachesOfResolvedIdentityRefs(moduleFields));
+        b.addToBody(getGetInstance(moduleFields));
+        b.addToBody(getReuseLogic(moduleFields, abstractFQN));
+        b.addToBody(getEqualsAndHashCode(abstractFQN));
+
+        b.addToBody(getMethods(methods));
+
+        return new GeneratedObjectBuilder(b.build()).toGeneratedObject();
+    }
+
+    private static String getMethods(List<? extends Method>  methods) {
+        String result = "\n// getters and setters\n";
+        for(Method method: methods) {
+            result += method.toString()+"\n";
+        }
+        return result;
+    }
+
+    private static String getEqualsAndHashCode(FullyQualifiedName abstractFQN) {
+        return "\n"+
+            "@Override\n"+
+            "public boolean equals(Object o) {\n"+
+                "if (this == o) return true;\n"+
+                "if (o == null || getClass() != o.getClass()) return false;\n"+
+                format("%s that = (%1$s) o;\n", abstractFQN.getTypeName())+
+                "return identifier.equals(that.identifier);\n"+
+            "}\n"+
+            "\n"+
+            "@Override\n"+
+            "public int hashCode() {\n"+
+                "return identifier.hashCode();\n"+
+            "}\n";
+    }
+
+    private static String getReuseLogic(List<ModuleField> moduleFields, FullyQualifiedName abstractFQN) {
+        String result = "\n"+
+            format("public boolean canReuseInstance(%s oldModule){\n", abstractFQN.getTypeName())+
+                "// allow reusing of old instance if no parameters was changed\n"+
+                "return isSame(oldModule);\n"+
+            "}\n"+
+            "\n"+
+            format("public %s reuseInstance(%1$s oldInstance){\n", AutoCloseable.class.getCanonicalName())+
+                "// implement if instance reuse should be supported. Override canReuseInstance to change the criteria.\n"+
+                "return oldInstance;\n"+
+            "}\n";
+        // isSame method that detects changed fields
+        result += "\n"+
+            format("public boolean isSame(%s other) {\n", abstractFQN.getTypeName())+
+                "if (other == null) {\n"+
+                    "throw new IllegalArgumentException(\"Parameter 'other' is null\");\n"+
+                "}\n";
+        // loop through fields, do deep equals on each field
+
+        for (ModuleField moduleField : moduleFields) {
+            if (moduleField.isListOfDependencies()) {
+                result += format(
+                    "if (%1$sDependency.equals(other.%1$sDependency) == false) {\n"+
+                        "return false;\n"+
+                    "}\n"+
+                    "for (int idx = 0; idx < %1$sDependency.size(); idx++) {\n"+
+                        "if (%1$sDependency.get(idx) != other.%1$sDependency.get(idx)) {\n"+
+                            "return false;\n"+
+                        "}\n"+
+                    "}\n" ,moduleField.getName());
+            } else if (moduleField.isDependent()) {
+                result += format(
+                    "if (%sDependency != other.%1$sDependency) { // reference to dependency must be same\n"+
+                        "return false;\n"+
+                    "}\n",moduleField.getName());
+            } else {
+                result += format(
+                    "if (java.util.Objects.deepEquals(%s, other.%1$s) == false) {\n"+
+                        "return false;\n"+
+                    "}\n", moduleField.getName());
+            }
+        }
+        result += "\n"+
+                "return true;\n"+
+            "}\n";
+
+        return result;
+    }
+
+    private static String getGetInstance(List<ModuleField> moduleFields) {
+        String result = "\n"+
+            "@Override\n"+
+            format("public final %s getInstance() {\n", AutoCloseable.class.getCanonicalName())+
+                "if(instance==null) {\n";
+        // create instance start
+
+        // loop through dependent fields, use dependency resolver to instantiate dependencies. Do it in loop in case field represents list of dependencies.
+        Map<ModuleField, String> resolveDependenciesMap = new HashMap<>();
+        for(ModuleField moduleField: moduleFields) {
+            if (moduleField.isDependent()) {
+                String str;
+                String osgi = moduleField.getDependency().getSie().getExportedOsgiClassName();
+                if (moduleField.isList()) {
+                    str = format(
+                        "%sDependency = new java.util.ArrayList<%s>();\n"+
+                        "for(javax.management.ObjectName dep : %1$s) {\n"+
+                            "%1$sDependency.add(dependencyResolver.resolveInstance(%2$s.class, dep, %1$sJmxAttribute));\n"+
+                        "}\n", moduleField.getName(), osgi);
+                } else {
+                    str = format(
+                        "%1$sDependency = dependencyResolver.resolveInstance(%2$s.class, %1$s, %1$sJmxAttribute);",
+                        moduleField.getName(), osgi);
+                }
+                resolveDependenciesMap.put(moduleField, str);
+            }
+        }
+
+        // wrap each field resolvation statement with if !=null when dependency is not mandatory
+        for (Map.Entry<ModuleField, String> entry : resolveDependenciesMap.entrySet()) {
+            if (entry.getKey().getDependency().isMandatory() == false) {
+                result += format("if (%s!=null) {\n%s;\n}", entry.getKey().getName(), entry.getValue());
+            } else {
+                result += entry.getValue();
+            }
+        }
+
+        // add code to inject dependency resolver to fields that support it
+        for(ModuleField moduleField: moduleFields) {
+            if (moduleField.isNeedsDepResolver()) {
+                result += format("if (%s!=null){\n", moduleField.getName());
+                if (moduleField.isList()) {
+                    result += format(
+                        "for(%s candidate : %s) {\n"+
+                            "candidate.injectDependencyResolver(dependencyResolver);\n"+
+                        "}\n", moduleField.getGenericInnerType(), moduleField.getName());
+                } else {
+                    result += format("%s.injectDependencyResolver(dependencyResolver);\n", moduleField.getName());
+                }
+                result += "}\n";
+            }
+        }
+
+        // identity refs need to be injected with dependencyResolver and base class
+        for (ModuleField moduleField : moduleFields) {
+            if (moduleField.isIdentityRef()) {
+                result += format("if (%s!=null) {", moduleField.getName());
+                result += format("set%s(%s.resolveIdentity(dependencyResolver, %s.class));",
+                        moduleField.getAttributeName(), moduleField.getName(),
+                        ((IdentityRefModuleField)moduleField).getIdentityBaseClass());
+                result += "}\n";
+            }
+        }
+
+        // create instance end: reuse and recreate logic
+        result +=   "if(oldInstance!=null && canReuseInstance(oldModule)) {\n"+
+                        "instance = reuseInstance(oldInstance);\n"+
+                    "} else {\n"+
+                        "if(oldInstance!=null) {\n"+
+                           "try {\n"+
+                                "oldInstance.close();\n"+
+                            "} catch(Exception e) {\n"+
+                                "logger.error(\"An error occurred while closing old instance \" + oldInstance, e);\n"+
+                            "}\n"+
+                        "}\n"+
+                        "instance = createInstance();\n"+
+                        "if (instance == null) {\n"+
+                            "throw new IllegalStateException(\"Error in createInstance - null is not allowed as return value\");\n"+
+                        "}\n"+
+                    "}\n"+
+                "}\n"+
+                "return instance;\n"+
+            "}\n"+
+            format("public abstract %s createInstance();\n", AutoCloseable.class.getCanonicalName());
+
+        return result;
+    }
+
+    private static String getCommonFields(FullyQualifiedName abstractFQN) {
+        return "\n"+
+            format("private final %s oldModule;\n", abstractFQN.getTypeName())+
+            format("private final %s oldInstance;\n", AutoCloseable.class.getCanonicalName())+
+            format("private %s instance;\n", AutoCloseable.class.getCanonicalName())+
+            format("private final %s dependencyResolver;\n", DependencyResolver.class.getCanonicalName())+
+            format("private final %s identifier;\n", ModuleIdentifier.class.getCanonicalName())+
+            "@Override\n"+
+            format("public %s getIdentifier() {\n", ModuleIdentifier.class.getCanonicalName())+
+                "return identifier;\n"+
+            "}\n";
+    }
+
+    private static String getCachesOfResolvedIdentityRefs(List<ModuleField> moduleFields) {
+        StringBuilder result = new StringBuilder();
+        for (ModuleField moduleField : moduleFields) {
+            if (moduleField.isIdentityRef()) {
+                IdentityRefModuleField field = (IdentityRefModuleField) moduleField;
+                result.append(format("private %s %s;\n", field.getIdentityClassType(), field.getIdentityClassName()));
+            }
+        }
+        return result.toString();
+    }
+
+    private static String getCachesOfResolvedDependencies(List<ModuleField> moduleFields) {
+        StringBuilder result = new StringBuilder();
+        for (ModuleField moduleField: moduleFields) {
+            if (moduleField.isDependent()) {
+                String osgi = moduleField.getDependency().getSie().getExportedOsgiClassName();
+                if (moduleField.isList()) {
+                    result
+                            .append(format("private java.util.List<%s> %sDependency = new java.util.ArrayList<%s>();", osgi, moduleField.getName(), osgi))
+                            .append(format("protected final java.util.List<%s> get%sDependency(){\n", osgi, moduleField.getAttributeName()))
+                            .append(format("return %sDependency;\n", moduleField.getName()))
+                            .append("}\n");
+                } else {
+                    result.append(format(
+                    "private %s %sDependency;\n"+
+                    "protected final %s get%sDependency(){\n"+
+                        "return %sDependency;\n"+
+                    "}",
+                    osgi, moduleField.getName(), osgi, moduleField.getAttributeName(), moduleField.getName()));
+                }
+            }
+        }
+        return result.toString();
+    }
+
+    private static String getRuntimeRegistratorCode(Optional<FullyQualifiedName> maybeRegistratorType) {
+        if (maybeRegistratorType.isPresent()) {
+            String registratorType = maybeRegistratorType.get().toString();
+
+            return "\n"+
+                format("private %s rootRuntimeBeanRegistratorWrapper;\n", registratorType)+
+                "\n"+
+                format("public %s getRootRuntimeBeanRegistratorWrapper(){\n", registratorType)+
+                    "return rootRuntimeBeanRegistratorWrapper;\n"+
+                "}\n"+
+                "\n"+
+                "@Override\n"+
+                format("public void setRuntimeBeanRegistrator(%s rootRuntimeRegistrator){\n", RootRuntimeBeanRegistrator.class.getCanonicalName())+
+                    format("this.rootRuntimeBeanRegistratorWrapper = new %s(rootRuntimeRegistrator);\n", registratorType)+
+                "}\n";
+        } else {
+            return "";
+        }
+    }
+
+    private static String getValidationMethods(List<ModuleField> moduleFields) {
+        String result = "\n"+
+            "@Override\n"+
+            "public void validate() {\n";
+        // validate each mandatory dependency
+        for(ModuleField moduleField: moduleFields) {
+            if (moduleField.isDependent() && moduleField.getDependency().isMandatory()) {
+                if (moduleField.isList()) {
+                    result += "" +
+                            format("for(javax.management.ObjectName dep : %s) {\n", moduleField.getName()) +
+                            format("    dependencyResolver.validateDependency(%s.class, dep, %sJmxAttribute);\n",
+                                    moduleField.getDependency().getSie().getFullyQualifiedName(), moduleField.getName()) +
+                            "}\n";
+                } else {
+                    result += format("dependencyResolver.validateDependency(%s.class, %s, %sJmxAttribute);",
+                            moduleField.getDependency().getSie().getFullyQualifiedName(), moduleField.getName(), moduleField.getName());
+                }
+            }
+        }
+        result += "\n"+
+                "customValidation();\n"+
+            "}\n"+
+            "\n"+
+            "protected void customValidation() {\n"+
+            "}\n";
+        return result;
+    }
+
+    private static String getLogger(FullyQualifiedName fqn) {
+        return format("private static final %s logger = %s.getLogger(%s.class);",
+                Logger.class.getCanonicalName(), LoggerFactory.class.getCanonicalName(), fqn);
+    }
+
+    // assumes that each parameter name corresponds to an field in this class, constructs lines setting this.field = field;
+    private static String getConstructorStart(FullyQualifiedName fqn,
+                                              LinkedHashMap<String, String> parameters, String after) {
+        String paramString = Joiner.on(",").withKeyValueSeparator(" ").join(parameters);
+        String setters = "";
+        for (String paramName : parameters.values()) {
+            setters += format("this.%s = %1$s;\n", paramName);
+        }
+        return format("public %s(", fqn.getTypeName()) +
+                paramString +
+                ") {\n" +
+                setters +
+                after +
+                "}\n";
+    }
+
+    private static String getNewConstructor(FullyQualifiedName abstractFQN) {
+        LinkedHashMap<String, String> parameters = new LinkedHashMap<>();
+        parameters.put(ModuleIdentifier.class.getCanonicalName(), "identifier");
+        parameters.put(DependencyResolver.class.getCanonicalName(), "dependencyResolver");
+
+        String setToNulls = "this.oldInstance=null;\n;" +
+                "this.oldModule=null;\n";
+        return getConstructorStart(abstractFQN, parameters, setToNulls);
+    }
+
+    private static String getCopyFromOldConstructor(FullyQualifiedName abstractFQN) {
+        LinkedHashMap<String, String> parameters = new LinkedHashMap<>();
+        parameters.put(ModuleIdentifier.class.getCanonicalName(), "identifier");
+        parameters.put(DependencyResolver.class.getCanonicalName(), "dependencyResolver");
+        parameters.put(abstractFQN.getTypeName(), "oldModule");
+        parameters.put(AutoCloseable.class.getCanonicalName(), "oldInstance");
+        return getConstructorStart(abstractFQN, parameters, "");
+    }
+}
diff --git a/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/gofactory/GenericGeneratedObjectFactory.groovy b/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/gofactory/GenericGeneratedObjectFactory.groovy
deleted file mode 100644 (file)
index 6504aac..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * 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.plugin.gofactory
-
-import com.google.common.base.Optional
-import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.FtlTemplate
-import org.opendaylight.controller.config.yangjmxgenerator.plugin.java.FullyQualifiedName
-import org.opendaylight.controller.config.yangjmxgenerator.plugin.java.GeneratedObject
-import org.opendaylight.controller.config.yangjmxgenerator.plugin.java.GeneratedObjectBuilder
-import org.opendaylight.controller.config.yangjmxgenerator.plugin.java.JavaFileInputBuilder
-
-public class GenericGeneratedObjectFactory {
-
-    public GeneratedObject toGeneratedObject(FtlTemplate template, Optional<String> copyright) {
-        JavaFileInputBuilder b = new JavaFileInputBuilder();
-        b.setHeader(template.headerString)
-        b.setFqn(new FullyQualifiedName(template.packageName, template.typeDeclaration.name))
-        b.setClassJavaDoc(template.maybeJavadoc)
-        template.annotations.each { b.addClassAnnotation(it) }
-        // type declaration
-        template.typeDeclaration.extended.each { b.addExtendsFQN(FullyQualifiedName.fromString(it)) }
-        template.typeDeclaration.implemented.each { b.addImplementsFQN(FullyQualifiedName.fromString(it)) }
-        b.setCopyright(copyright);
-        b.setTypeName(template.typeDeclaration.toTypeName())
-        // fields
-        template.fields.each { b.addToBody(it.toString()) }
-        // constructors
-        template.constructors.each { b.addToBody(it.toString()) }
-        // methods
-        template.methods.each { b.addToBody(it.toString()) }
-
-        return new GeneratedObjectBuilder(b.build()).toGeneratedObject();
-    }
-}
diff --git a/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/gofactory/GenericGeneratedObjectFactory.java b/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/gofactory/GenericGeneratedObjectFactory.java
new file mode 100644 (file)
index 0000000..11bb677
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * 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.plugin.gofactory;
+
+import com.google.common.base.Optional;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.FtlTemplate;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Annotation;
+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.Method;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.java.FullyQualifiedName;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.java.GeneratedObject;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.java.GeneratedObjectBuilder;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.java.JavaFileInputBuilder;
+
+public class GenericGeneratedObjectFactory {
+
+    public GeneratedObject toGeneratedObject(FtlTemplate template, Optional<String> copyright) {
+        JavaFileInputBuilder b = new JavaFileInputBuilder();
+        b.setHeader(template.getHeaderString());
+        b.setFqn(new FullyQualifiedName(template.getPackageName(), template.getTypeDeclaration().getName()));
+        b.setClassJavaDoc(template.getMaybeJavadoc());
+        for (Annotation annotation : template.getAnnotations()) {
+            b.addClassAnnotation(annotation);
+        }
+        // type declaration
+        for (String extended : template.getTypeDeclaration().getExtended()) {
+            b.addExtendsFQN(FullyQualifiedName.fromString(extended));
+        }
+        for (String implemented : template.getTypeDeclaration().getImplemented()) {
+            b.addImplementsFQN(FullyQualifiedName.fromString(implemented));
+        }
+        b.setCopyright(copyright);
+        b.setTypeName(template.getTypeDeclaration().toTypeName());
+        // fields
+        for (Field field : template.getFields()) {
+            b.addToBody(field.toString());
+        }
+        // constructors
+        for (Constructor constructor : template.getConstructors()) {
+            b.addToBody(constructor.toString());
+        }
+        // methods
+        for (Method method : template.getMethods()) {
+            b.addToBody(method.toString());
+        }
+        return new GeneratedObjectBuilder(b.build()).toGeneratedObject();
+    }
+}
index ad50fedf6b564fbef12cfd6f8e3308b717d9515a..f4efb1fc7dc9bf56cd3176c769f72c312ee351d2 100644 (file)
@@ -8,7 +8,8 @@
 
 package org.opendaylight.controller.netconf.client;
 
-import io.netty.channel.Channel;
+import java.util.Collection;
+
 import org.opendaylight.controller.netconf.util.AbstractNetconfSession;
 import org.opendaylight.controller.netconf.util.handler.NetconfEXICodec;
 import org.opendaylight.controller.netconf.util.handler.NetconfEXIToMessageDecoder;
@@ -18,7 +19,7 @@ import org.opendaylight.controller.netconf.util.handler.NetconfXMLToMessageDecod
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.util.Collection;
+import io.netty.channel.Channel;
 
 public final class NetconfClientSession extends AbstractNetconfSession<NetconfClientSession, NetconfClientSessionListener> {
 
@@ -36,7 +37,6 @@ public final class NetconfClientSession extends AbstractNetconfSession<NetconfCl
         return capabilities;
     }
 
-
     @Override
     protected NetconfClientSession thisInstance() {
         return this;
index f8f73fc8e5296b9534e0759bc43f24bbf493521e..0c5935b57162b44354a0db08a734886d18bd386a 100644 (file)
@@ -8,13 +8,14 @@
 
 package org.opendaylight.controller.netconf.client;
 
-import io.netty.channel.Channel;
 import io.netty.channel.ChannelFuture;
 import io.netty.channel.ChannelFutureListener;
 import io.netty.channel.ChannelHandlerContext;
 import io.netty.channel.ChannelInboundHandlerAdapter;
-import io.netty.util.Timer;
-import io.netty.util.concurrent.Promise;
+import java.util.Collection;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpression;
+
 import org.opendaylight.controller.netconf.api.NetconfClientSessionPreferences;
 import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
 import org.opendaylight.controller.netconf.api.NetconfMessage;
@@ -22,6 +23,7 @@ import org.opendaylight.controller.netconf.util.AbstractChannelInitializer;
 import org.opendaylight.controller.netconf.util.AbstractNetconfSessionNegotiator;
 import org.opendaylight.controller.netconf.util.messages.NetconfHelloMessage;
 import org.opendaylight.controller.netconf.util.messages.NetconfMessageUtil;
+import org.opendaylight.controller.netconf.util.messages.NetconfStartExiMessage;
 import org.opendaylight.controller.netconf.util.xml.XMLNetconfUtil;
 import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
 import org.opendaylight.controller.netconf.util.xml.XmlUtil;
@@ -31,8 +33,9 @@ import org.w3c.dom.Document;
 import org.w3c.dom.Node;
 import org.w3c.dom.NodeList;
 
-import javax.xml.xpath.XPathConstants;
-import javax.xml.xpath.XPathExpression;
+import io.netty.channel.Channel;
+import io.netty.util.Timer;
+import io.netty.util.concurrent.Promise;
 
 public class NetconfClientSessionNegotiator extends
         AbstractNetconfSessionNegotiator<NetconfClientSessionPreferences, NetconfClientSession, NetconfClientSessionListener>
@@ -55,19 +58,45 @@ public class NetconfClientSessionNegotiator extends
 
     @Override
     protected void handleMessage(NetconfHelloMessage netconfMessage) throws NetconfDocumentedException {
-        NetconfClientSession session = super.getSessionForHelloMessage(netconfMessage);
-
-        if (shouldUseExi(netconfMessage.getDocument())){
-            logger.debug("Netconf session: {} should use exi.", session);
-            tryToStartExi(session);
+        final NetconfClientSession session = getSessionForHelloMessage(netconfMessage);
+        replaceHelloMessageInboundHandler(session);
+
+        // If exi should be used, try to initiate exi communication
+        // Call negotiationSuccessFul after exi negotiation is finished successfully or not
+        if (shouldUseExi(netconfMessage)) {
+            logger.debug("Netconf session {} should use exi.", session);
+            NetconfStartExiMessage startExiMessage = (NetconfStartExiMessage) sessionPreferences.getStartExiMessage();
+            tryToInitiateExi(session, startExiMessage);
+        // Exi is not supported, release session immediately
         } else {
             logger.debug("Netconf session {} isn't capable using exi.", session);
             negotiationSuccessful(session);
         }
     }
 
-    private boolean shouldUseExi(Document doc) {
-        return containsExi10Capability(doc)
+    /**
+     * Initiates exi communication by sending start-exi message and waiting for positive/negative response.
+     *
+     * @param startExiMessage
+     */
+    void tryToInitiateExi(final NetconfClientSession session, final NetconfStartExiMessage startExiMessage) {
+        session.sendMessage(startExiMessage).addListener(new ChannelFutureListener() {
+            @Override
+            public void operationComplete(final ChannelFuture f) {
+                if (!f.isSuccess()) {
+                    logger.warn("Failed to send start-exi message {} on session {}", startExiMessage, this, f.cause());
+                } else {
+                    logger.trace("Start-exi message {} sent to socket on session {}", startExiMessage, this);
+                    channel.pipeline().addAfter(
+                            AbstractChannelInitializer.NETCONF_MESSAGE_DECODER, ExiConfirmationInboundHandler.EXI_CONFIRMED_HANDLER,
+                            new ExiConfirmationInboundHandler(session, startExiMessage));
+                }
+            }
+        });
+    }
+
+    private boolean shouldUseExi(NetconfHelloMessage helloMsg) {
+        return containsExi10Capability(helloMsg.getDocument())
                 && containsExi10Capability(sessionPreferences.getHelloMessage().getDocument());
     }
 
@@ -81,23 +110,6 @@ public class NetconfClientSessionNegotiator extends
         return false;
     }
 
-    private void tryToStartExi(final NetconfClientSession session) {
-        final NetconfMessage startExi = sessionPreferences.getStartExiMessage();
-        session.sendMessage(startExi).addListener(new ChannelFutureListener() {
-            @Override
-            public void operationComplete(final ChannelFuture f) {
-                if (!f.isSuccess()) {
-                    logger.warn("Failed to send start-exi message {} on session {}", startExi, session, f.cause());
-                } else {
-                    logger.trace("Start-exi message {} sent to socket on session {}", startExi, session);
-                    NetconfClientSessionNegotiator.this.channel.pipeline().addAfter(
-                            AbstractChannelInitializer.NETCONF_MESSAGE_DECODER, ExiConfirmationInboundHandler.EXI_CONFIRMED_HANDLER,
-                            new ExiConfirmationInboundHandler(session));
-                }
-            }
-        });
-    }
-
     private long extractSessionId(Document doc) {
         final Node sessionIdNode = (Node) XmlUtil.evaluateXPath(sessionIdXPath, doc, XPathConstants.NODE);
         String textContent = sessionIdNode.getTextContent();
@@ -109,9 +121,11 @@ public class NetconfClientSessionNegotiator extends
     }
 
     @Override
-    protected NetconfClientSession getSession(NetconfClientSessionListener sessionListener, Channel channel, NetconfHelloMessage message) throws NetconfDocumentedException {
-        return new NetconfClientSession(sessionListener, channel, extractSessionId(message.getDocument()),
-                NetconfMessageUtil.extractCapabilitiesFromHello(message.getDocument()));
+    protected NetconfClientSession getSession(NetconfClientSessionListener sessionListener, Channel channel,
+            NetconfHelloMessage message) throws NetconfDocumentedException {
+        long sessionId = extractSessionId(message.getDocument());
+        Collection<String> capabilities = NetconfMessageUtil.extractCapabilitiesFromHello(message.getDocument());
+        return new NetconfClientSession(sessionListener, channel, sessionId, capabilities);
     }
 
     /**
@@ -121,9 +135,11 @@ public class NetconfClientSessionNegotiator extends
         private static final String EXI_CONFIRMED_HANDLER = "exiConfirmedHandler";
 
         private final NetconfClientSession session;
+        private NetconfStartExiMessage startExiMessage;
 
-        ExiConfirmationInboundHandler(NetconfClientSession session) {
+        ExiConfirmationInboundHandler(NetconfClientSession session, final NetconfStartExiMessage startExiMessage) {
             this.session = session;
+            this.startExiMessage = startExiMessage;
         }
 
         @Override
@@ -136,19 +152,19 @@ public class NetconfClientSessionNegotiator extends
             if (NetconfMessageUtil.isOKMessage(netconfMessage)) {
                 logger.trace("Positive response on start-exi call received on session {}", session);
                 try {
-                    session.startExiCommunication(sessionPreferences.getStartExiMessage());
+                    session.startExiCommunication(startExiMessage);
                 } catch (RuntimeException e) {
                     // Unable to add exi, continue without exi
                     logger.warn("Unable to start exi communication, Communication will continue without exi on session {}", session, e);
                 }
 
-            // Error response
+                // Error response
             } else if(NetconfMessageUtil.isErrorMessage(netconfMessage)) {
                 logger.warn(
                         "Error response to start-exi message {}, Communication will continue without exi on session {}",
                         XmlUtil.toString(netconfMessage.getDocument()), session);
 
-            // Unexpected response to start-exi, throwing message away, continue without exi
+                // Unexpected response to start-exi, throwing message away, continue without exi
             } else {
                 logger.warn(
                         "Unexpected response to start-exi message, should be ok, was {}, " +
@@ -159,4 +175,5 @@ public class NetconfClientSessionNegotiator extends
             negotiationSuccessful(session);
         }
     }
+
 }
index 7af1f01a08ea89c0712b1fb69e80f550832b97cf..e65adc3fdf5b736522592a55e5c81d8fa8f37451 100644 (file)
@@ -74,7 +74,7 @@ public class NetconfClientSessionNegotiatorFactory implements SessionNegotiatorF
             throw new IllegalStateException(e);
         }
 
-        NetconfClientSessionPreferences proposal = new NetconfClientSessionPreferences(helloMessage,startExiMessage);
+        NetconfClientSessionPreferences proposal = new NetconfClientSessionPreferences(helloMessage, startExiMessage);
         return new NetconfClientSessionNegotiator(proposal, promise, channel, timer,
                 sessionListenerFactory.getSessionListener(),connectionTimeoutMillis);
     }
index 5c389fa966af340ef277f5d51ebe992b2c6081ff..6528fe251775694e8ea47cf59b094d0efcc07ddf 100644 (file)
@@ -8,10 +8,9 @@
 
 package org.opendaylight.controller.netconf.impl;
 
-import com.google.common.base.Optional;
-import io.netty.channel.Channel;
-import io.netty.util.Timer;
-import io.netty.util.concurrent.Promise;
+import java.net.InetSocketAddress;
+
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
 import org.opendaylight.controller.netconf.api.NetconfServerSessionPreferences;
 import org.opendaylight.controller.netconf.util.AbstractNetconfSessionNegotiator;
 import org.opendaylight.controller.netconf.util.messages.NetconfHelloMessage;
@@ -19,7 +18,11 @@ import org.opendaylight.controller.netconf.util.messages.NetconfHelloMessageAddi
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.net.InetSocketAddress;
+import com.google.common.base.Optional;
+
+import io.netty.channel.Channel;
+import io.netty.util.Timer;
+import io.netty.util.concurrent.Promise;
 
 public class NetconfServerSessionNegotiator extends
         AbstractNetconfSessionNegotiator<NetconfServerSessionPreferences, NetconfServerSession, NetconfServerSessionListener> {
@@ -32,6 +35,14 @@ public class NetconfServerSessionNegotiator extends
         super(sessionPreferences, promise, channel, timer, sessionListener, connectionTimeoutMillis);
     }
 
+    @Override
+    protected void handleMessage(NetconfHelloMessage netconfMessage) throws NetconfDocumentedException {
+        NetconfServerSession session = getSessionForHelloMessage(netconfMessage);
+        replaceHelloMessageInboundHandler(session);
+        // Negotiation successful after all non hello messages were processed
+        negotiationSuccessful(session);
+    }
+
     @Override
     protected NetconfServerSession getSession(NetconfServerSessionListener sessionListener, Channel channel, NetconfHelloMessage message) {
         Optional<NetconfHelloMessageAdditionalHeader> additionalHeader = message.getAdditionalHeader();
index c5f8e99f2271baaa84b9a4c58657a69f50c92369..d5a34d11b244681b1540ff95bae22b26ba607670 100644 (file)
@@ -10,6 +10,7 @@ package org.opendaylight.controller.netconf.impl;
 
 import static org.opendaylight.controller.netconf.mapping.api.NetconfOperationProvider.NetconfOperationProviderUtil.getNetconfSessionIdForReporting;
 
+import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableSet;
 import java.util.Set;
 
@@ -35,12 +36,10 @@ import org.slf4j.LoggerFactory;
 
 public class NetconfServerSessionNegotiatorFactory implements SessionNegotiatorFactory<NetconfHelloMessage, NetconfServerSession, NetconfServerSessionListener> {
 
-    private static final Set<String> DEFAULT_BASE_CAPABILITIES = ImmutableSet.of(
-            XmlNetconfConstants.URN_IETF_PARAMS_NETCONF_BASE_1_0
-            // FIXME, Chunk framing causes ConcurrentClientsTest to fail, investigate
-//            XmlNetconfConstants.URN_IETF_PARAMS_NETCONF_BASE_1_1,
-            // FIXME, EXI causing issues with sal-netconf-connector, investigate
-//            XmlNetconfConstants.URN_IETF_PARAMS_NETCONF_CAPABILITY_EXI_1_0
+    public static final Set<String> DEFAULT_BASE_CAPABILITIES = ImmutableSet.of(
+            XmlNetconfConstants.URN_IETF_PARAMS_NETCONF_BASE_1_0,
+            XmlNetconfConstants.URN_IETF_PARAMS_NETCONF_BASE_1_1,
+            XmlNetconfConstants.URN_IETF_PARAMS_NETCONF_CAPABILITY_EXI_1_0
     );
 
     private final Timer timer;
@@ -51,18 +50,42 @@ public class NetconfServerSessionNegotiatorFactory implements SessionNegotiatorF
     private final DefaultCommitNotificationProducer commitNotificationProducer;
     private final SessionMonitoringService monitoringService;
     private static final Logger logger = LoggerFactory.getLogger(NetconfServerSessionNegotiatorFactory.class);
+    private final Set<String> baseCapabilities;
 
     // TODO too many params, refactor
     public NetconfServerSessionNegotiatorFactory(Timer timer, NetconfOperationProvider netconfOperationProvider,
                                                  SessionIdProvider idProvider, long connectionTimeoutMillis,
                                                  DefaultCommitNotificationProducer commitNot,
                                                  SessionMonitoringService monitoringService) {
+        this(timer, netconfOperationProvider, idProvider, connectionTimeoutMillis, commitNot, monitoringService, DEFAULT_BASE_CAPABILITIES);
+    }
+
+    // TODO too many params, refactor
+    public NetconfServerSessionNegotiatorFactory(Timer timer, NetconfOperationProvider netconfOperationProvider,
+                                                 SessionIdProvider idProvider, long connectionTimeoutMillis,
+                                                 DefaultCommitNotificationProducer commitNot,
+                                                 SessionMonitoringService monitoringService, Set<String> baseCapabilities) {
         this.timer = timer;
         this.netconfOperationProvider = netconfOperationProvider;
         this.idProvider = idProvider;
         this.connectionTimeoutMillis = connectionTimeoutMillis;
         this.commitNotificationProducer = commitNot;
         this.monitoringService = monitoringService;
+        this.baseCapabilities = validateBaseCapabilities(baseCapabilities);
+    }
+
+    private ImmutableSet<String> validateBaseCapabilities(final Set<String> baseCapabilities) {
+        // Check base capabilities to be supported by the server
+        Sets.SetView<String> unknownBaseCaps = Sets.difference(baseCapabilities, DEFAULT_BASE_CAPABILITIES);
+        Preconditions.checkArgument(unknownBaseCaps.isEmpty(),
+                "Base capabilities that will be supported by netconf server have to be subset of %s, unknown base capabilities: %s",
+                DEFAULT_BASE_CAPABILITIES, unknownBaseCaps);
+
+        ImmutableSet.Builder<String> b = ImmutableSet.builder();
+        b.addAll(baseCapabilities);
+        // Base 1.0 capability is supported by default
+        b.add(XmlNetconfConstants.URN_IETF_PARAMS_NETCONF_BASE_1_0);
+        return b.build();
     }
 
     /**
@@ -100,7 +123,7 @@ public class NetconfServerSessionNegotiatorFactory implements SessionNegotiatorF
     }
 
     private NetconfHelloMessage createHelloMessage(long sessionId, CapabilityProvider capabilityProvider) throws NetconfDocumentedException {
-        return NetconfHelloMessage.createServerHello(Sets.union(capabilityProvider.getCapabilities(), DEFAULT_BASE_CAPABILITIES), sessionId);
+        return NetconfHelloMessage.createServerHello(Sets.union(capabilityProvider.getCapabilities(), baseCapabilities), sessionId);
     }
 
 }
index 02889b62a5bf71fe6cdb1161c7ac3fdaa0c00eaf..0a0cd2208891982efce68832bb66d46e29fe9cb3 100644 (file)
@@ -9,26 +9,42 @@
 package org.opendaylight.controller.netconf.impl;
 
 import static com.google.common.base.Preconditions.checkNotNull;
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.fail;
 import static org.mockito.Matchers.any;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.mock;
 
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Lists;
+import io.netty.channel.EventLoopGroup;
+import io.netty.util.HashedWheelTimer;
 import java.io.DataOutputStream;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.lang.management.ManagementFactory;
 import java.net.InetSocketAddress;
 import java.net.Socket;
-import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 import java.util.Set;
 
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.atomic.AtomicLong;
 import org.apache.commons.io.IOUtils;
 import org.junit.After;
+import org.junit.AfterClass;
 import org.junit.Before;
+import org.junit.BeforeClass;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
 import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
 import org.opendaylight.controller.netconf.api.NetconfMessage;
 import org.opendaylight.controller.netconf.client.NetconfClientDispatcher;
@@ -42,37 +58,61 @@ import org.opendaylight.controller.netconf.mapping.api.NetconfOperationChainedEx
 import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService;
 import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory;
 import org.opendaylight.controller.netconf.util.messages.NetconfHelloMessageAdditionalHeader;
+import org.opendaylight.controller.netconf.util.messages.NetconfMessageUtil;
 import org.opendaylight.controller.netconf.util.messages.NetconfStartExiMessage;
 import org.opendaylight.controller.netconf.util.test.XmlFileLoader;
+import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
 import org.opendaylight.controller.netconf.util.xml.XmlUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.w3c.dom.Document;
 
-import com.google.common.base.Optional;
 import com.google.common.collect.Sets;
 
 import io.netty.channel.ChannelFuture;
-import io.netty.channel.EventLoopGroup;
 import io.netty.channel.nio.NioEventLoopGroup;
-import io.netty.util.HashedWheelTimer;
 
+@RunWith(Parameterized.class)
 public class ConcurrentClientsTest {
+    private static final Logger logger = LoggerFactory.getLogger(ConcurrentClientsTest.class);
 
-    private static final int CONCURRENCY = 16;
-    private EventLoopGroup nettyGroup;
-    private NetconfClientDispatcher netconfClientDispatcher;
+    private static ExecutorService clientExecutor;
 
-    private final InetSocketAddress netconfAddress = new InetSocketAddress("127.0.0.1", 8303);
+    private static final int CONCURRENCY = 32;
+    private static final InetSocketAddress netconfAddress = new InetSocketAddress("127.0.0.1", 8303);
 
-    static final Logger logger = LoggerFactory.getLogger(ConcurrentClientsTest.class);
+    private int nettyThreads;
+    private Class<? extends Runnable> clientRunnable;
+    private Set<String> serverCaps;
 
-    private DefaultCommitNotificationProducer commitNot;
-    private NetconfServerDispatcher dispatch;
+    public ConcurrentClientsTest(int nettyThreads, Class<? extends Runnable> clientRunnable, Set<String> serverCaps) {
+        this.nettyThreads = nettyThreads;
+        this.clientRunnable = clientRunnable;
+        this.serverCaps = serverCaps;
+    }
+
+    @Parameterized.Parameters()
+    public static Collection<Object[]> data() {
+        return Arrays.asList(new Object[][]{
+                {4, TestingNetconfClientRunnable.class, NetconfServerSessionNegotiatorFactory.DEFAULT_BASE_CAPABILITIES},
+                {1, TestingNetconfClientRunnable.class, NetconfServerSessionNegotiatorFactory.DEFAULT_BASE_CAPABILITIES},
+                // empty set of capabilities = only base 1.0 netconf capability
+                {4, TestingNetconfClientRunnable.class, Collections.emptySet()},
+                {4, TestingNetconfClientRunnable.class, getOnlyExiServerCaps()},
+                {4, TestingNetconfClientRunnable.class, getOnlyChunkServerCaps()},
+
+                {4, BlockingClientRunnable.class, getOnlyExiServerCaps()},
+                {1, BlockingClientRunnable.class, getOnlyExiServerCaps()},
+        });
+    }
 
+    private EventLoopGroup nettyGroup;
+    private NetconfClientDispatcher netconfClientDispatcher;
 
+    private DefaultCommitNotificationProducer commitNot;
 
     HashedWheelTimer hashedWheelTimer;
+    private TestingNetconfOperation testingNetconfOperation;
 
     public static SessionMonitoringService createMockedMonitoringService() {
         SessionMonitoringService monitoring = mock(SessionMonitoringService.class);
@@ -81,26 +121,43 @@ public class ConcurrentClientsTest {
         return monitoring;
     }
 
+    @BeforeClass
+    public static void setUpClientExecutor() {
+        clientExecutor = Executors.newFixedThreadPool(CONCURRENCY, new ThreadFactory() {
+            int i = 1;
+
+            @Override
+            public Thread newThread(final Runnable r) {
+                Thread thread = new Thread(r);
+                thread.setName("client-" + i++);
+                thread.setDaemon(true);
+                return thread;
+            }
+        });
+    }
+
     @Before
     public void setUp() throws Exception {
 
-        nettyGroup = new NioEventLoopGroup();
+        nettyGroup = new NioEventLoopGroup(nettyThreads);
         NetconfHelloMessageAdditionalHeader additionalHeader = new NetconfHelloMessageAdditionalHeader("uname", "10.10.10.1", "830", "tcp", "client");
         netconfClientDispatcher = new NetconfClientDispatcher( nettyGroup, nettyGroup, additionalHeader, 5000);
 
         NetconfOperationServiceFactoryListenerImpl factoriesListener = new NetconfOperationServiceFactoryListenerImpl();
-        factoriesListener.onAddNetconfOperationServiceFactory(mockOpF());
+
+        testingNetconfOperation = new TestingNetconfOperation();
+        factoriesListener.onAddNetconfOperationServiceFactory(new TestingOperationServiceFactory(testingNetconfOperation));
 
         SessionIdProvider idProvider = new SessionIdProvider();
         hashedWheelTimer = new HashedWheelTimer();
 
         NetconfServerSessionNegotiatorFactory serverNegotiatorFactory = new NetconfServerSessionNegotiatorFactory(
-                hashedWheelTimer, factoriesListener, idProvider, 5000, commitNot, createMockedMonitoringService());
+                hashedWheelTimer, factoriesListener, idProvider, 5000, commitNot, createMockedMonitoringService(), serverCaps);
 
         commitNot = new DefaultCommitNotificationProducer(ManagementFactory.getPlatformMBeanServer());
 
         NetconfServerDispatcher.ServerChannelInitializer serverChannelInitializer = new NetconfServerDispatcher.ServerChannelInitializer(serverNegotiatorFactory);
-        dispatch = new NetconfServerDispatcher(serverChannelInitializer, nettyGroup, nettyGroup);
+        final NetconfServerDispatcher dispatch = new NetconfServerDispatcher(serverChannelInitializer, nettyGroup, nettyGroup);
 
         ChannelFuture s = dispatch.createServer(netconfAddress);
         s.await();
@@ -108,113 +165,131 @@ public class ConcurrentClientsTest {
 
     @After
     public void tearDown(){
+        commitNot.close();
         hashedWheelTimer.stop();
-        nettyGroup.shutdownGracefully();
+        try {
+            nettyGroup.shutdownGracefully().get();
+        } catch (InterruptedException | ExecutionException e) {
+            logger.warn("Ignoring exception while cleaning up after test", e);
+        }
     }
 
-    private NetconfOperationServiceFactory mockOpF() {
-        return new NetconfOperationServiceFactory() {
-            @Override
-            public NetconfOperationService createService(String netconfSessionIdForReporting) {
-                return new NetconfOperationService() {
-                    @Override
-                    public Set<Capability> getCapabilities() {
-                        return Collections.emptySet();
-                    }
-
-                    @Override
-                    public Set<NetconfOperation> getNetconfOperations() {
-                        return Sets.<NetconfOperation> newHashSet(new NetconfOperation() {
-                            @Override
-                            public HandlingPriority canHandle(Document message) {
-                                return XmlUtil.toString(message).contains(NetconfStartExiMessage.START_EXI) ?
-                                        HandlingPriority.CANNOT_HANDLE :
-                                        HandlingPriority.HANDLE_WITH_MAX_PRIORITY;
-                            }
-
-                            @Override
-                            public Document handle(Document requestMessage, NetconfOperationChainedExecution subsequentOperation) throws NetconfDocumentedException {
-                                try {
-                                    return XmlUtil.readXmlToDocument("<test/>");
-                                } catch (Exception e) {
-                                    throw new RuntimeException(e);
-                                }
-                            }
-                        });
-                    }
-
-                    @Override
-                    public void close() {
-                    }
-                };
-            }
-        };
+    @AfterClass
+    public static void tearDownClientExecutor() {
+        clientExecutor.shutdownNow();
     }
 
-    @After
-    public void cleanUp() throws Exception {
-        commitNot.close();
-    }
+    @Test(timeout = CONCURRENCY * 1000)
+    public void testConcurrentClients() throws Exception {
 
-    @Test(timeout = 30 * 1000)
-    public void multipleClients() throws Exception {
-        List<TestingThread> threads = new ArrayList<>();
+        List<Future<?>> futures = Lists.newArrayListWithCapacity(CONCURRENCY);
 
-        final int attempts = 5;
         for (int i = 0; i < CONCURRENCY; i++) {
-            TestingThread thread = new TestingThread(String.valueOf(i), attempts);
-            threads.add(thread);
-            thread.start();
+            futures.add(clientExecutor.submit(getInstanceOfClientRunnable()));
         }
 
-        for (TestingThread thread : threads) {
-            thread.join();
-            if(thread.thrownException.isPresent()) {
-                Exception exception = thread.thrownException.get();
-                logger.error("Thread for testing client failed", exception);
-                fail("Client thread " + thread + " failed: " + exception.getMessage());
+        for (Future<?> future : futures) {
+            try {
+                future.get();
+            } catch (InterruptedException e) {
+                throw new IllegalStateException(e);
+            } catch (ExecutionException e) {
+                logger.error("Thread for testing client failed", e);
+                fail("Client failed: " + e.getMessage());
             }
         }
+
+        assertEquals(CONCURRENCY, testingNetconfOperation.getMessageCount());
     }
 
-    @Test(timeout = 30 * 1000)
-    public void synchronizationTest() throws Exception {
-        new BlockingThread("foo").run2();
+    public static Set<String> getOnlyExiServerCaps() {
+        return Sets.newHashSet(
+                XmlNetconfConstants.URN_IETF_PARAMS_NETCONF_BASE_1_0,
+                XmlNetconfConstants.URN_IETF_PARAMS_NETCONF_CAPABILITY_EXI_1_0
+        );
     }
 
-    @Test(timeout = 30 * 1000)
-    public void multipleBlockingClients() throws Exception {
-        List<BlockingThread> threads = new ArrayList<>();
-        for (int i = 0; i < CONCURRENCY; i++) {
-            BlockingThread thread = new BlockingThread(String.valueOf(i));
-            threads.add(thread);
-            thread.start();
+    public static Set<String> getOnlyChunkServerCaps() {
+        return Sets.newHashSet(
+                XmlNetconfConstants.URN_IETF_PARAMS_NETCONF_BASE_1_0,
+                XmlNetconfConstants.URN_IETF_PARAMS_NETCONF_BASE_1_1
+        );
+    }
+
+    public Runnable getInstanceOfClientRunnable() throws Exception {
+        return clientRunnable.getConstructor(ConcurrentClientsTest.class).newInstance(this);
+    }
+
+    /**
+     * Responds to all operations except start-exi and counts all requests
+     */
+    private static class TestingNetconfOperation implements NetconfOperation {
+
+        private final AtomicLong counter = new AtomicLong();
+
+        @Override
+        public HandlingPriority canHandle(Document message) {
+            return XmlUtil.toString(message).contains(NetconfStartExiMessage.START_EXI) ?
+                    HandlingPriority.CANNOT_HANDLE :
+                    HandlingPriority.HANDLE_WITH_MAX_PRIORITY;
         }
 
-        for (BlockingThread thread : threads) {
-            thread.join();
-            if(thread.thrownException.isPresent()) {
-                Exception exception = thread.thrownException.get();
-                logger.error("Thread for testing client failed", exception);
-                fail("Client thread " + thread + " failed: " + exception.getMessage());
+        @Override
+        public Document handle(Document requestMessage, NetconfOperationChainedExecution subsequentOperation) throws NetconfDocumentedException {
+            try {
+                logger.info("Handling netconf message from test {}", XmlUtil.toString(requestMessage));
+                counter.getAndIncrement();
+                return XmlUtil.readXmlToDocument("<test/>");
+            } catch (Exception e) {
+                throw new RuntimeException(e);
             }
         }
+
+        public long getMessageCount() {
+            return counter.get();
+        }
     }
 
-    class BlockingThread extends Thread {
-        private Optional<Exception> thrownException;
+    /**
+     * Hardcoded operation service factory
+     */
+    private static class TestingOperationServiceFactory implements NetconfOperationServiceFactory {
+        private final NetconfOperation[] operations;
+
+        public TestingOperationServiceFactory(final NetconfOperation... operations) {
+            this.operations = operations;
+        }
 
-        public BlockingThread(String name) {
-            super("client-" + name);
+        @Override
+        public NetconfOperationService createService(String netconfSessionIdForReporting) {
+            return new NetconfOperationService() {
+                @Override
+                public Set<Capability> getCapabilities() {
+                    return Collections.emptySet();
+                }
+
+                @Override
+                public Set<NetconfOperation> getNetconfOperations() {
+                    return Sets.newHashSet(operations);
+                }
+
+                @Override
+                public void close() {}
+            };
         }
+    }
+
+    /**
+     * Pure socket based blocking client
+     */
+    public final class BlockingClientRunnable implements Runnable {
 
         @Override
         public void run() {
             try {
                 run2();
-                thrownException = Optional.absent();
             } catch (Exception e) {
-                thrownException = Optional.of(e);
+                throw new IllegalStateException(Thread.currentThread().getName(), e);
             }
         }
 
@@ -250,34 +325,32 @@ public class ConcurrentClientsTest {
         }
     }
 
-    class TestingThread extends Thread {
-
-        private final String clientId;
-        private final int attempts;
-        private Optional<Exception> thrownException;
-
-        TestingThread(String clientId, int attempts) {
-            this.clientId = clientId;
-            this.attempts = attempts;
-            setName("client-" + clientId);
-        }
+    /**
+     * TestingNetconfClient based runnable
+     */
+    public final class TestingNetconfClientRunnable implements Runnable {
 
         @Override
         public void run() {
             try {
-                final TestingNetconfClient netconfClient = new TestingNetconfClient(clientId, netconfAddress, netconfClientDispatcher);
+                final TestingNetconfClient netconfClient = new TestingNetconfClient(Thread.currentThread().getName(),
+                        netconfAddress, netconfClientDispatcher);
                 long sessionId = netconfClient.getSessionId();
-                logger.info("Client with sessionid {} hello exchanged", sessionId);
+                logger.info("Client with session id {}: hello exchanged", sessionId);
 
                 final NetconfMessage getMessage = XmlFileLoader
                         .xmlFileToNetconfMessage("netconfMessages/getConfig.xml");
                 NetconfMessage result = netconfClient.sendRequest(getMessage).get();
-                logger.info("Client with sessionid {} got result {}", sessionId, result);
+                logger.info("Client with session id {}: got result {}", sessionId, result);
+
+                Preconditions.checkState(NetconfMessageUtil.isErrorMessage(result) == false,
+                        "Received error response: " + XmlUtil.toString(result.getDocument()) + " to request: "
+                                + XmlUtil.toString(getMessage.getDocument()));
+
                 netconfClient.close();
-                logger.info("Client with session id {} ended", sessionId);
-                thrownException = Optional.absent();
+                logger.info("Client with session id {}: ended", sessionId);
             } catch (final Exception e) {
-                thrownException = Optional.of(e);
+                throw new IllegalStateException(Thread.currentThread().getName(), e);
             }
         }
     }
index b330f9bcd4f05b915e76caf878dc0fa25e6489e2..0c03dda45bee20342466c35adb15f6653641d2bf 100644 (file)
   </properties>
 
   <dependencies>
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>sal-binding-it</artifactId>
+      <exclusions>
+        <!-- FIXME see IdentityRefNetconfTest  -->
+        <!-- Pax-url-aether contains guava classes e.g. ImmutableSet that clashes with guava and causes tests to fail-->
+        <exclusion>
+          <groupId>org.ops4j.pax.url</groupId>
+          <artifactId>pax-url-aether</artifactId>
+        </exclusion>
+      </exclusions>
+    </dependency>
     <dependency>
       <groupId>org.opendaylight.controller</groupId>
       <artifactId>commons.logback_settings</artifactId>
       <artifactId>netty-config-api</artifactId>
       <scope>test</scope>
     </dependency>
-    <dependency>
-      <groupId>${project.groupId}</groupId>
-      <artifactId>sal-binding-it</artifactId>
-    </dependency>
     <dependency>
       <groupId>org.opendaylight.controller</groupId>
       <artifactId>yang-test</artifactId>
index 96a9effcfc7cdfc4ccf9b28995b26a9eea5ef148..c54285bc908c817157287022d5dcc92011b0d145 100644 (file)
@@ -1,10 +1,10 @@
 /*
- * 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
- */
+* 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.netconf.it.pax;
 
 import static org.junit.Assert.fail;
@@ -26,7 +26,6 @@ import java.net.InetSocketAddress;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeoutException;
 
-import javax.inject.Inject;
 import javax.xml.parsers.ParserConfigurationException;
 
 import org.junit.Assert;
@@ -59,7 +58,11 @@ public class IdentityRefNetconfTest {
     public static final int CLIENT_CONNECTION_TIMEOUT_MILLIS = 15000;
 
     // Wait for controller to start
-    @Inject
+
+    // FIXME move this (pax) test to different module
+    // pax jars contain guava classes that clash with real guava dependencies in non-pax tests
+    //
+    //@Inject
     @Filter(timeout = 60 * 1000)
     BindingAwareBroker broker;
 
index aa1afc025d736f154822e88dee11c89766d9bd63..c789206436f1eecb1fd92fc1480a027eb8943d09 100644 (file)
@@ -93,23 +93,19 @@ public abstract class AbstractNetconfSession<S extends NetconfSession, L extends
         return sb.toString();
     }
 
-    protected <T extends ChannelHandler> T removeHandler(final Class<T> handlerType) {
-        return this.channel.pipeline().remove(handlerType);
-    }
-
-    protected void replaceMessageDecoder(final ChannelHandler handler) {
+    protected final void replaceMessageDecoder(final ChannelHandler handler) {
         replaceChannelHandler(AbstractChannelInitializer.NETCONF_MESSAGE_DECODER, handler);
     }
 
-    protected void replaceMessageEncoder(final ChannelHandler handler) {
+    protected final void replaceMessageEncoder(final ChannelHandler handler) {
         replaceChannelHandler(AbstractChannelInitializer.NETCONF_MESSAGE_ENCODER, handler);
     }
 
-    protected void replaceMessageEncoderAfterNextMessage(final ChannelHandler handler) {
+    protected final void replaceMessageEncoderAfterNextMessage(final ChannelHandler handler) {
         this.delayedEncoder = handler;
     }
 
-    protected void replaceChannelHandler(final String handlerName, final ChannelHandler handler) {
+    protected final void replaceChannelHandler(final String handlerName, final ChannelHandler handler) {
         channel.pipeline().replace(handlerName, handlerName, handler);
     }
 
@@ -124,7 +120,7 @@ public abstract class AbstractNetconfSession<S extends NetconfSession, L extends
         }
         final NetconfEXICodec exiCodec = new NetconfEXICodec(exiParams.getOptions());
         addExiHandlers(exiCodec);
-        logger.debug("EXI handlers added to pipeline on session {}", this);
+        logger.debug("Session {} EXI handlers added to pipeline", this);
     }
 
     protected abstract void addExiHandlers(NetconfEXICodec exiCodec);
index 5521e28818b20fcb9291089ced8867cce787e2e6..b0c8c6dc19e6b3b6c97f90b21fe0e1dc680e0ba3 100644 (file)
@@ -28,6 +28,7 @@ import org.opendaylight.controller.netconf.api.NetconfSessionPreferences;
 import org.opendaylight.controller.netconf.util.handler.FramingMechanismHandlerFactory;
 import org.opendaylight.controller.netconf.util.handler.NetconfChunkAggregator;
 import org.opendaylight.controller.netconf.util.handler.NetconfMessageToXMLEncoder;
+import org.opendaylight.controller.netconf.util.handler.NetconfXMLToHelloMessageDecoder;
 import org.opendaylight.controller.netconf.util.handler.NetconfXMLToMessageDecoder;
 import org.opendaylight.controller.netconf.util.messages.FramingMechanism;
 import org.opendaylight.controller.netconf.util.messages.NetconfHelloMessage;
@@ -74,7 +75,7 @@ extends AbstractSessionNegotiator<NetconfHelloMessage, S> {
     }
 
     @Override
-    protected void startNegotiation() {
+    protected final void startNegotiation() {
         final Optional<SslHandler> sslHandler = getSslHandler(channel);
         if (sslHandler.isPresent()) {
             Future<Channel> future = sslHandler.get().handshakeFuture();
@@ -125,27 +126,22 @@ extends AbstractSessionNegotiator<NetconfHelloMessage, S> {
 
         // FIXME, make sessionPreferences return HelloMessage, move NetconfHelloMessage to API
         sendMessage((NetconfHelloMessage)helloMessage);
+
+        replaceHelloMessageOutboundHandler();
         changeState(State.OPEN_WAIT);
     }
+
     private void cancelTimeout() {
         if(timeout!=null) {
             timeout.cancel();
         }
     }
 
-    @Override
-    protected void handleMessage(NetconfHelloMessage netconfMessage) throws NetconfDocumentedException {
-        S session = getSessionForHelloMessage(netconfMessage)   ;
-        negotiationSuccessful(session);
-    }
-
     protected final S getSessionForHelloMessage(NetconfHelloMessage netconfMessage) throws NetconfDocumentedException {
         Preconditions.checkNotNull(netconfMessage, "netconfMessage");
 
         final Document doc = netconfMessage.getDocument();
 
-        replaceHelloMessageHandlers();
-
         if (shouldUseChunkFraming(doc)) {
             insertChunkFramingToPipeline();
         }
@@ -157,23 +153,44 @@ extends AbstractSessionNegotiator<NetconfHelloMessage, S> {
     /**
      * Insert chunk framing handlers into the pipeline
      */
-    protected void insertChunkFramingToPipeline() {
+    private void insertChunkFramingToPipeline() {
         replaceChannelHandler(channel, AbstractChannelInitializer.NETCONF_MESSAGE_FRAME_ENCODER,
                 FramingMechanismHandlerFactory.createHandler(FramingMechanism.CHUNK));
         replaceChannelHandler(channel, AbstractChannelInitializer.NETCONF_MESSAGE_AGGREGATOR,
                 new NetconfChunkAggregator());
     }
 
-    protected boolean shouldUseChunkFraming(Document doc) {
+    private boolean shouldUseChunkFraming(Document doc) {
         return containsBase11Capability(doc)
                 && containsBase11Capability(sessionPreferences.getHelloMessage().getDocument());
     }
 
     /**
-     * Remove special handlers for hello message. Insert regular netconf xml message (en|de)coders.
+     * Remove special inbound handler for hello message. Insert regular netconf xml message (en|de)coders.
+     *
+     * Inbound hello message handler should be kept until negotiation is successful
+     * It caches any non-hello messages while negotiation is still in progress
+     */
+    protected final void replaceHelloMessageInboundHandler(final S session) {
+        ChannelHandler helloMessageHandler = replaceChannelHandler(channel, AbstractChannelInitializer.NETCONF_MESSAGE_DECODER, new NetconfXMLToMessageDecoder());
+
+        Preconditions.checkState(helloMessageHandler instanceof NetconfXMLToHelloMessageDecoder,
+                "Pipeline handlers misplaced on session: %s, pipeline: %s", session, channel.pipeline());
+        Iterable<NetconfMessage> netconfMessagesFromNegotiation =
+                ((NetconfXMLToHelloMessageDecoder) helloMessageHandler).getPostHelloNetconfMessages();
+
+        // Process messages received during negotiation
+        // The hello message handler does not have to be synchronized, since it is always call from the same thread by netty
+        // It means, we are now using the thread now
+        for (NetconfMessage message : netconfMessagesFromNegotiation) {
+            session.handleMessage(message);
+        }
+    }
+
+    /**
+     * Remove special outbound handler for hello message. Insert regular netconf xml message (en|de)coders.
      */
-    protected void replaceHelloMessageHandlers() {
-        replaceChannelHandler(channel, AbstractChannelInitializer.NETCONF_MESSAGE_DECODER, new NetconfXMLToMessageDecoder());
+    private void replaceHelloMessageOutboundHandler() {
         replaceChannelHandler(channel, AbstractChannelInitializer.NETCONF_MESSAGE_ENCODER, new NetconfMessageToXMLEncoder());
     }
 
@@ -183,7 +200,7 @@ extends AbstractSessionNegotiator<NetconfHelloMessage, S> {
 
     protected abstract S getSession(L sessionListener, Channel channel, NetconfHelloMessage message) throws NetconfDocumentedException;
 
-    protected synchronized void changeState(final State newState) {
+    private synchronized void changeState(final State newState) {
         logger.debug("Changing state from : {} to : {}", state, newState);
         Preconditions.checkState(isStateChangePermitted(state, newState), "Cannot change state from %s to %s", state,
                 newState);
index cbfbfe1c05a21ebb5db74b5973ce32fc90f86c98..ae330d67e68344e16d1904b7c1547caea73cadb8 100644 (file)
@@ -34,8 +34,6 @@ public final class NetconfEXIToMessageDecoder extends ByteToMessageDecoder {
 
     private static final Logger LOG = LoggerFactory.getLogger(NetconfEXIToMessageDecoder.class);
 
-//    private static final SAXTransformerFactory saxTransformerFactory = (SAXTransformerFactory)SAXTransformerFactory.newInstance();
-
     private final NetconfEXICodec codec;
 
     public NetconfEXIToMessageDecoder(final NetconfEXICodec codec) {
@@ -50,9 +48,9 @@ public final class NetconfEXIToMessageDecoder extends ByteToMessageDecoder {
          * the use of EXI, which means the next message needs to be decoded not by us, but rather
          * by the XML decoder.
          */
-        // If empty Byte buffer is passed to r.parse, EOFException is thrown
 
-        if (in.readableBytes() == 0) {
+        // If empty Byte buffer is passed to r.parse, EOFException is thrown
+        if (in.isReadable() == false) {
             LOG.debug("No more content in incoming buffer.");
             return;
         }
@@ -69,7 +67,6 @@ public final class NetconfEXIToMessageDecoder extends ByteToMessageDecoder {
         final DOMResult domResult = new DOMResult();
         handler.setResult(domResult);
 
-
         try (final InputStream is = new ByteBufInputStream(in)) {
             r.parse(new InputSource(is));
         }
index b930b65b9742625071f6f0618be9461add72a2c7..361d4fcee908018eac90a54844569ced70c22a21 100644 (file)
@@ -7,6 +7,8 @@
  */
 package org.opendaylight.controller.netconf.util.handler;
 
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Lists;
 import io.netty.buffer.ByteBuf;
 import io.netty.buffer.ByteBufUtil;
 import io.netty.channel.ChannelHandlerContext;
@@ -36,7 +38,12 @@ import org.xml.sax.SAXException;
  * Customized NetconfXMLToMessageDecoder that reads additional header with
  * session metadata from
  * {@link org.opendaylight.controller.netconf.util.messages.NetconfHelloMessage}
- * . Used by netconf server to retrieve information about session metadata.
+ *
+ *
+ * This handler should be replaced in pipeline by regular message handler as last step of negotiation.
+ * It serves as a message barrier and halts all non-hello netconf messages.
+ * Netconf messages after hello should be processed once the negotiation succeeded.
+ *
  */
 public final class NetconfXMLToHelloMessageDecoder extends ByteToMessageDecoder {
     private static final Logger LOG = LoggerFactory.getLogger(NetconfXMLToHelloMessageDecoder.class);
@@ -49,6 +56,12 @@ public final class NetconfXMLToHelloMessageDecoder extends ByteToMessageDecoder
             new byte[] { '\r', '\n', '[' },
             new byte[] { '\n', '[' });
 
+    // State variables do not have to by synchronized
+    // Netty uses always the same (1) thread per pipeline
+    // We use instance of this per pipeline
+    private List<NetconfMessage> nonHelloMessages = Lists.newArrayList();
+    private boolean helloReceived = false;
+
     @Override
     @VisibleForTesting
     public void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws IOException, SAXException, NetconfDocumentedException {
@@ -79,18 +92,39 @@ public final class NetconfXMLToHelloMessageDecoder extends ByteToMessageDecoder
 
             Document doc = XmlUtil.readXmlToDocument(new ByteArrayInputStream(bytes));
 
-            final NetconfMessage message;
-            if (additionalHeader != null) {
-                message = new NetconfHelloMessage(doc, NetconfHelloMessageAdditionalHeader.fromString(additionalHeader));
+            final NetconfMessage message = getNetconfMessage(additionalHeader, doc);
+            if (message instanceof NetconfHelloMessage) {
+                Preconditions.checkState(helloReceived == false,
+                        "Multiple hello messages received, unexpected hello: %s",
+                        XmlUtil.toString(message.getDocument()));
+                out.add(message);
+                helloReceived = true;
+            // Non hello message, suspend the message and insert into cache
             } else {
-                message = new NetconfHelloMessage(doc);
+                Preconditions.checkState(helloReceived, "Hello message not received, instead received: %s",
+                        XmlUtil.toString(message.getDocument()));
+                LOG.debug("Netconf message received during negotiation, caching {}",
+                        XmlUtil.toString(message.getDocument()));
+                nonHelloMessages.add(message);
             }
-            out.add(message);
         } finally {
             in.discardReadBytes();
         }
     }
 
+    private NetconfMessage getNetconfMessage(final String additionalHeader, final Document doc) throws NetconfDocumentedException {
+        NetconfMessage msg = new NetconfMessage(doc);
+        if(NetconfHelloMessage.isHelloMessage(msg)) {
+            if (additionalHeader != null) {
+                return new NetconfHelloMessage(doc, NetconfHelloMessageAdditionalHeader.fromString(additionalHeader));
+            } else {
+                return new NetconfHelloMessage(doc);
+            }
+        }
+
+        return msg;
+    }
+
     private int getAdditionalHeaderEndIndex(byte[] bytes) {
         for (byte[] possibleEnd : POSSIBLE_ENDS) {
             int idx = findByteSequence(bytes, possibleEnd);
@@ -155,4 +189,10 @@ public final class NetconfXMLToHelloMessageDecoder extends ByteToMessageDecoder
         return Charsets.UTF_8.decode(ByteBuffer.wrap(bytes)).toString();
     }
 
+    /**
+     * @return Collection of NetconfMessages that were not hello, but were received during negotiation
+     */
+    public Iterable<NetconfMessage> getPostHelloNetconfMessages() {
+        return nonHelloMessages;
+    }
 }
index 89f76f39e5eafed6526388e9305a129d97bf7517..23f48b31d822ff2b4c10351c844718989ca3b7c2 100644 (file)
@@ -7,13 +7,6 @@
  */
 package org.opendaylight.controller.netconf.util.handler;
 
-import io.netty.buffer.ByteBuf;
-import io.netty.buffer.ByteBufInputStream;
-import io.netty.buffer.ByteBufUtil;
-import io.netty.channel.ChannelHandlerContext;
-import io.netty.handler.codec.ByteToMessageDecoder;
-
-import java.io.IOException;
 import java.util.List;
 
 import org.opendaylight.controller.netconf.api.NetconfMessage;
@@ -22,14 +15,20 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.google.common.annotations.VisibleForTesting;
-import org.xml.sax.SAXException;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.ByteBufInputStream;
+import io.netty.buffer.ByteBufUtil;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.ByteToMessageDecoder;
 
 public final class NetconfXMLToMessageDecoder extends ByteToMessageDecoder {
     private static final Logger LOG = LoggerFactory.getLogger(NetconfXMLToMessageDecoder.class);
 
     @Override
     @VisibleForTesting
-    public void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws IOException, SAXException {
+    public void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
+
         if (in.readableBytes() != 0) {
             LOG.trace("Received to decode: {}", ByteBufUtil.hexDump(in));
             out.add(new NetconfMessage(XmlUtil.readXmlToDocument(new ByteBufInputStream(in))));
index 3fd25e814d697d37e607e1e0ade520db254e83a8..86b2ba1671478a022e30f7dd8c724b07c1d4d703 100644 (file)
@@ -8,17 +8,21 @@
 
 package org.opendaylight.controller.netconf.util.messages;
 
-import com.google.common.base.Optional;
-import com.google.common.collect.Sets;
-import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
 import org.opendaylight.controller.netconf.api.NetconfMessage;
+
+import java.util.Set;
+
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.util.exception.MissingNameSpaceException;
 import org.opendaylight.controller.netconf.util.xml.XmlElement;
 import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
 import org.opendaylight.controller.netconf.util.xml.XmlUtil;
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 
-import java.util.Set;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Sets;
 
 /**
  * NetconfMessage that can carry additional header with session metadata. See {@link org.opendaylight.controller.netconf.util.messages.NetconfHelloMessageAdditionalHeader}
@@ -43,10 +47,10 @@ public final class NetconfHelloMessage extends NetconfMessage {
         return additionalHeader== null ? Optional.<NetconfHelloMessageAdditionalHeader>absent() : Optional.of(additionalHeader);
     }
 
-    private static void checkHelloMessage(Document doc) throws NetconfDocumentedException {
-        XmlElement.fromDomElementWithExpected(doc.getDocumentElement(), HELLO_TAG,
-                XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0);
-
+    private static void checkHelloMessage(Document doc) {
+        Preconditions.checkArgument(isHelloMessage(doc),
+                "Hello message invalid format, should contain %s tag from namespace %s, but is: %s", HELLO_TAG,
+                XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0, XmlUtil.toString(doc));
     }
 
     public static NetconfHelloMessage createClientHello(Iterable<String> capabilities,
@@ -81,4 +85,21 @@ public final class NetconfHelloMessage extends NetconfMessage {
         doc.getDocumentElement().appendChild(sessionIdElement);
         return new NetconfHelloMessage(doc);
     }
+
+    public static boolean isHelloMessage(final NetconfMessage msg) {
+        Document document = msg.getDocument();
+        return isHelloMessage(document);
+    }
+
+    private static boolean isHelloMessage(final Document document) {
+        XmlElement element = XmlElement.fromDomElement(document.getDocumentElement());
+        try {
+            return element.getName().equals(HELLO_TAG) &&
+                   element.hasNamespace() &&
+                   element.getNamespace().equals(XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0);
+        } catch (MissingNameSpaceException e) {
+            // Cannot happen, since we check for hasNamespace
+            throw new IllegalStateException(e);
+        }
+    }
 }
index 5b9707f2b52fff9875cd18e63d91ca257b815a0c..fdcaa2a5b8c98616385231d5c6c8c0f7e39c0adc 100644 (file)
@@ -10,6 +10,8 @@ package org.opendaylight.controller.netconf.util.messages;
 
 import com.google.common.base.Preconditions;
 import io.netty.channel.Channel;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelFutureListener;
 import org.opendaylight.controller.netconf.api.NetconfSession;
 import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
 import org.opendaylight.controller.netconf.api.NetconfMessage;
@@ -38,13 +40,15 @@ public final class SendErrorExceptionUtil {
             final NetconfDocumentedException sendErrorException) {
         logger.trace("Sending error {}", sendErrorException.getMessage(), sendErrorException);
         final Document errorDocument = createDocument(sendErrorException);
-        session.sendMessage(new NetconfMessage(errorDocument));
+        ChannelFuture f = session.sendMessage(new NetconfMessage(errorDocument));
+        f.addListener(new SendErrorVerifyingListener(sendErrorException));
     }
 
     public static void sendErrorMessage(Channel channel, NetconfDocumentedException sendErrorException) {
         logger.trace("Sending error {}", sendErrorException.getMessage(), sendErrorException);
         final Document errorDocument = createDocument(sendErrorException);
-        channel.writeAndFlush(new NetconfMessage(errorDocument));
+        ChannelFuture f = channel.writeAndFlush(new NetconfMessage(errorDocument));
+        f.addListener(new SendErrorVerifyingListener(sendErrorException));
     }
 
     public static void sendErrorMessage(NetconfSession session, NetconfDocumentedException sendErrorException,
@@ -52,7 +56,8 @@ public final class SendErrorExceptionUtil {
         final Document errorDocument = createDocument(sendErrorException);
         logger.trace("Sending error {}", XmlUtil.toString(errorDocument));
         tryToCopyAttributes(incommingMessage.getDocument(), errorDocument, sendErrorException);
-        session.sendMessage(new NetconfMessage(errorDocument));
+        ChannelFuture f = session.sendMessage(new NetconfMessage(errorDocument));
+        f.addListener(new SendErrorVerifyingListener(sendErrorException));
     }
 
     private static void tryToCopyAttributes(final Document incommingDocument, final Document errorDocument,
@@ -133,4 +138,20 @@ public final class SendErrorExceptionUtil {
         return errorDocument;
     }
 
+    /**
+     * Checks if netconf error was sent successfully.
+     */
+    private static final class SendErrorVerifyingListener implements ChannelFutureListener {
+        private final NetconfDocumentedException sendErrorException;
+
+        public SendErrorVerifyingListener(final NetconfDocumentedException sendErrorException) {
+            this.sendErrorException = sendErrorException;
+        }
+
+        @Override
+        public void operationComplete(final ChannelFuture channelFuture) throws Exception {
+            Preconditions.checkState(channelFuture.isSuccess(), "Unable to send exception {}", sendErrorException,
+                    channelFuture.cause());
+        }
+    }
 }