</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>
+++ /dev/null
-/*
- * 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
- }
-
-}
--- /dev/null
+/*
+ * 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;
+ }
+
+}
+++ /dev/null
-/*
- * 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, "")
- }
-}
--- /dev/null
+/*
+ * 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, "");
+ }
+}
+++ /dev/null
-/*
- * 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();
- }
-}
--- /dev/null
+/*
+ * 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();
+ }
+}
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;
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> {
return capabilities;
}
-
@Override
protected NetconfClientSession thisInstance() {
return this;
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;
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;
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>
@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());
}
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();
}
@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);
}
/**
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
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 {}, " +
negotiationSuccessful(session);
}
}
+
}
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);
}
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;
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> {
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();
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;
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;
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();
}
/**
}
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);
}
}
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;
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);
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();
@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);
}
}
}
}
- 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);
}
}
}
</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>
/*
- * 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;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
-import javax.inject.Inject;
import javax.xml.parsers.ParserConfigurationException;
import org.junit.Assert;
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;
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);
}
}
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);
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;
}
@Override
- protected void startNegotiation() {
+ protected final void startNegotiation() {
final Optional<SslHandler> sslHandler = getSslHandler(channel);
if (sslHandler.isPresent()) {
Future<Channel> future = sslHandler.get().handshakeFuture();
// 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();
}
/**
* 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());
}
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);
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) {
* 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;
}
final DOMResult domResult = new DOMResult();
handler.setResult(domResult);
-
try (final InputStream is = new ByteBufInputStream(in)) {
r.parse(new InputSource(is));
}
*/
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;
* 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);
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 {
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);
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;
+ }
}
*/
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;
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))));
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}
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,
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);
+ }
+ }
}
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;
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,
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,
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());
+ }
+ }
}