Initial code drop of yang model driven configuration system 62/1462/5
authorTomas Olvecky <tolvecky@cisco.com>
Fri, 27 Sep 2013 13:38:10 +0000 (15:38 +0200)
committerGerrit Code Review <gerrit@opendaylight.org>
Wed, 2 Oct 2013 14:19:10 +0000 (14:19 +0000)
Configuration system employs two phase commit to validate and push configuration changes to a running system. It allows changing simple attributes, complex transfer objects, and also provides dependency injection between configured modules.
Module config-api contains base yang model config.yang, defining language extensions and other elements required by all configuration models. Other than that it contains both apis implemented by config-manager and spis to be implemeted by configuration providers. Since the configuration system is internally driven by JMX, package org.opendaylight.controller.config.api.jmx contains all M(X)Bean interfaces exposed by config-manager.
Module config-manager is implementation of config-api.
Module config-util contains clients (both JMX and jolokia, which is http based bridge to JMX) of configuration system.
Module yang-jmx-generator parses yang models and creates java representation of configuration models and service interfaces.
Module yang-jmx-generator-plugin is connector to yangtools yang-maven-plugin that generates skeletons of java files needed by configuration providers.
Module yang-test contains example yang file, from which code is being generated.
Module yang-jmx-generator-it tests yang-test.
Module yang-store-api contains api for a registry of all yang models parsed by yang-jmx-generator.
Module yang-store-impl uses OSGi extender pattern to read META-INF/yang/*.yang from all bundles and provides snapshot view of currently available configuration models.

Change-Id: Icf3201f9754e4ca28ebce3411d2a667dcd7e75c8
Signed-off-by: Tomas Olvecky <tolvecky@cisco.com>
262 files changed:
opendaylight/commons/opendaylight/pom.xml
opendaylight/config/config-api/.gitignore [new file with mode: 0644]
opendaylight/config/config-api/pom.xml [new file with mode: 0644]
opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/ConfigRegistry.java [new file with mode: 0644]
opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/ConfigTransactionController.java [new file with mode: 0644]
opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/ConflictingVersionException.java [new file with mode: 0644]
opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/DependencyResolver.java [new file with mode: 0644]
opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/DynamicMBeanWithInstance.java [new file with mode: 0644]
opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/JmxAttribute.java [new file with mode: 0644]
opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/JmxAttributeValidationException.java [new file with mode: 0644]
opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/LookupRegistry.java [new file with mode: 0644]
opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/ModuleIdentifier.java [new file with mode: 0644]
opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/RuntimeBeanRegistratorAwareModule.java [new file with mode: 0644]
opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/ValidationException.java [new file with mode: 0644]
opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/annotations/AbstractServiceInterface.java [new file with mode: 0644]
opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/annotations/Description.java [new file with mode: 0644]
opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/annotations/RequireInterface.java [new file with mode: 0644]
opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/annotations/ServiceInterfaceAnnotation.java [new file with mode: 0644]
opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/jmx/CommitStatus.java [new file with mode: 0644]
opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/jmx/ConfigRegistryMXBean.java [new file with mode: 0644]
opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/jmx/ConfigTransactionControllerMXBean.java [new file with mode: 0644]
opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/jmx/ObjectNameUtil.java [new file with mode: 0644]
opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/jmx/constants/ConfigRegistryConstants.java [new file with mode: 0644]
opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/runtime/HierarchicalRuntimeBeanRegistration.java [new file with mode: 0644]
opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/runtime/RootRuntimeBeanRegistrator.java [new file with mode: 0644]
opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/runtime/RuntimeBean.java [new file with mode: 0644]
opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/spi/Module.java [new file with mode: 0644]
opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/spi/ModuleFactory.java [new file with mode: 0644]
opendaylight/config/config-api/src/main/resources/META-INF/yang/config.yang [new file with mode: 0644]
opendaylight/config/config-api/src/main/resources/META-INF/yang/rpc-context.yang [new file with mode: 0644]
opendaylight/config/config-manager/.gitignore [new file with mode: 0644]
opendaylight/config/config-manager/pom.xml [new file with mode: 0644]
opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/CommitInfo.java [new file with mode: 0644]
opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/ConfigRegistryImpl.java [new file with mode: 0644]
opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/ConfigRegistryImplMXBean.java [new file with mode: 0644]
opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/ConfigTransactionControllerImpl.java [new file with mode: 0644]
opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/ConfigTransactionControllerImplMXBean.java [new file with mode: 0644]
opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/ConfigTransactionControllerInternal.java [new file with mode: 0644]
opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/DestroyedModule.java [new file with mode: 0644]
opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/ModuleInternalInfo.java [new file with mode: 0644]
opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/ModuleInternalTransactionalInfo.java [new file with mode: 0644]
opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/TransactionIdentifier.java [new file with mode: 0644]
opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/TransactionStatus.java [new file with mode: 0644]
opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/dependencyresolver/DependencyResolverImpl.java [new file with mode: 0644]
opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/dependencyresolver/DependencyResolverManager.java [new file with mode: 0644]
opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/dependencyresolver/ModulesHolder.java [new file with mode: 0644]
opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/dependencyresolver/TransactionHolder.java [new file with mode: 0644]
opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/dynamicmbean/AbstractDynamicWrapper.java [new file with mode: 0644]
opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/dynamicmbean/AnnotationsHelper.java [new file with mode: 0644]
opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/dynamicmbean/AttributeHolder.java [new file with mode: 0644]
opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/dynamicmbean/DynamicMBeanModuleWrapper.java [new file with mode: 0644]
opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/dynamicmbean/DynamicReadableWrapper.java [new file with mode: 0644]
opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/dynamicmbean/DynamicWritableWrapper.java [new file with mode: 0644]
opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/dynamicmbean/ReadOnlyAtomicBoolean.java [new file with mode: 0644]
opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/factoriesresolver/HierarchicalConfigMBeanFactoriesHolder.java [new file with mode: 0644]
opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/factoriesresolver/ModuleFactoriesResolver.java [new file with mode: 0644]
opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/jmx/BaseJMXRegistrator.java [new file with mode: 0644]
opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/jmx/ConfigRegistryJMXRegistrator.java [new file with mode: 0644]
opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/jmx/HierarchicalRuntimeBeanRegistrationImpl.java [new file with mode: 0644]
opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/jmx/InternalJMXRegistrator.java [new file with mode: 0644]
opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/jmx/ModuleJMXRegistrator.java [new file with mode: 0644]
opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/jmx/RootRuntimeBeanRegistratorImpl.java [new file with mode: 0644]
opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/jmx/TransactionJMXRegistrator.java [new file with mode: 0644]
opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/jmx/TransactionModuleJMXRegistrator.java [new file with mode: 0644]
opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/osgi/BeanToOsgiServiceManager.java [new file with mode: 0644]
opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/osgi/BundleContextBackedModuleFactoriesResolver.java [new file with mode: 0644]
opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/osgi/ConfigManagerActivator.java [new file with mode: 0644]
opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/osgi/ExtenderBundleTracker.java [new file with mode: 0644]
opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/util/InterfacesHelper.java [new file with mode: 0644]
opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/util/LookupBeansUtil.java [new file with mode: 0644]
opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/ConfigRegistryImplTest.java [new file with mode: 0644]
opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/impl/AbstractConfigTest.java [new file with mode: 0644]
opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/impl/AbstractConfigWithJolokiaTest.java [new file with mode: 0644]
opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/impl/AbstractLockedPlatformMBeanServerTest.java [new file with mode: 0644]
opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/impl/AbstractMockedModule.java [new file with mode: 0644]
opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/impl/ClassBasedModuleFactory.java [new file with mode: 0644]
opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/impl/ConfigRegistryImplLookupTest.java [new file with mode: 0644]
opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/impl/ConfigTransactionControllerImplTest.java [new file with mode: 0644]
opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/impl/ConfigTransactionManagerImplTest.java [new file with mode: 0644]
opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/impl/dependencyresolver/DependencyResolverManagerTest.java [new file with mode: 0644]
opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/impl/dynamicmbean/AbstractDynamicWrapperTest.java [new file with mode: 0644]
opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/impl/dynamicmbean/AnnotationsTest.java [new file with mode: 0644]
opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/impl/dynamicmbean/DynamicReadableWrapperTest.java [new file with mode: 0644]
opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/impl/dynamicmbean/DynamicWritableWrapperTest.java [new file with mode: 0644]
opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/impl/factoriesresolver/HardcodedModuleFactoriesResolver.java [new file with mode: 0644]
opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/impl/factoriesresolver/HierarchicalConfigMBeanFactoriesHolderTest.java [new file with mode: 0644]
opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/impl/runtimembean/RuntimeBeanRegistratorImplTest.java [new file with mode: 0644]
opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/impl/runtimembean/TestingRuntimeBean.java [new file with mode: 0644]
opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/impl/runtimembean/TestingRuntimeBeanMXBean.java [new file with mode: 0644]
opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/impl/util/InterfacesHelperTest.java [new file with mode: 0644]
opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/impl/util/ObjectNameUtilTest.java [new file with mode: 0644]
opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/parallelapsp/TestingAPSP.java [new file with mode: 0644]
opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/parallelapsp/TestingParallelAPSPConfigMXBean.java [new file with mode: 0644]
opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/parallelapsp/TestingParallelAPSPImpl.java [new file with mode: 0644]
opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/parallelapsp/TestingParallelAPSPModule.java [new file with mode: 0644]
opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/parallelapsp/TestingParallelAPSPModuleFactory.java [new file with mode: 0644]
opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/parallelapsp/package-info.java [new file with mode: 0644]
opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/parallelapsp/test/AbstractParallelAPSPTest.java [new file with mode: 0644]
opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/parallelapsp/test/DependentWiringTest.java [new file with mode: 0644]
opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/parallelapsp/test/MockedDependenciesTest.java [new file with mode: 0644]
opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/scheduledthreadpool/TestingScheduledThreadPoolConfigBeanMXBean.java [new file with mode: 0644]
opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/scheduledthreadpool/TestingScheduledThreadPoolIfc.java [new file with mode: 0644]
opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/scheduledthreadpool/TestingScheduledThreadPoolImpl.java [new file with mode: 0644]
opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/scheduledthreadpool/TestingScheduledThreadPoolModule.java [new file with mode: 0644]
opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/scheduledthreadpool/TestingScheduledThreadPoolModuleFactory.java [new file with mode: 0644]
opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/scheduledthreadpool/package-info.java [new file with mode: 0644]
opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/scheduledthreadpool/runtimebeans/TestingScheduledRuntimeBean.java [new file with mode: 0644]
opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/scheduledthreadpool/runtimebeans/TestingScheduledRuntimeMXBean.java [new file with mode: 0644]
opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/scheduledthreadpool/test/AbstractScheduledTest.java [new file with mode: 0644]
opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/scheduledthreadpool/test/RuntimeBeanTest.java [new file with mode: 0644]
opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/scheduledthreadpool/test/TwoInterfacesExportTest.java [new file with mode: 0644]
opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/seviceinterface/ModifiableThreadPoolServiceInterface.java [new file with mode: 0644]
opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/seviceinterface/TestingScheduledThreadPoolServiceInterface.java [new file with mode: 0644]
opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/seviceinterface/TestingThreadPoolServiceInterface.java [new file with mode: 0644]
opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/threadpool/TestingFixedThreadPool.java [new file with mode: 0644]
opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/threadpool/TestingFixedThreadPoolConfigMXBean.java [new file with mode: 0644]
opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/threadpool/TestingFixedThreadPoolModule.java [new file with mode: 0644]
opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/threadpool/TestingFixedThreadPoolModuleFactory.java [new file with mode: 0644]
opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/threadpool/TestingModifiableThreadPoolIfc.java [new file with mode: 0644]
opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/threadpool/TestingThreadPoolConfigMXBean.java [new file with mode: 0644]
opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/threadpool/TestingThreadPoolIfc.java [new file with mode: 0644]
opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/threadpool/package-info.java [new file with mode: 0644]
opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/threadpool/test/SimpleConfigurationTest.java [new file with mode: 0644]
opendaylight/config/config-util/.gitignore [new file with mode: 0644]
opendaylight/config/config-util/pom.xml [new file with mode: 0644]
opendaylight/config/config-util/src/main/java/org/opendaylight/controller/config/util/AttributeEntry.java [new file with mode: 0644]
opendaylight/config/config-util/src/main/java/org/opendaylight/controller/config/util/ConfigRegistryClient.java [new file with mode: 0644]
opendaylight/config/config-util/src/main/java/org/opendaylight/controller/config/util/ConfigRegistryJMXClient.java [new file with mode: 0644]
opendaylight/config/config-util/src/main/java/org/opendaylight/controller/config/util/ConfigTransactionClient.java [new file with mode: 0644]
opendaylight/config/config-util/src/main/java/org/opendaylight/controller/config/util/ConfigTransactionJMXClient.java [new file with mode: 0644]
opendaylight/config/config-util/src/main/java/org/opendaylight/controller/config/util/jolokia/ConfigRegistryJolokiaClient.java [new file with mode: 0644]
opendaylight/config/config-util/src/main/java/org/opendaylight/controller/config/util/jolokia/ConfigTransactionJolokiaClient.java [new file with mode: 0644]
opendaylight/config/config-util/src/main/java/org/opendaylight/controller/config/util/jolokia/ListableJolokiaClient.java [new file with mode: 0644]
opendaylight/config/config-util/src/test/java/org/opendaylight/controller/config/util/ConfigRegistryClientsTest.java [new file with mode: 0644]
opendaylight/config/config-util/src/test/java/org/opendaylight/controller/config/util/ConfigTransactionClientsTest.java [new file with mode: 0644]
opendaylight/config/config-util/src/test/java/org/opendaylight/controller/config/util/JolokiaHelper.java [new file with mode: 0644]
opendaylight/config/config-util/src/test/java/org/opendaylight/controller/config/util/LookupTest.java [new file with mode: 0644]
opendaylight/config/config-util/src/test/java/org/opendaylight/controller/config/util/TestingBeanImpl.java [new file with mode: 0644]
opendaylight/config/config-util/src/test/java/org/opendaylight/controller/config/util/TestingBeanMXBean.java [new file with mode: 0644]
opendaylight/config/config-util/src/test/java/org/opendaylight/controller/config/util/TestingConfigRegistry.java [new file with mode: 0644]
opendaylight/config/config-util/src/test/java/org/opendaylight/controller/config/util/TestingConfigTransactionController.java [new file with mode: 0644]
opendaylight/config/config-util/src/test/java/org/opendaylight/controller/config/util/jolokia/ListableJolokiaClientTest.java [new file with mode: 0644]
opendaylight/config/pom.xml [new file with mode: 0755]
opendaylight/config/yang-jmx-generator-it/pom.xml [new file with mode: 0644]
opendaylight/config/yang-jmx-generator-it/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/it/ITTest.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator-plugin/pom.xml [new file with mode: 0644]
opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/CodeWriter.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/FreeMarkerCodeWriterImpl.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/JMXGenerator.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/AbstractFactoryTemplate.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/AbstractFtlTemplate.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/AbstractModuleTemplate.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/FtlFilePersister.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/FtlTemplate.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/GeneralClassTemplate.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/GeneralInterfaceTemplate.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/RuntimeRegistratorFtlTemplate.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/StubFactoryTemplate.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/StubModuleTemplate.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/TemplateFactory.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/TypeHelper.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/directives/AnnotationsDirective.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/directives/ConstructorsDirective.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/directives/FieldsDirectiveProg.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/directives/FieldsDirectiveTemplate.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/directives/HeaderDirective.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/directives/JavadocDirective.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/directives/MethodsDirective.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/directives/ModuleFieldsDirective.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/directives/TypeDeclarationDirective.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/directives/UnimplementedExceptionDirective.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/model/Annotation.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/model/Constructor.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/model/Field.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/model/Header.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/model/Method.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/model/MethodDeclaration.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/model/MethodDefinition.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/model/ModuleField.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/model/TypeDeclaration.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator-plugin/src/main/resources/freeMarker/abstract_ftl_file.ftl [new file with mode: 0644]
opendaylight/config/yang-jmx-generator-plugin/src/main/resources/freeMarker/factory_abs_template.ftl [new file with mode: 0644]
opendaylight/config/yang-jmx-generator-plugin/src/main/resources/freeMarker/module_abs_template_new.ftl [new file with mode: 0644]
opendaylight/config/yang-jmx-generator-plugin/src/main/resources/freeMarker/module_stub_template.ftl [new file with mode: 0644]
opendaylight/config/yang-jmx-generator-plugin/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/AbstractGeneratorTest.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator-plugin/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/JMXGeneratorFileNamesValidationTest.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator-plugin/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/JMXGeneratorGeneratedFilesTrackerTest.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator-plugin/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/JMXGeneratorTest.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator-plugin/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ModuleMXBeanEntryPluginTest.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator-plugin/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ModuleMXBeanEntryTemplatesTest.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator-plugin/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/RuntimeRegistratorFtlFileTest.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator-plugin/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/FtlFilePersisterTest.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator-plugin/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/util/FormattingUtil.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator/pom.xml [new file with mode: 0644]
opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/AbstractEntry.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/ConfigConstants.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/ModuleMXBeanEntry.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/PackageTranslator.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/RuntimeBeanEntry.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/ServiceInterfaceEntry.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/TypeProviderWrapper.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/AbstractAttribute.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/AttributeIfc.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/DependencyAttribute.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/JavaAttribute.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/ListAttribute.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/SimpleTypeResolver.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/TOAttribute.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/TypedAttribute.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/Util.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/util/FullyQualifiedNameHelper.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/util/NameConflictException.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/AbstractYangTest.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/ModuleMXBeanEntryNameConflictTest.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/ModuleMXBeanEntryTest.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/PackageTranslatorTest.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/RuntimeBeanEntryTest.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/RuntimeRegistratorTest.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/SchemaContextTest.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/ServiceInterfaceEntryTest.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/util/YangModelSearchUtils.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/unknownextension/UnknownExtensionTest.java [new file with mode: 0644]
opendaylight/config/yang-jmx-generator/src/test/resources/config-bgp-listener-impl.yang [new file with mode: 0644]
opendaylight/config/yang-jmx-generator/src/test/resources/config-jmx-it-impl.yang [new file with mode: 0644]
opendaylight/config/yang-jmx-generator/src/test/resources/config-jmx-it.yang [new file with mode: 0644]
opendaylight/config/yang-jmx-generator/src/test/resources/duplicates/config-test-duplicate-attribute-in-list.yang [new file with mode: 0644]
opendaylight/config/yang-jmx-generator/src/test/resources/duplicates/config-test-duplicate-attribute-runtime-bean.yang [new file with mode: 0644]
opendaylight/config/yang-jmx-generator/src/test/resources/duplicates/config-test-duplicate-attribute.yang [new file with mode: 0644]
opendaylight/config/yang-jmx-generator/src/test/resources/duplicates/config-test-generated-attributes-name-conflict.yang [new file with mode: 0644]
opendaylight/config/yang-jmx-generator/src/test/resources/duplicates/config-test-runtime-bean-list-name-conflict.yang [new file with mode: 0644]
opendaylight/config/yang-jmx-generator/src/test/resources/duplicates/config-test-runtime-bean-list-name-conflict2.yang [new file with mode: 0644]
opendaylight/config/yang-jmx-generator/src/test/resources/duplicates/config-test-runtime-bean-name-conflict.yang [new file with mode: 0644]
opendaylight/config/yang-jmx-generator/src/test/resources/duplicates/config-test-runtime-bean-name-conflict2.yang [new file with mode: 0644]
opendaylight/config/yang-jmx-generator/src/test/resources/ietf-inet-types.yang [new file with mode: 0644]
opendaylight/config/yang-jmx-generator/src/test/resources/org/opendaylight/controller/config/yangjmxgenerator/unknownextension/test-ifcWithUnknownExtension.yang [new file with mode: 0644]
opendaylight/config/yang-jmx-generator/src/test/resources/test-config-files.yang [new file with mode: 0644]
opendaylight/config/yang-jmx-generator/src/test/resources/test-config-files1.yang [new file with mode: 0644]
opendaylight/config/yang-jmx-generator/src/test/resources/test-config-threads-java.yang [new file with mode: 0644]
opendaylight/config/yang-jmx-generator/src/test/resources/test-config-threads.yang [new file with mode: 0644]
opendaylight/config/yang-store-api/pom.xml [new file with mode: 0644]
opendaylight/config/yang-store-api/src/main/java/org/opendaylight/controller/config/yang/store/api/YangStoreException.java [new file with mode: 0644]
opendaylight/config/yang-store-api/src/main/java/org/opendaylight/controller/config/yang/store/api/YangStoreService.java [new file with mode: 0644]
opendaylight/config/yang-store-api/src/main/java/org/opendaylight/controller/config/yang/store/api/YangStoreSnapshot.java [new file with mode: 0644]
opendaylight/config/yang-store-impl/.gitignore [new file with mode: 0644]
opendaylight/config/yang-store-impl/pom.xml [new file with mode: 0644]
opendaylight/config/yang-store-impl/src/main/java/org/opendaylight/controller/config/yang/store/impl/ExtenderYangTracker.java [new file with mode: 0644]
opendaylight/config/yang-store-impl/src/main/java/org/opendaylight/controller/config/yang/store/impl/MbeParser.java [new file with mode: 0644]
opendaylight/config/yang-store-impl/src/main/java/org/opendaylight/controller/config/yang/store/impl/YangStoreActivator.java [new file with mode: 0644]
opendaylight/config/yang-store-impl/src/main/java/org/opendaylight/controller/config/yang/store/impl/YangStoreSnapshotImpl.java [new file with mode: 0644]
opendaylight/config/yang-store-impl/src/test/java/org/opendaylight/controller/config/yang/store/impl/ExtenderYangTrackerTest.java [new file with mode: 0644]
opendaylight/config/yang-store-impl/src/test/java/org/opendaylight/controller/config/yang/store/impl/HardcodedYangStoreService.java [new file with mode: 0644]
opendaylight/config/yang-test/README.txt [new file with mode: 0644]
opendaylight/config/yang-test/pom.xml [new file with mode: 0644]
opendaylight/config/yang-test/src/main/java/org/opendaylight/controller/config/yang/test/impl/DepTestImplModule.java [new file with mode: 0644]
opendaylight/config/yang-test/src/main/java/org/opendaylight/controller/config/yang/test/impl/DepTestImplModuleFactory.java [new file with mode: 0644]
opendaylight/config/yang-test/src/main/java/org/opendaylight/controller/config/yang/test/impl/NetconfTestImplModule.java [new file with mode: 0644]
opendaylight/config/yang-test/src/main/java/org/opendaylight/controller/config/yang/test/impl/NetconfTestImplModuleFactory.java [new file with mode: 0644]
opendaylight/config/yang-test/src/main/java/org/opendaylight/controller/config/yang/test/impl/TestImplModule.java [new file with mode: 0644]
opendaylight/config/yang-test/src/main/java/org/opendaylight/controller/config/yang/test/impl/TestImplModuleFactory.java [new file with mode: 0644]
opendaylight/config/yang-test/src/main/yang/config-test-impl.yang [new file with mode: 0644]
opendaylight/config/yang-test/src/main/yang/config-test.yang [new file with mode: 0644]
opendaylight/distribution/opendaylight/pom.xml

index f78fd4723d921df00a92bacb710531215f2682b0..d8c4ccebb1bc93acab0a62e68a79f0d5d1a122aa 100644 (file)
@@ -61,6 +61,7 @@
     <enforcer.version>1.3.1</enforcer.version>
     <bundle.plugin.version>2.3.7</bundle.plugin.version>
     <junit.version>4.8.1</junit.version>
+    <bgpcep.version>0.2.0-SNAPSHOT</bgpcep.version>
   </properties>
 
  <dependencyManagement>
           <artifactId>javassist</artifactId>
           <version>${javassist.version}</version>
         </dependency>
+        <dependency>
+          <groupId>org.opendaylight.bgpcep</groupId>
+          <artifactId>concepts</artifactId>
+          <version>${bgpcep.version}</version>
+        </dependency>
+        <dependency>
+          <groupId>org.opendaylight.bgpcep</groupId>
+          <artifactId>util</artifactId>
+          <version>${bgpcep.version}</version>
+        </dependency>
       </dependencies>
       <properties>
         <build.suffix>${project.version}</build.suffix>
     <dependency>
       <groupId>commons-io</groupId>
       <artifactId>commons-io</artifactId>
-      <version>2.3</version>
+      <version>2.4</version>
+    </dependency>
+    <dependency>
+      <groupId>commons-codec</groupId>
+      <artifactId>commons-codec</artifactId>
+      <version>1.7</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-lang3</artifactId>
+      <version>${commons.lang.version}</version>
     </dependency>
     <dependency>
       <groupId>commons-fileupload</groupId>
diff --git a/opendaylight/config/config-api/.gitignore b/opendaylight/config/config-api/.gitignore
new file mode 100644 (file)
index 0000000..fc1d35e
--- /dev/null
@@ -0,0 +1,3 @@
+target
+.classpath
+.settings
diff --git a/opendaylight/config/config-api/pom.xml b/opendaylight/config/config-api/pom.xml
new file mode 100644 (file)
index 0000000..5c36f20
--- /dev/null
@@ -0,0 +1,52 @@
+<?xml version="1.0"?>
+<project
+        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
+        xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <artifactId>config-subsystem</artifactId>
+        <groupId>org.opendaylight</groupId>
+        <version>0.2.1-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>config-api</artifactId>
+    <name>${project.artifactId}</name>
+    <packaging>bundle</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.google.code.findbugs</groupId>
+            <artifactId>jsr305</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.bgpcep</groupId>
+            <artifactId>concepts</artifactId>
+            <version>0.2.0-SNAPSHOT</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Import-Package>
+                            javax.management,
+                            org.opendaylight.protocol.concepts
+                        </Import-Package>
+                        <Export-Package>
+                            org.opendaylight.controller.config.api,
+                            org.opendaylight.controller.config.api.annotations,
+                            org.opendaylight.controller.config.spi,
+                            org.opendaylight.controller.config.api.jmx,
+                            org.opendaylight.controller.config.api.jmx.constants,
+                            org.opendaylight.controller.config.api.runtime,
+                        </Export-Package>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/ConfigRegistry.java b/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/ConfigRegistry.java
new file mode 100644 (file)
index 0000000..d50d6ba
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * 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.api;
+
+import java.util.List;
+import java.util.Set;
+
+import javax.management.ObjectName;
+
+import org.opendaylight.controller.config.api.jmx.CommitStatus;
+import org.opendaylight.controller.config.api.jmx.ConfigTransactionControllerMXBean;
+import org.opendaylight.controller.config.api.jmx.constants.ConfigRegistryConstants;
+
+/**
+ * Provides functionality for working with configuration registry - mainly
+ * creating and committing config transactions.
+ */
+public interface ConfigRegistry extends LookupRegistry {
+
+    /**
+     * Only well-known ObjectName in configuration system, under which
+     * ConfigRegisry is registered.
+     */
+    public static final ObjectName OBJECT_NAME = ConfigRegistryConstants.OBJECT_NAME;
+
+    /**
+     * Opens new configuration transaction.
+     *
+     * @return {@link ObjectName} of {@link ConfigTransactionControllerMXBean}
+     */
+    ObjectName beginConfig();
+
+    /**
+     * Verifies and commits transaction.
+     *
+     * @param transactionControllerON
+     *            {@link ObjectName} of
+     *            {@link ConfigTransactionControllerMXBean} that was received in
+     *            {@link #beginConfig()} method call.
+     * @return CommitStatus
+     * @throws ValidationException
+     *             if validation fails
+     * @throws ConflictingVersionException
+     *             if configuration state was changed
+     */
+    CommitStatus commitConfig(ObjectName transactionControllerON)
+            throws ConflictingVersionException, ValidationException;
+
+    /**
+     * @return list of open configuration transactions.
+     */
+    List<ObjectName> getOpenConfigs();
+
+    /**
+     * Will return true unless there was a transaction that succeeded during
+     * validation but failed in second phase of commit. In this case the server
+     * is unstable and its state is undefined.
+     */
+    boolean isHealthy();
+
+    Set<String> getAvailableModuleNames();
+
+    /**
+     * Find all runtime beans
+     *
+     * @return objectNames
+     */
+    Set<ObjectName> lookupRuntimeBeans();
+
+    /**
+     * Find all runtime of specified module
+     *
+     * @param moduleName
+     *            of bean
+     * @param instanceName
+     *            of bean
+     * @return objectNames
+     */
+    Set<ObjectName> lookupRuntimeBeans(String moduleName, String instanceName);
+
+}
diff --git a/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/ConfigTransactionController.java b/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/ConfigTransactionController.java
new file mode 100644 (file)
index 0000000..7e8ee64
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * 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.api;
+
+import java.util.Set;
+
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.InstanceNotFoundException;
+import javax.management.ObjectName;
+
+/**
+ * Represents functionality provided by configuration transaction.
+ */
+public interface ConfigTransactionController extends LookupRegistry {
+
+    /**
+     * Create new configuration bean.
+     *
+     * @param moduleName
+     * @param instanceName
+     * @return ObjectName of newly created module
+     * @throws InstanceAlreadyExistsException
+     *             if given ifcName and instanceName is already registered
+     */
+    ObjectName createModule(String moduleName, String instanceName)
+            throws InstanceAlreadyExistsException;
+
+    /**
+     * Destroy existing module.
+     *
+     * @param objectName
+     *            can be either read-only module name that can be obtained using
+     *            {@link ConfigRegistry#lookupConfigBean(String, String)} or
+     *            writable module name that must contain current transaction
+     *            name.
+     * @throws InstanceNotFoundException
+     *             if module is not found
+     * @throws IllegalArgumentException
+     *             if object name contains wrong transaction name or domain
+     */
+    void destroyModule(ObjectName objectName) throws InstanceNotFoundException;
+
+    /**
+     * Destroy current transaction.
+     */
+    void abortConfig();
+
+    /**
+     * This method can be called multiple times, has no side effects.
+     *
+     * @throws ValidationException
+     *             if validation fails
+     */
+    void validateConfig() throws ValidationException;
+
+    /**
+     *
+     * @return transactionName
+     */
+    String getTransactionName();
+
+    Set<String> getAvailableModuleNames();
+
+}
diff --git a/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/ConflictingVersionException.java b/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/ConflictingVersionException.java
new file mode 100644 (file)
index 0000000..9c25242
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * 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.api;
+
+/**
+ * Can be thrown during
+ * {@link ConfigRegistry#commitConfig(javax.management.ObjectName)} to indicate
+ * that the transaction cannot be committed due to the fact that another
+ * transaction was committed after creating this transaction. Clients can create
+ * new transaction and merge the changes.
+ */
+public class ConflictingVersionException extends RuntimeException {
+    private static final long serialVersionUID = 1L;
+
+    public ConflictingVersionException() {
+        super();
+    }
+
+    public ConflictingVersionException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public ConflictingVersionException(String message) {
+        super(message);
+    }
+
+    public ConflictingVersionException(Throwable cause) {
+        super(cause);
+    }
+
+}
diff --git a/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/DependencyResolver.java b/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/DependencyResolver.java
new file mode 100644 (file)
index 0000000..a2b171a
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * 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.api;
+
+import javax.management.ObjectName;
+
+import org.opendaylight.controller.config.api.annotations.AbstractServiceInterface;
+
+/**
+ * Each new {@link org.opendaylight.controller.config.spi.Module} can receive
+ * resolver from {@link org.opendaylight.controller.config.spi.ModuleFactory}
+ * for looking up dependencies during validation and second phase commit.
+ *
+ * @see org.opendaylight.controller.config.spi.Module
+ */
+public interface DependencyResolver {
+
+    /**
+     * To be used during validation phase to validate serice interface of
+     * dependent module.
+     *
+     * @param expectedServiceInterface
+     *            MBean/MXBean interface which will back the proxy object.
+     * @param objectName
+     *            ObjectName of dependent module without transaction name
+     *            (platformON).
+     * @param jmxAttribute
+     * @throws {@link IllegalArgumentException} when module is not found
+     * @throws {@link IllegalStateException} if module does not export this
+     *         service interface.
+     */
+    void validateDependency(
+            Class<? extends AbstractServiceInterface> expectedServiceInterface,
+            ObjectName objectName, JmxAttribute jmxAttribute);
+
+    @Deprecated
+    // TODO remove once all config code is generated
+    void validateDependency(
+            Class<? extends AbstractServiceInterface> expectedServiceInterface,
+            ObjectName objectName, String attributeNameForErrorReporting);
+
+    /**
+     * To be used during commit phase to wire actual dependencies.
+     *
+     * @return dependency instance using
+     *         {@link org.opendaylight.controller.config.spi.Module#getInstance()}
+     * @throws {@link IllegalArgumentException} when module is not found
+     */
+    <T> T resolveInstance(Class<T> expectedType, ObjectName objectName,
+            JmxAttribute jmxAttribute);
+
+    @Deprecated
+    <T> T resolveInstance(Class<T> expectedType, ObjectName objectName);
+
+}
diff --git a/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/DynamicMBeanWithInstance.java b/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/DynamicMBeanWithInstance.java
new file mode 100644 (file)
index 0000000..cf0cb6a
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * 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.api;
+
+import javax.management.DynamicMBean;
+
+import org.opendaylight.controller.config.spi.Module;
+
+/**
+ * Each {@link org.opendaylight.controller.config.spi.Module} that is committed
+ * will be wrapped into this interface.
+ */
+public interface DynamicMBeanWithInstance extends DynamicMBean {
+
+    /**
+     * Get original module that is wrapped with this instance.
+     */
+    Module getModule();
+
+    /**
+     * Gets 'live object' associated with current config object. Useful when
+     * reconfiguring {@link org.opendaylight.controller.config.spi.Module}
+     * instances.
+     */
+    AutoCloseable getInstance();
+
+}
diff --git a/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/JmxAttribute.java b/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/JmxAttribute.java
new file mode 100644 (file)
index 0000000..96deb05
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.config.api;
+
+/**
+ * Wrapper around strings to make {@link JmxAttributeValidationException} type
+ * safe.
+ */
+public class JmxAttribute {
+    private final String attributeName;
+
+    public JmxAttribute(String attributeName) {
+        if (attributeName == null)
+            throw new NullPointerException("Parameter 'attributeName' is null");
+        this.attributeName = attributeName;
+    }
+
+    public String getAttributeName() {
+        return attributeName;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o)
+            return true;
+        if (o == null || getClass() != o.getClass())
+            return false;
+
+        JmxAttribute that = (JmxAttribute) o;
+
+        if (attributeName != null ? !attributeName.equals(that.attributeName)
+                : that.attributeName != null)
+            return false;
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        return attributeName != null ? attributeName.hashCode() : 0;
+    }
+
+    @Override
+    public String toString() {
+        return "JmxAttribute{'" + attributeName + "'}";
+    }
+}
diff --git a/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/JmxAttributeValidationException.java b/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/JmxAttributeValidationException.java
new file mode 100644 (file)
index 0000000..3380a10
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * 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.api;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Exception that can be thrown during validation phase. This allows for
+ * pointing user to a specific list of parameters that fail the validation. Note
+ * that {@link org.opendaylight.controller.config.spi.Module#validate()} can
+ * throw any runtime exception to trigger validation failure.
+ */
+public class JmxAttributeValidationException extends RuntimeException {
+    private static final long serialVersionUID = 1L;
+    private final List<JmxAttribute> attributeNames;
+
+    public JmxAttributeValidationException(JmxAttribute jmxAttribute) {
+        this(Arrays.asList(jmxAttribute));
+    }
+
+    public JmxAttributeValidationException(List<JmxAttribute> jmxAttribute) {
+        this.attributeNames = jmxAttribute;
+    }
+
+    public JmxAttributeValidationException(String message,
+            JmxAttribute jmxAttribute) {
+        this(message, Arrays.asList(jmxAttribute));
+    }
+
+    public JmxAttributeValidationException(String message,
+            List<JmxAttribute> jmxAttributes) {
+        super(message);
+        this.attributeNames = jmxAttributes;
+    }
+
+    public JmxAttributeValidationException(String message, Throwable cause,
+            JmxAttribute jmxAttribute) {
+        this(message, cause, Arrays.asList(jmxAttribute));
+    }
+
+    public JmxAttributeValidationException(String message, Throwable cause,
+            List<JmxAttribute> jmxAttributes) {
+        super(message, cause);
+        this.attributeNames = jmxAttributes;
+    }
+
+    public List<JmxAttribute> getAttributeNames() {
+        return attributeNames;
+    }
+
+    public static <T> T checkNotNull(T param, JmxAttribute jmxAttribute) {
+        String message = "is null";
+        return checkNotNull(param, message, jmxAttribute);
+    }
+
+    public static <T> T checkNotNull(T param, String message,
+            JmxAttribute jmxAttribute) {
+        if (param == null) {
+            throw new JmxAttributeValidationException(
+                    jmxAttribute.getAttributeName() + " " + message,
+                    jmxAttribute);
+        }
+        return param;
+    }
+
+    public static JmxAttributeValidationException wrap(Throwable throwable,
+            JmxAttribute jmxAttribute) throws JmxAttributeValidationException {
+        return wrap(throwable, throwable.getMessage(), jmxAttribute);
+    }
+
+    public static JmxAttributeValidationException wrap(Throwable throwable,
+            String message, JmxAttribute jmxAttribute) {
+
+        throw new JmxAttributeValidationException(
+                jmxAttribute.getAttributeName() + " " + message, throwable,
+                jmxAttribute);
+    }
+
+    public static void checkCondition(boolean condition, String message,
+            JmxAttribute jmxAttribute) throws JmxAttributeValidationException {
+        if (condition == false) {
+            throw new JmxAttributeValidationException(
+                    jmxAttribute.getAttributeName() + " " + message,
+                    jmxAttribute);
+        }
+    }
+}
diff --git a/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/LookupRegistry.java b/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/LookupRegistry.java
new file mode 100644 (file)
index 0000000..7a3c4bf
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * 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.api;
+
+import java.util.Set;
+
+import javax.management.InstanceNotFoundException;
+import javax.management.ObjectName;
+
+public interface LookupRegistry {
+
+    /**
+     * Find all modules. Same Module can be registered multiple times.
+     *
+     * @return objectNames
+     */
+    Set<ObjectName> lookupConfigBeans();
+
+    /**
+     * Find modules with given module name.
+     *
+     * @param moduleName
+     * @return objectNames
+     */
+    Set<ObjectName> lookupConfigBeans(String moduleName);
+
+    /**
+     * Find read modules.
+     *
+     * @param moduleName
+     *            exact match for searched module name, can contain '*' to match
+     *            all values.
+     * @param instanceName
+     *            exact match for searched instance name, can contain '*' to
+     *            match all values.
+     * @return objectNames
+     */
+    Set<ObjectName> lookupConfigBeans(String moduleName, String instanceName);
+
+    /**
+     * Find read module.
+     *
+     * @param moduleName
+     *            exact match for searched module name, can contain '*' to match
+     *            all values.
+     * @param instanceName
+     *            exact match for searched instance name, can contain '*' to
+     *            match all values.
+     * @return objectNames
+     * @throws InstanceNotFoundException
+     *             if search did not find exactly one instance
+     */
+    ObjectName lookupConfigBean(String moduleName, String instanceName)
+            throws InstanceNotFoundException;
+
+}
diff --git a/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/ModuleIdentifier.java b/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/ModuleIdentifier.java
new file mode 100644 (file)
index 0000000..d162ca0
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * 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.api;
+
+import org.opendaylight.protocol.concepts.Identifier;
+
+public class ModuleIdentifier implements Identifier {
+    private static final long serialVersionUID = 1L;
+    private final String factoryName, instanceName;
+
+    public ModuleIdentifier(String factoryName, String instanceName) {
+        if (factoryName == null)
+            throw new IllegalArgumentException(
+                    "Parameter 'factoryName' is null");
+        if (instanceName == null)
+            throw new IllegalArgumentException(
+                    "Parameter 'instanceName' is null");
+        this.factoryName = factoryName;
+        this.instanceName = instanceName;
+    }
+
+    public String getFactoryName() {
+        return factoryName;
+    }
+
+    public String getInstanceName() {
+        return instanceName;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o)
+            return true;
+        if (o == null || getClass() != o.getClass())
+            return false;
+
+        ModuleIdentifier that = (ModuleIdentifier) o;
+
+        if (!factoryName.equals(that.factoryName))
+            return false;
+        if (!instanceName.equals(that.instanceName))
+            return false;
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = factoryName.hashCode();
+        result = 31 * result + instanceName.hashCode();
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        return "ModuleIdentifier{" + "factoryName='" + factoryName + '\''
+                + ", instanceName='" + instanceName + '\'' + '}';
+    }
+}
diff --git a/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/RuntimeBeanRegistratorAwareModule.java b/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/RuntimeBeanRegistratorAwareModule.java
new file mode 100644 (file)
index 0000000..a23d47b
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * 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.api;
+
+import java.io.Closeable;
+
+import org.opendaylight.controller.config.api.runtime.RootRuntimeBeanRegistrator;
+import org.opendaylight.controller.config.spi.Module;
+
+/**
+ * Module implementing this interface will receive
+ * {@link RootRuntimeBeanRegistrator} before getInstance() is invoked.
+ */
+public interface RuntimeBeanRegistratorAwareModule extends Module {
+    /**
+     * Configuration framework will call this setter on all modules implementing
+     * this interface. It is responsibility of modules or rather their instances
+     * to close registrator in their {@link Closeable#close()} method. Same
+     * module will get the same registrator during reconfiguration.
+     *
+     * @param rootRuntimeBeanRegistrator
+     */
+    public void setRuntimeBeanRegistrator(
+            RootRuntimeBeanRegistrator rootRuntimeBeanRegistrator);
+
+}
diff --git a/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/ValidationException.java b/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/ValidationException.java
new file mode 100644 (file)
index 0000000..90b8bb6
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * 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.api;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+/**
+ * This exception is not intended to be used while implementing modules,
+ * itaggregates validation exceptions and sends them back to the user.
+ */
+public class ValidationException extends RuntimeException {
+    private static final long serialVersionUID = -6072893219820274247L;
+
+    private final Map<String, Map<String, ExceptionMessageWithStackTrace>> failedValidations;
+
+    public ValidationException(
+            Map<String /* module name */, Map<String /* instance name */, ExceptionMessageWithStackTrace>> failedValidations) {
+        super(failedValidations.toString());
+        this.failedValidations = Collections.unmodifiableMap(failedValidations);
+    }
+
+    public static ValidationException createFromCollectedValidationExceptions(
+            List<ValidationException> collectedExceptions) {
+        Map<String, Map<String, ExceptionMessageWithStackTrace>> failedValidations = new HashMap<>();
+        for (ValidationException ve : collectedExceptions) {
+            for (Entry<String, Map<String, ExceptionMessageWithStackTrace>> outerEntry : ve
+                    .getFailedValidations().entrySet()) {
+                for (Entry<String, ExceptionMessageWithStackTrace> innerEntry : outerEntry
+                        .getValue().entrySet()) {
+                    String moduleName = outerEntry.getKey();
+                    String instanceName = innerEntry.getKey();
+                    ExceptionMessageWithStackTrace ex = innerEntry.getValue();
+                    Map<String, ExceptionMessageWithStackTrace> instanceToExMap = failedValidations
+                            .get(moduleName);
+                    if (instanceToExMap == null) {
+                        instanceToExMap = new HashMap<>();
+                        failedValidations.put(moduleName, instanceToExMap);
+                    }
+                    if (instanceToExMap.containsKey(instanceName)) {
+                        throw new IllegalArgumentException(
+                                "Cannot merge with same module name "
+                                        + moduleName + " and instance name "
+                                        + instanceName);
+                    }
+                    instanceToExMap.put(instanceName, ex);
+                }
+            }
+        }
+        return new ValidationException(failedValidations);
+    }
+
+    public static ValidationException createForSingleException(
+            ModuleIdentifier moduleIdentifier, Exception e) {
+        Map<String, Map<String, ExceptionMessageWithStackTrace>> failedValidations = new HashMap<>();
+        Map<String, ExceptionMessageWithStackTrace> innerMap = new HashMap<>();
+
+        failedValidations.put(moduleIdentifier.getFactoryName(), innerMap);
+        innerMap.put(moduleIdentifier.getInstanceName(),
+                new ExceptionMessageWithStackTrace(e));
+        return new ValidationException(failedValidations);
+    }
+
+    public Map<String, Map<String, ExceptionMessageWithStackTrace>> getFailedValidations() {
+        return failedValidations;
+    }
+
+    public static class ExceptionMessageWithStackTrace {
+        private String message, stackTrace;
+
+        public ExceptionMessageWithStackTrace() {
+        }
+
+        public ExceptionMessageWithStackTrace(String message, String stackTrace) {
+            this.message = message;
+            this.stackTrace = stackTrace;
+        }
+
+        public ExceptionMessageWithStackTrace(Exception e) {
+            this(e.getMessage(), Arrays.toString(e.getStackTrace()));
+        }
+
+        public String getMessage() {
+            return message;
+        }
+
+        public String getTrace() {
+            return stackTrace;
+        }
+
+        public void setMessage(String message) {
+            this.message = message;
+        }
+
+        public void setTrace(String stackTrace) {
+            this.stackTrace = stackTrace;
+        }
+
+        @Override
+        public int hashCode() {
+            final int prime = 31;
+            int result = 1;
+            result = prime * result
+                    + ((message == null) ? 0 : message.hashCode());
+            result = prime * result
+                    + ((stackTrace == null) ? 0 : stackTrace.hashCode());
+            return result;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj)
+                return true;
+            if (obj == null)
+                return false;
+            if (getClass() != obj.getClass())
+                return false;
+            ExceptionMessageWithStackTrace other = (ExceptionMessageWithStackTrace) obj;
+            if (message == null) {
+                if (other.message != null)
+                    return false;
+            } else if (!message.equals(other.message))
+                return false;
+            if (stackTrace == null) {
+                if (other.stackTrace != null)
+                    return false;
+            } else if (!stackTrace.equals(other.stackTrace))
+                return false;
+            return true;
+        }
+
+        @Override
+        public String toString() {
+            return "ExceptionMessageWithStackTrace [message=" + message
+                    + ", stackTrace=" + stackTrace + "]";
+        }
+
+    }
+}
diff --git a/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/annotations/AbstractServiceInterface.java b/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/annotations/AbstractServiceInterface.java
new file mode 100644 (file)
index 0000000..261c5f5
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * 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.api.annotations;
+
+/**
+ * Marker interface on all Service Interface annotated classes, each Service
+ * Interface must extend this interface. Service Intefaces can form hierarchies,
+ * one SI can extend another one, in which case all annotations in hierarchy
+ * will be observed.
+ */
+public abstract interface AbstractServiceInterface {
+}
diff --git a/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/annotations/Description.java b/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/annotations/Description.java
new file mode 100644 (file)
index 0000000..77362a8
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * 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.api.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Exports attribute and module class descriptions. Description annotation can
+ * be applied to module directly or to its super class or MXBean interface.
+ */
+@Target({ ElementType.TYPE, ElementType.METHOD })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Description {
+    /**
+     * Returns a human-readable description of the annotated attribute.
+     * Descriptions should be clear and concise, describing what the attribute
+     * affects.
+     *
+     * @return attribute description
+     */
+    String value();
+}
diff --git a/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/annotations/RequireInterface.java b/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/annotations/RequireInterface.java
new file mode 100644 (file)
index 0000000..3584776
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * 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.api.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import javax.management.ObjectName;
+
+/**
+ * Indicates what service interface is expected to be obtained as a dependency
+ * of a module. This annotation must be present for each dependency setter in
+ * {@link org.opendaylight.controller.config.spi.Module} M(X)Bean interface.
+ * Together with name of dependent bean the {@link #value()} will be used to get
+ * {@link ObjectName} of dependency.
+ *
+ * <p>
+ * Example:<br>
+ *
+ * <code>
+ *
+ * @RequireInterface(value = ThreadPoolServiceInterface.class, optional =
+ *                         false)<br/> void setThreadPool(ObjectName on);
+ *                         </code>
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+public @interface RequireInterface {
+
+    /**
+     * Declares dependency on service interface.
+     */
+    Class<? extends AbstractServiceInterface> value();
+
+}
diff --git a/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/annotations/ServiceInterfaceAnnotation.java b/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/annotations/ServiceInterfaceAnnotation.java
new file mode 100644 (file)
index 0000000..a81d992
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * 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.api.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Marks an interface implemented by
+ * {@link org.opendaylight.controller.config.spi.Module} as a Service Interface.
+ * Each service interface is identified by globally unique and human readable
+ * name. By convention the name is all lower case without spaces.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+public @interface ServiceInterfaceAnnotation {
+
+    /**
+     * Specifies human readable name of this service. Each service name should
+     * be globally unique. Should not contain spaces.
+     */
+    String value();
+
+    /**
+     * Mandatory class which will be used as key for OSGi service registration
+     * once {@link org.opendaylight.controller.config.spi.Module#getInstance()}
+     * is called.
+     */
+    Class<?> osgiRegistrationType();
+}
diff --git a/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/jmx/CommitStatus.java b/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/jmx/CommitStatus.java
new file mode 100644 (file)
index 0000000..bd53bd4
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * 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.api.jmx;
+
+import java.beans.ConstructorProperties;
+import java.util.Collections;
+import java.util.List;
+
+import javax.annotation.concurrent.Immutable;
+import javax.management.ObjectName;
+
+@Immutable
+public class CommitStatus {
+    private final List<ObjectName> newInstances, reusedInstances,
+            recreatedInstances;
+
+    /**
+     *
+     * @param newInstances
+     *            newly created instances
+     * @param reusedInstances
+     *            reused instances
+     * @param recreatedInstances
+     *            recreated instances
+     */
+    @ConstructorProperties({ "newInstances", "reusedInstances",
+            "recreatedInstances" })
+    public CommitStatus(List<ObjectName> newInstances,
+            List<ObjectName> reusedInstances,
+            List<ObjectName> recreatedInstances) {
+        this.newInstances = Collections.unmodifiableList(newInstances);
+        this.reusedInstances = Collections.unmodifiableList(reusedInstances);
+        this.recreatedInstances = Collections
+                .unmodifiableList(recreatedInstances);
+    }
+
+    /**
+     *
+     * @return list of objectNames representing newly created instances
+     */
+    public List<ObjectName> getNewInstances() {
+        return newInstances;
+    }
+
+    /**
+     *
+     * @return list of objectNames representing reused instances
+     */
+    public List<ObjectName> getReusedInstances() {
+        return reusedInstances;
+    }
+
+    /**
+     *
+     * @return list of objectNames representing recreated instances
+     */
+    public List<ObjectName> getRecreatedInstances() {
+        return recreatedInstances;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result
+                + ((newInstances == null) ? 0 : newInstances.hashCode());
+        result = prime
+                * result
+                + ((recreatedInstances == null) ? 0 : recreatedInstances
+                        .hashCode());
+        result = prime * result
+                + ((reusedInstances == null) ? 0 : reusedInstances.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        CommitStatus other = (CommitStatus) obj;
+        if (newInstances == null) {
+            if (other.newInstances != null)
+                return false;
+        } else if (!newInstances.equals(other.newInstances))
+            return false;
+        if (recreatedInstances == null) {
+            if (other.recreatedInstances != null)
+                return false;
+        } else if (!recreatedInstances.equals(other.recreatedInstances))
+            return false;
+        if (reusedInstances == null) {
+            if (other.reusedInstances != null)
+                return false;
+        } else if (!reusedInstances.equals(other.reusedInstances))
+            return false;
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        return "CommitStatus [newInstances=" + newInstances
+                + ", reusedInstances=" + reusedInstances
+                + ", recreatedInstances=" + recreatedInstances + "]";
+    }
+
+}
diff --git a/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/jmx/ConfigRegistryMXBean.java b/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/jmx/ConfigRegistryMXBean.java
new file mode 100644 (file)
index 0000000..af02f14
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * 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.api.jmx;
+
+import org.opendaylight.controller.config.api.ConfigRegistry;
+
+/**
+ * Represents entry point of configuration management. Note: Reason for having
+ * methods in super interface is that JMX allows only one MXBean to be
+ * implemented and implementations can expose additional methods to be exported.<br>
+ * Implementation of {@link ConfigRegistry} is not required to implement this
+ * interface, but is required to export all methods of ConfigRegistry to JMX so
+ * that this interface can be used as a proxy.
+ */
+public interface ConfigRegistryMXBean extends ConfigRegistry {
+
+}
diff --git a/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/jmx/ConfigTransactionControllerMXBean.java b/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/jmx/ConfigTransactionControllerMXBean.java
new file mode 100644 (file)
index 0000000..e5123e2
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * 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.api.jmx;
+
+import org.opendaylight.controller.config.api.ConfigTransactionController;
+
+/**
+ * Those are executed by Jolokia clients on configuration transaction
+ * represented by {@link ConfigMBeanServer} instance. Note: Reason for having
+ * methods in super interface is that JMX allows only one MXBean to be
+ * implemented and implementations can expose additional methods to be exported. <br>
+ * Implementation of {@link ConfigTransactionController} is not required to
+ * implement this interface, but is required to export all methods of
+ * ConfigTransactionController to JMX so that this interface can be used as a
+ * proxy.
+ */
+public interface ConfigTransactionControllerMXBean extends
+        ConfigTransactionController {
+
+}
diff --git a/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/jmx/ObjectNameUtil.java b/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/jmx/ObjectNameUtil.java
new file mode 100644 (file)
index 0000000..8111690
--- /dev/null
@@ -0,0 +1,259 @@
+/*
+ * 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.api.jmx;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import javax.annotation.concurrent.ThreadSafe;
+import javax.management.ObjectName;
+
+import org.opendaylight.controller.config.api.ModuleIdentifier;
+import org.opendaylight.controller.config.api.jmx.constants.ConfigRegistryConstants;
+
+/**
+ * Provides ObjectName creation. Each created ObjectName consists of domain that
+ * is defined as {@link #ON_DOMAIN} and at least one key-value pair. The only
+ * mandatory property is {@link #TYPE_KEY}. All transaction related mbeans have
+ * {@link #TRANSACTION_NAME_KEY} property set.
+ *
+ */
+@ThreadSafe
+public class ObjectNameUtil {
+
+    public static final String ON_DOMAIN = ConfigRegistryConstants.ON_DOMAIN;
+    public static final String MODULE_FACTORY_NAME_KEY = "moduleFactoryName";
+    public static final String INSTANCE_NAME_KEY = "instanceName";
+    public static final String TYPE_KEY = ConfigRegistryConstants.TYPE_KEY;
+    public static final String TYPE_CONFIG_REGISTRY = ConfigRegistryConstants.TYPE_CONFIG_REGISTRY;
+    public static final String TYPE_CONFIG_TRANSACTION = "ConfigTransaction";
+    public static final String TYPE_MODULE = "Module";
+    public static final String TYPE_RUNTIME_BEAN = "RuntimeBean";
+
+    public static final String TRANSACTION_NAME_KEY = "TransactionName";
+
+    public static ObjectName createON(String on) {
+        try {
+            return new ObjectName(on);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static ObjectName createONWithDomainAndType(String type) {
+        return ConfigRegistryConstants.createONWithDomainAndType(type);
+    }
+
+    public static ObjectName createON(String name, String key, String value) {
+        return ConfigRegistryConstants.createON(name, key, value);
+    }
+
+    public static ObjectName createON(String name, Map<String, String> attribs) {
+        Hashtable<String, String> table = new Hashtable<>(attribs);
+        try {
+            return new ObjectName(name, table);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+
+    }
+
+    public static ObjectName createTransactionControllerON(
+            String transactionName) {
+        Map<String, String> onParams = new HashMap<>();
+        onParams.put(TRANSACTION_NAME_KEY, transactionName);
+        onParams.put(TYPE_KEY, TYPE_CONFIG_TRANSACTION);
+        return createON(ON_DOMAIN, onParams);
+    }
+
+    public static ObjectName createTransactionModuleON(String transactionName,
+            ModuleIdentifier moduleIdentifier) {
+        return createTransactionModuleON(transactionName,
+                moduleIdentifier.getFactoryName(),
+                moduleIdentifier.getInstanceName());
+    }
+
+    public static ObjectName createTransactionModuleON(String transactionName,
+            String moduleName, String instanceName) {
+        Map<String, String> onParams = createModuleON(moduleName, instanceName);
+        onParams.put(TRANSACTION_NAME_KEY, transactionName);
+        return createON(ON_DOMAIN, onParams);
+    }
+
+    public static ObjectName createTransactionModuleON(String transactionName,
+            ObjectName on) {
+        return createTransactionModuleON(transactionName, getFactoryName(on),
+                getInstanceName(on));
+    }
+
+    public static ObjectName createReadOnlyModuleON(
+            ModuleIdentifier moduleIdentifier) {
+        return createReadOnlyModuleON(moduleIdentifier.getFactoryName(),
+                moduleIdentifier.getInstanceName());
+    }
+
+    public static ObjectName createReadOnlyModuleON(String moduleName,
+            String instanceName) {
+        Map<String, String> onParams = createModuleON(moduleName, instanceName);
+        return createON(ON_DOMAIN, onParams);
+    }
+
+    private static Map<String, String> createModuleON(String moduleName,
+            String instanceName) {
+        Map<String, String> onParams = new HashMap<>();
+        onParams.put(TYPE_KEY, TYPE_MODULE);
+        onParams.put(MODULE_FACTORY_NAME_KEY, moduleName);
+        onParams.put(INSTANCE_NAME_KEY, instanceName);
+        return onParams;
+    }
+
+    public static String getFactoryName(ObjectName objectName) {
+        return objectName.getKeyProperty(MODULE_FACTORY_NAME_KEY);
+    }
+
+    public static String getInstanceName(ObjectName objectName) {
+        return objectName.getKeyProperty(INSTANCE_NAME_KEY);
+    }
+
+    public static String getTransactionName(ObjectName objectName) {
+        return objectName.getKeyProperty(TRANSACTION_NAME_KEY);
+    }
+
+    public static ObjectName withoutTransactionName(ObjectName on) {
+        if (getTransactionName(on) == null) {
+            throw new IllegalArgumentException(
+                    "Expected ObjectName with transaction:" + on);
+        }
+        if (ON_DOMAIN.equals(on.getDomain()) == false) {
+            throw new IllegalArgumentException("Expected different domain: "
+                    + on);
+        }
+        String moduleName = getFactoryName(on);
+        String instanceName = getInstanceName(on);
+        return createReadOnlyModuleON(moduleName, instanceName);
+    }
+
+    private static void assertDoesNotContain(
+            Map<String, String> additionalProperties, String key) {
+        if (additionalProperties.containsKey(key)) {
+            throw new IllegalArgumentException(
+                    "Map 'additionalProperties' cannot overwrite attribute "
+                            + key);
+        }
+    }
+
+    public static ObjectName createRuntimeBeanName(String moduleName,
+            String instanceName, Map<String, String> additionalProperties) {
+        // check that there is no overwriting of default attributes
+        assertDoesNotContain(additionalProperties, MODULE_FACTORY_NAME_KEY);
+        assertDoesNotContain(additionalProperties, INSTANCE_NAME_KEY);
+        assertDoesNotContain(additionalProperties, TYPE_KEY);
+        assertDoesNotContain(additionalProperties, TRANSACTION_NAME_KEY);
+        Map<String, String> map = new HashMap<>(additionalProperties);
+        map.put(MODULE_FACTORY_NAME_KEY, moduleName);
+        map.put(INSTANCE_NAME_KEY, instanceName);
+        map.put(TYPE_KEY, TYPE_RUNTIME_BEAN);
+        return createON(ON_DOMAIN, map);
+    }
+
+    private static Set<String> blacklist = new HashSet<>(Arrays.asList(
+            MODULE_FACTORY_NAME_KEY, INSTANCE_NAME_KEY, TYPE_KEY));
+
+    public static Map<String, String> getAdditionalPropertiesOfRuntimeBeanName(
+            ObjectName on) {
+        checkType(on, TYPE_RUNTIME_BEAN);
+        Map<String, String> allProperties = getAdditionalProperties(on);
+        Map<String, String> result = new HashMap<>();
+        for (Entry<String, String> entry : allProperties.entrySet()) {
+            if (blacklist.contains(entry.getKey()) == false) {
+                result.put(entry.getKey(), entry.getValue());
+            }
+        }
+        return result;
+    }
+
+    public static Map<String, String> getAdditionalProperties(ObjectName on) {
+        Hashtable<String, String> keyPropertyList = on.getKeyPropertyList();
+        Map<String, String> result = new HashMap<>();
+        for (Entry<String, String> entry : keyPropertyList.entrySet()) {
+            result.put(entry.getKey(), entry.getValue());
+        }
+        return result;
+    }
+
+    public static void checkDomain(ObjectName objectName) {
+        if (!ON_DOMAIN.equals(objectName.getDomain())) {
+            throw new IllegalArgumentException("Wrong domain " + objectName);
+        }
+
+    }
+
+    public static void checkType(ObjectName objectName, String type) {
+        if (!type.equals(objectName.getKeyProperty(TYPE_KEY))) {
+            throw new IllegalArgumentException("Wrong type, expected '" + type
+                    + "', got " + objectName);
+        }
+    }
+
+    public static ObjectName createModulePattern(String moduleName,
+            String instanceName) {
+        if (moduleName == null)
+            moduleName = "*";
+        if (instanceName == null)
+            instanceName = "*";
+        // do not return object names containing transaction name
+        ObjectName namePattern = ObjectNameUtil
+                .createON(ObjectNameUtil.ON_DOMAIN + ":"
+                        + ObjectNameUtil.TYPE_KEY + "="
+                        + ObjectNameUtil.TYPE_MODULE + ","
+                        + ObjectNameUtil.MODULE_FACTORY_NAME_KEY + "="
+                        + moduleName + "," + ""
+                        + ObjectNameUtil.INSTANCE_NAME_KEY + "=" + instanceName);
+        return namePattern;
+    }
+
+    public static ObjectName createModulePattern(String ifcName,
+            String instanceName, String transactionName) {
+        return ObjectNameUtil.createON(ObjectNameUtil.ON_DOMAIN
+                + ":type=Module," + ObjectNameUtil.MODULE_FACTORY_NAME_KEY
+                + "=" + ifcName + "," + ObjectNameUtil.INSTANCE_NAME_KEY + "="
+                + instanceName + "," + ObjectNameUtil.TRANSACTION_NAME_KEY
+                + "=" + transactionName);
+    }
+
+    public static ObjectName createRuntimeBeanPattern(String moduleName,
+            String instanceName) {
+        return ObjectNameUtil.createON(ObjectNameUtil.ON_DOMAIN + ":"
+                + ObjectNameUtil.TYPE_KEY + "="
+                + ObjectNameUtil.TYPE_RUNTIME_BEAN + ","
+                + ObjectNameUtil.MODULE_FACTORY_NAME_KEY + "=" + moduleName
+                + "," + ObjectNameUtil.INSTANCE_NAME_KEY + "=" + instanceName
+                + ",*");
+
+    }
+
+    public static ModuleIdentifier fromON(ObjectName objectName,
+            String expectedType) {
+        checkType(objectName, expectedType);
+        String factoryName = getFactoryName(objectName);
+        if (factoryName == null)
+            throw new IllegalArgumentException(
+                    "ObjectName does not contain module name");
+        String instanceName = getInstanceName(objectName);
+        if (instanceName == null)
+            throw new IllegalArgumentException(
+                    "ObjectName does not contain instance name");
+        return new ModuleIdentifier(factoryName, instanceName);
+    }
+
+}
diff --git a/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/jmx/constants/ConfigRegistryConstants.java b/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/jmx/constants/ConfigRegistryConstants.java
new file mode 100644 (file)
index 0000000..81a29bf
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * 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.api.jmx.constants;
+
+import javax.management.ObjectName;
+
+public class ConfigRegistryConstants {
+
+    public static final String TYPE_CONFIG_REGISTRY = "ConfigRegistry";
+
+    public static final String ON_DOMAIN = "org.opendaylight.controller";
+
+    public static final String TYPE_KEY = "type";
+
+    public static final ObjectName OBJECT_NAME = createONWithDomainAndType(TYPE_CONFIG_REGISTRY);
+
+    public static String GET_AVAILABLE_MODULE_NAMES_ATTRIBUT_NAME = "AvailableModuleNames";
+
+    public static ObjectName createONWithDomainAndType(String type) {
+        return createON(ON_DOMAIN, TYPE_KEY, type);
+    }
+
+    public static ObjectName createON(String name, String key, String value) {
+        try {
+            return new ObjectName(name, key, value);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+
+    }
+}
diff --git a/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/runtime/HierarchicalRuntimeBeanRegistration.java b/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/runtime/HierarchicalRuntimeBeanRegistration.java
new file mode 100644 (file)
index 0000000..185c13c
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * 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.api.runtime;
+
+import javax.management.ObjectName;
+
+public interface HierarchicalRuntimeBeanRegistration extends AutoCloseable {
+
+    ObjectName getObjectName();
+
+    HierarchicalRuntimeBeanRegistration register(String key, String value,
+            RuntimeBean mxBean);
+
+    /**
+     * Unregister beans that were registered using this registrator and its
+     * child registrators. This method is not idempotent, it is not allowed to
+     * use this instance or child registrators after they are closed.
+     */
+    @Override
+    void close();
+
+}
diff --git a/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/runtime/RootRuntimeBeanRegistrator.java b/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/runtime/RootRuntimeBeanRegistrator.java
new file mode 100644 (file)
index 0000000..76949bf
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * 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.api.runtime;
+
+import java.io.Closeable;
+
+/**
+ * Entry point for runtime bean functionality. Allows for registering root
+ * runtime bean, returning an object that allows for hierarchical registrations.
+ */
+public interface RootRuntimeBeanRegistrator extends Closeable {
+
+    HierarchicalRuntimeBeanRegistration registerRoot(RuntimeBean mxBean);
+
+    /**
+     * Close all runtime beans. This method is idempotent. It is allowed to use
+     * this instance to register root or create new child registrators
+     * afterwards, but it is not allowed to use closed registrations.
+     */
+    @Override
+    void close();
+
+}
diff --git a/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/runtime/RuntimeBean.java b/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/runtime/RuntimeBean.java
new file mode 100644 (file)
index 0000000..b7ac773
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ * 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.api.runtime;
+
+/**
+ * Marker interface for all runtime beans
+ */
+
+public interface RuntimeBean {
+}
diff --git a/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/spi/Module.java b/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/spi/Module.java
new file mode 100644 (file)
index 0000000..b11d5f8
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * 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.spi;
+
+import javax.annotation.concurrent.NotThreadSafe;
+
+import org.opendaylight.controller.config.api.ModuleIdentifier;
+import org.opendaylight.controller.config.api.annotations.RequireInterface;
+import org.opendaylight.protocol.concepts.NamedObject;
+
+/**
+ * Represents one service that is to be configured. These methods need to be
+ * implemented in addition to the usual attribute getters/setters. Dependencies
+ * should always be injected as ObjectName references to the corresponding
+ * ConfigBeans.
+ * <p>
+ * In order to guide dependency resolution, the setter method should be
+ * annotated with {@link RequireInterface}.
+ * </p>
+ * <p>
+ * Thread safety note: implementations of this interface are not required to be
+ * thread safe as thread safety is enforced by configuration manager.
+ * </p>
+ */
+@NotThreadSafe
+public interface Module extends NamedObject<ModuleIdentifier> {
+    /**
+     * This method will be called as first phase in two phase commit. Instance
+     * can check attributes, but is not allowed to do any kind of work that
+     * could leave any resources open. It is prohibited to call
+     * {@link #getInstance()} on dependent {@link Module} because it would
+     * destroy separation between validation and commit phase.
+     *
+     */
+    void validate();
+
+    /**
+     * Returns 'live' object that was configured using this object. It is
+     * allowed to call this method only after all ConfigBeans were validated. In
+     * this method new resources might be opened or old instance might be
+     * modified. Note that when obtaining dependent Module using
+     * {@link org.opendaylight.controller.config.api.DependencyResolver#validateDependency(Class, javax.management.ObjectName, String)}
+     * a proxy will be created that will disallow calling this method before
+     * second commit phase begins.
+     *
+     * @return closeable instance: After bundle update the factory might be able
+     *         to copy old configuration into new one without being able to cast
+     *         Module or the instance. Thus to clean up old instance, it will
+     *         call close().
+     */
+    AutoCloseable getInstance();
+
+}
diff --git a/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/spi/ModuleFactory.java b/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/spi/ModuleFactory.java
new file mode 100644 (file)
index 0000000..00db2c2
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * 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.spi;
+
+import javax.management.DynamicMBean;
+
+import org.opendaylight.controller.config.api.DependencyResolver;
+import org.opendaylight.controller.config.api.DynamicMBeanWithInstance;
+import org.opendaylight.controller.config.api.annotations.AbstractServiceInterface;
+
+/**
+ * Factory which creates {@link Module instances. An instance of this interface
+ * needs to be exported into the OSGi Service Registry. Such an instance
+ * provides metadata describing services which can be published from it.
+ *
+ * Each {@link Module } can optionally be instantiated with a
+ * {@link DynamicMBean} which represents the configuration of the currently
+ * running instance.
+ */
+public interface ModuleFactory {
+
+    /**
+     * Returns the human-friendly implementation name. This value needs to be
+     * unique within all implementations of all interfaces returned by
+     * getImplementedInterfaces().
+     *
+     * @return human-friendly implementation name
+     */
+    public String getImplementationName();
+
+    /**
+     * Create a new Module instance. The returned object is expected to use the
+     * dependencyResolver provided when resolving ObjectNames to actual Module
+     * instances.
+     *
+     * @param dependencyResolver
+     *            This resolver will return actual config mbean based on its
+     *            ObjectName.
+     * @return newly created module
+     *
+     */
+    public Module createModule(String instanceName,
+            DependencyResolver dependencyResolver);
+
+    /**
+     * Create a new Module instance. The returned object is expected to use the
+     * dependencyResolver provided when resolving ObjectNames to actual Module
+     * instances. A reference to an abstract view of the previous configuration
+     * is also provided in the form of a {@link DynamicMBean}. Implementations
+     * should use the MBeanInfo interface to understand the structure of the
+     * configuration information.
+     *
+     * Structural information impacts hot-swap operations in that in order to
+     * perform such a swap the newly loaded code needs to understand the
+     * previously-running instance configuration layout and how to map it onto
+     * itself.
+     *
+     * @param dependencyResolver
+     *            This resolver will return actual config mbean based on its
+     *            ObjectName.
+     * @param old
+     *            existing module from platform MBeanServer that is being
+     *            reconfigured. Implementations should inspect its attributes
+     *            using {@link DynamicMBean#getAttribute(String)} and set those
+     *            attributes on newly created module. If reconfiguration of live
+     *            instances is supported, this live instance can be retreived
+     *            using
+     *            {@link org.opendaylight.controller.config.api.DynamicMBeanWithInstance#getInstance()}
+     *            . It is possible that casting this old instance throws
+     *            {@link ClassCastException} when OSGi bundle is being updated.
+     *            In this case, implementation should revert to creating new
+     *            instance.
+     * @return newly created module
+     * @throws Exception
+     *             if it is not possible to recover configuration from old. This
+     *             leaves server in a running state but no configuration
+     *             transaction can be created.
+     */
+    public Module createModule(String instanceName,
+            DependencyResolver dependencyResolver, DynamicMBeanWithInstance old)
+            throws Exception;
+
+    boolean isModuleImplementingServiceInterface(
+            Class<? extends AbstractServiceInterface> serviceInterface);
+
+}
diff --git a/opendaylight/config/config-api/src/main/resources/META-INF/yang/config.yang b/opendaylight/config/config-api/src/main/resources/META-INF/yang/config.yang
new file mode 100644 (file)
index 0000000..5d6c11f
--- /dev/null
@@ -0,0 +1,192 @@
+// vi: set smarttab et sw=4 tabstop=4:
+module config {
+    yang-version 1;
+    namespace "urn:opendaylight:params:xml:ns:yang:controller:config";
+    prefix "config";
+
+
+    description
+         "This module contains the base YANG definitions for NS-OS
+         configuration subsystem. The system modeled revolves around two
+         major concepts: modules and services.";
+
+    revision "2013-04-05" {
+        description
+            "Reworked to give modules their own space.";
+    }
+
+    revision "2013-04-03" {
+        description
+            "Initial revision.";
+    }
+
+    extension java-class {
+        description
+            "YANG language extension carrying the fully-qualified name of
+             a Java class. Code generation tools use the provided reference
+             to tie a specific construct to its Java representation.";
+
+        argument "name";
+    }
+
+    extension required-identity {
+        description
+            "YANG language extension which indicates that a particular
+             leafref, which points to a identityref, should additionally
+             require the target node is actually set to a descendant to
+             of a particular identity.
+
+             This is a workaround to two YANG deficiencies:
+             1) not being able to leafref instances of identityref
+             2) not being able to refine an identityref
+
+             This extension takes one argument, name, which MUST be the name
+             of an identity. Furthermore, that identity MUST be based,
+             directly or indirectly, on the identity, which is referenced by
+             the leaf reference, which is annotated with this extension.";
+
+        argument "name";
+    }
+
+    extension inner-state-bean {
+        description
+            "YANG language extension which indicates that a particular
+             list located under module's state should be treated as a list
+             of child state beans instead of just an ordinary list attribute";
+    }
+
+    extension provided-service {
+        description
+            "YANG language extension which indicates that a particular
+            module provides certain service. This extension can be placed
+            on identities that are based on module-type. Zero or more services
+            can be provided.
+            This extension takes one argument - name - which MUST be the name
+            of an identity. Furthermore, this identity MUST be based on
+            service-type.";
+
+        argument "name";
+    }
+
+    extension java-name-prefix {
+        description
+            "YANG language extension carrying java simple class name prefix
+            that will be taken into account when generating java code from
+            identities that are based on module-type.";
+            argument "java-prefix";
+    }
+
+    identity module-type {
+        description
+            "Module identity base type. All module identities must be derived
+            from this type. A module type uniquely defines a single atomic
+            component, such as an application. Each such component is assumed
+            to have its unique, stable and versioned configuration structure.";
+    }
+
+    identity service-type {
+        description
+            "Service identity base type. All service identities must be
+             derived from this type. A service type uniquely defines a single
+             atomic API contract, such as a Java interface, a set of C
+             function declarations, or similar.
+
+             If the service type has a corresponding Java interface, the name
+             of that interface should be attached to the derived identity MUST
+             include a java-class keyword, whose name argument points to that
+             interface.";
+    }
+
+    typedef service-type-ref {
+        description
+            "Internal type of references to service type identity.";
+
+        type identityref {
+            base service-type;
+        }
+    }
+
+    grouping service-ref {
+        description
+            "Type of references to a particular service instance. This type
+             can be used when defining module configuration to refer to a
+             particular service instance. Containers using this grouping
+             should not define anything else. The run-time implementation
+             is expected to inject a reference to the service as the value
+             of the container.";
+
+        leaf type {
+            description
+                "Type of the service being referenced. Users of this grouping
+                 should refine this leaf with required-identity pointing to
+                 the actual service-type which is actually required.";
+
+            mandatory true;
+            type leafref {
+                path "/config:services/config:service/config:type";
+            }
+        }
+
+        leaf name {
+            mandatory true;
+            type leafref {
+                path "/config:services/config:service[config:type=current()/../type]/config:instance/config:name";
+            }
+        }
+    }
+
+    container modules {
+        description
+            "Top level container encapsulating configuration of all modules.";
+
+        list module {
+            key "name";
+            leaf name {
+                description "Unique module instance name";
+                type string;
+                mandatory true;
+            }
+
+            leaf type {
+                type identityref {
+                    base module-type;
+                }
+                mandatory true;
+            }
+
+            choice configuration {
+                mandatory true;
+                config true;
+            }
+
+            choice state {
+                config false;
+            }
+        }
+    }
+
+
+    container services {
+        list service {
+            key "type";
+            leaf type {
+                type service-type-ref;
+            }
+            list instance {
+                key "name";
+                leaf name {
+                    type string;
+                }
+
+                leaf provider {
+                    mandatory true;
+                    type leafref {
+                        path "/modules/module/name";
+                    }
+                }
+            }
+        }
+    }
+
+
+}
diff --git a/opendaylight/config/config-api/src/main/resources/META-INF/yang/rpc-context.yang b/opendaylight/config/config-api/src/main/resources/META-INF/yang/rpc-context.yang
new file mode 100644 (file)
index 0000000..5c8b113
--- /dev/null
@@ -0,0 +1,32 @@
+module rpc-context {
+    yang-version 1;
+    namespace "urn:ietf:params:xml:ns:yang:rpc-context";
+    prefix "rpcx";
+
+    organization "TBD";
+
+    contact "TBD";
+
+    description "";
+
+    revision 2013-06-17 {
+        description "Initial mock";
+    }
+
+
+    grouping rpc-context-ref {
+        description "A reference to RPC context.";
+        leaf context-instance {
+            type instance-identifier;
+            description "Pointer to the context. ";
+        }
+    }
+
+    extension "rpc-context-instance" {
+        description
+            "Marks enclosing (parent) schema node as suitable RPC context.
+             The argument is identity which is used to identify RPC context
+             type.";
+        argument "context-type";
+    }
+}
diff --git a/opendaylight/config/config-manager/.gitignore b/opendaylight/config/config-manager/.gitignore
new file mode 100644 (file)
index 0000000..fc1d35e
--- /dev/null
@@ -0,0 +1,3 @@
+target
+.classpath
+.settings
diff --git a/opendaylight/config/config-manager/pom.xml b/opendaylight/config/config-manager/pom.xml
new file mode 100644 (file)
index 0000000..eb3dcc1
--- /dev/null
@@ -0,0 +1,115 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <artifactId>config-subsystem</artifactId>
+        <groupId>org.opendaylight</groupId>
+        <version>0.2.1-SNAPSHOT</version>
+        <relativePath>..</relativePath>
+    </parent>
+    <artifactId>config-manager</artifactId>
+    <name>${project.artifactId}</name>
+    <packaging>bundle</packaging>
+
+    <dependencies>
+        <!-- compile dependencies -->
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>config-api</artifactId>
+            <version>0.2.1-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.google.code.findbugs</groupId>
+            <artifactId>jsr305</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.googlecode.json-simple</groupId>
+            <artifactId>json-simple</artifactId>
+            <version>1.1</version>
+        </dependency>
+        <dependency>
+            <groupId>commons-io</groupId>
+            <artifactId>commons-io</artifactId>
+        </dependency>
+
+        <!-- test dependencies -->
+        <dependency>
+            <groupId>org.opendaylight.bgpcep</groupId>
+            <artifactId>mockito-configuration</artifactId>
+            <version>0.2.0-SNAPSHOT</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>config-util</artifactId>
+            <version>0.2.1-SNAPSHOT</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>config-util</artifactId>
+            <version>0.2.1-SNAPSHOT</version>
+            <scope>test</scope>
+            <type>test-jar</type>
+        </dependency>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Bundle-Activator>org.opendaylight.controller.config.manager.impl.osgi.ConfigManagerActivator
+                        </Bundle-Activator>
+                        <Private-Package>
+                            org.opendaylight.controller.config.manager.*,
+                            javax.annotation.*,
+                        </Private-Package>
+                        <Import-Package>
+                            org.opendaylight.controller.config.api.*,
+                            org.opendaylight.controller.config.spi.*,
+                            org.slf4j,
+                            javax.management,
+                            org.osgi.framework,
+                            org.opendaylight.protocol.concepts,
+                            org.apache.commons.io,
+                            org.osgi.util.tracker,
+                        </Import-Package>
+                        <Export-Package>
+                        </Export-Package>
+                    </instructions>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jar-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <forkCount>1</forkCount>
+                    <reuseForks>false</reuseForks>
+                    <perCoreThreadCount>false</perCoreThreadCount>
+                    <threadCount>1</threadCount>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/CommitInfo.java b/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/CommitInfo.java
new file mode 100644 (file)
index 0000000..9d08629
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * 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.manager.impl;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import javax.annotation.concurrent.Immutable;
+
+import org.opendaylight.controller.config.api.ModuleIdentifier;
+
+/**
+ * Structure obtained during first phase commit, contains destroyed modules from
+ * previous transactions that need to be closed and all committed modules with
+ * meta data.
+ */
+@Immutable
+public class CommitInfo {
+    private final List<DestroyedModule> destroyedFromPreviousTransactions;
+    private final Map<ModuleIdentifier, ModuleInternalTransactionalInfo> commitMap;
+
+    public CommitInfo(List<DestroyedModule> destroyedFromPreviousTransactions,
+            Map<ModuleIdentifier, ModuleInternalTransactionalInfo> commitMap) {
+        this.destroyedFromPreviousTransactions = Collections
+                .unmodifiableList(destroyedFromPreviousTransactions);
+        this.commitMap = Collections.unmodifiableMap(commitMap);
+    }
+
+    /**
+     * Get ordered list of modules that can be closed in the same order, i.e.
+     * first element will be a leaf on which no other module depends, n-th
+     * element can only have dependencies with index smaller than n.
+     */
+    public List<DestroyedModule> getDestroyedFromPreviousTransactions() {
+        return destroyedFromPreviousTransactions;
+    }
+
+    public Map<ModuleIdentifier, ModuleInternalTransactionalInfo> getCommitted() {
+        return commitMap;
+    }
+}
diff --git a/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/ConfigRegistryImpl.java b/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/ConfigRegistryImpl.java
new file mode 100644 (file)
index 0000000..97d57a4
--- /dev/null
@@ -0,0 +1,600 @@
+/*
+ * 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.manager.impl;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import javax.annotation.concurrent.GuardedBy;
+import javax.annotation.concurrent.NotThreadSafe;
+import javax.annotation.concurrent.ThreadSafe;
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.InstanceNotFoundException;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.ObjectName;
+
+import org.opendaylight.controller.config.api.ConflictingVersionException;
+import org.opendaylight.controller.config.api.ModuleIdentifier;
+import org.opendaylight.controller.config.api.RuntimeBeanRegistratorAwareModule;
+import org.opendaylight.controller.config.api.ValidationException;
+import org.opendaylight.controller.config.api.jmx.CommitStatus;
+import org.opendaylight.controller.config.api.jmx.ConfigRegistryMXBean;
+import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
+import org.opendaylight.controller.config.manager.impl.dynamicmbean.DynamicReadableWrapper;
+import org.opendaylight.controller.config.manager.impl.factoriesresolver.HierarchicalConfigMBeanFactoriesHolder;
+import org.opendaylight.controller.config.manager.impl.factoriesresolver.ModuleFactoriesResolver;
+import org.opendaylight.controller.config.manager.impl.jmx.BaseJMXRegistrator;
+import org.opendaylight.controller.config.manager.impl.jmx.ModuleJMXRegistrator;
+import org.opendaylight.controller.config.manager.impl.jmx.RootRuntimeBeanRegistratorImpl;
+import org.opendaylight.controller.config.manager.impl.jmx.TransactionJMXRegistrator;
+import org.opendaylight.controller.config.manager.impl.osgi.BeanToOsgiServiceManager;
+import org.opendaylight.controller.config.manager.impl.osgi.BeanToOsgiServiceManager.OsgiRegistration;
+import org.opendaylight.controller.config.manager.impl.util.LookupBeansUtil;
+import org.opendaylight.controller.config.spi.Module;
+import org.osgi.framework.BundleContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Singleton that is responsible for creating and committing Config
+ * Transactions. It is registered in Platform MBean Server.
+ */
+@ThreadSafe
+public class ConfigRegistryImpl implements AutoCloseable,
+        ConfigRegistryImplMXBean {
+    private static final Logger logger = LoggerFactory
+            .getLogger(ConfigRegistryImpl.class);
+
+    private final ModuleFactoriesResolver resolver;
+    private final MBeanServer configMBeanServer;
+    @GuardedBy("this")
+    private long version = 0;
+    @GuardedBy("this")
+    private long versionCounter = 0;
+
+    /**
+     * Contains current configuration in form of {moduleName:{instanceName,read
+     * only module}} for copying state to new transaction. Each running module
+     * is present just once, no matter how many interfaces it exposes.
+     */
+    @GuardedBy("this")
+    private final ConfigHolder currentConfig = new ConfigHolder();
+
+    /**
+     * Will return true unless there was a transaction that succeeded during
+     * validation but failed in second phase of commit. In this case the server
+     * is unstable and its state is undefined.
+     */
+    @GuardedBy("this")
+    private boolean isHealthy = true;
+
+    /**
+     * Holds Map<transactionName, transactionController> and purges it each time
+     * its content is requested.
+     */
+    @GuardedBy("this")
+    private final TransactionsHolder transactionsHolder = new TransactionsHolder();
+
+    private final BaseJMXRegistrator baseJMXRegistrator;
+
+    private final BeanToOsgiServiceManager beanToOsgiServiceManager;
+
+    // internal jmx server for read only beans
+    private final MBeanServer registryMBeanServer;
+    // internal jmx server shared by all transactions
+    private final MBeanServer transactionsMBeanServer;
+
+    // constructor
+    public ConfigRegistryImpl(ModuleFactoriesResolver resolver,
+            BundleContext bundleContext, MBeanServer configMBeanServer) {
+        this(resolver, bundleContext, configMBeanServer,
+                new BaseJMXRegistrator(configMBeanServer));
+    }
+
+    // constructor
+    public ConfigRegistryImpl(ModuleFactoriesResolver resolver,
+            BundleContext bundleContext, MBeanServer configMBeanServer,
+            BaseJMXRegistrator baseJMXRegistrator) {
+        this.resolver = resolver;
+        this.beanToOsgiServiceManager = new BeanToOsgiServiceManager(
+                bundleContext);
+        this.configMBeanServer = configMBeanServer;
+        this.baseJMXRegistrator = baseJMXRegistrator;
+        this.registryMBeanServer = MBeanServerFactory
+                .createMBeanServer("ConfigRegistry" + configMBeanServer.getDefaultDomain());
+        this.transactionsMBeanServer = MBeanServerFactory
+                .createMBeanServer("ConfigTransactions" + configMBeanServer.getDefaultDomain());
+    }
+
+    /**
+     * Create new {@link ConfigTransactionControllerImpl }
+     */
+    @Override
+    public synchronized ObjectName beginConfig() {
+        return beginConfigInternal().getControllerObjectName();
+    }
+
+    private synchronized ConfigTransactionControllerInternal beginConfigInternal() {
+        versionCounter++;
+        String transactionName = "ConfigTransaction-" + version + "-"
+                + versionCounter;
+        TransactionJMXRegistrator transactionRegistrator = baseJMXRegistrator
+                .createTransactionJMXRegistrator(transactionName);
+        ConfigTransactionControllerInternal transactionController = new ConfigTransactionControllerImpl(
+                transactionName, transactionRegistrator, version,
+                versionCounter, resolver.getAllFactories(),
+                transactionsMBeanServer, configMBeanServer);
+        try {
+            transactionRegistrator.registerMBean(transactionController, transactionController.getControllerObjectName
+                    ());
+        } catch (InstanceAlreadyExistsException e) {
+            throw new IllegalStateException(e);
+        }
+
+        // copy old configuration to this server
+        for (ModuleInternalInfo oldConfigInfo : currentConfig.getEntries()) {
+            try {
+                transactionController.copyExistingModule(oldConfigInfo);
+            } catch (InstanceAlreadyExistsException e) {
+                throw new IllegalStateException("Error while copying "
+                        + oldConfigInfo, e);
+            }
+        }
+        transactionsHolder.add(transactionName, transactionController);
+        return transactionController;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public synchronized CommitStatus commitConfig(
+            ObjectName transactionControllerON)
+            throws ConflictingVersionException, ValidationException {
+        final String transactionName = ObjectNameUtil
+                .getTransactionName(transactionControllerON);
+        logger.info(
+                "About to commit {}. Current parentVersion: {}, versionCounter {}",
+                transactionName, version, versionCounter);
+
+        // find ConfigTransactionController
+        Map<String, ConfigTransactionControllerInternal> transactions = transactionsHolder
+                .getCurrentTransactions();
+        ConfigTransactionControllerInternal configTransactionController = transactions
+                .get(transactionName);
+        if (configTransactionController == null) {
+            throw new IllegalArgumentException(String.format(
+                    "Transaction with name '%s' not found", transactionName));
+        }
+        // check optimistic lock
+        if (version != configTransactionController.getParentVersion()) {
+            throw new ConflictingVersionException(
+                    String.format(
+                            "Optimistic lock failed. Expected parent version %d, was %d",
+                            version,
+                            configTransactionController.getParentVersion()));
+        }
+        // optimistic lock ok
+
+        CommitInfo commitInfo = configTransactionController
+                .validateBeforeCommitAndLockTransaction();
+        final ConfigRegistryImpl a = this;
+        // non recoverable from here:
+        try {
+            final CommitStatus secondPhaseCommitStatus = secondPhaseCommit(
+                    configTransactionController, commitInfo);
+
+            return secondPhaseCommitStatus;
+        } catch (Throwable t) { // some libs throw Errors: e.g.
+                                // javax.xml.ws.spi.FactoryFinder$ConfigurationError
+            isHealthy = false;
+            logger.error(
+                    "Configuration Transaction failed on 2PC, server is unhealthy",
+                    t);
+            if (t instanceof RuntimeException)
+                throw (RuntimeException) t;
+            else if (t instanceof Error)
+                throw (Error) t;
+            else
+                throw new RuntimeException(t);
+        }
+    }
+
+    private CommitStatus secondPhaseCommit(
+            ConfigTransactionControllerInternal configTransactionController,
+            CommitInfo commitInfo) {
+
+        // close instances which were destroyed by the user, including
+        // (hopefully) runtime beans
+        for (DestroyedModule toBeDestroyed : commitInfo
+                .getDestroyedFromPreviousTransactions()) {
+            toBeDestroyed.close(); // closes instance (which should close
+                                   // runtime jmx registrator),
+            // also closes osgi registration and ModuleJMXRegistrator
+            // registration
+            currentConfig.remove(toBeDestroyed.getName());
+        }
+
+        // set RuntimeBeanRegistrators on beans implementing
+        // RuntimeBeanRegistratorAwareModule, each module
+        // should have exactly one runtime jmx registrator.
+        Map<ModuleIdentifier, RootRuntimeBeanRegistratorImpl> runtimeRegistrators = new HashMap<>();
+        for (ModuleInternalTransactionalInfo entry : commitInfo.getCommitted()
+                .values()) {
+            RootRuntimeBeanRegistratorImpl runtimeBeanRegistrator;
+            if (entry.hasOldModule() == false) {
+                runtimeBeanRegistrator = baseJMXRegistrator
+                        .createRuntimeBeanRegistrator(entry.getName());
+            } else {
+                // reuse old JMX registrator
+                runtimeBeanRegistrator = entry.getOldInternalInfo()
+                        .getRuntimeBeanRegistrator();
+            }
+            // set runtime jmx registrator if required
+            Module module = entry.getModule();
+            if (module instanceof RuntimeBeanRegistratorAwareModule) {
+                ((RuntimeBeanRegistratorAwareModule) module)
+                        .setRuntimeBeanRegistrator(runtimeBeanRegistrator);
+            }
+            // save it to info so it is accessible afterwards
+            runtimeRegistrators.put(entry.getName(), runtimeBeanRegistrator);
+        }
+
+        // can register runtime beans
+        List<ModuleIdentifier> orderedModuleIdentifiers = configTransactionController
+                .secondPhaseCommit();
+
+        // copy configuration to read only mode
+        List<ObjectName> newInstances = new LinkedList<>();
+        List<ObjectName> reusedInstances = new LinkedList<>();
+        List<ObjectName> recreatedInstances = new LinkedList<>();
+
+        Map<Module, ModuleInternalInfo> newConfigEntries = new HashMap<>();
+
+        int orderingIdx = 0;
+        for (ModuleIdentifier moduleIdentifier : orderedModuleIdentifiers) {
+            ModuleInternalTransactionalInfo entry = commitInfo.getCommitted()
+                    .get(moduleIdentifier);
+            if (entry == null)
+                throw new NullPointerException("Module not found "
+                        + moduleIdentifier);
+            Module module = entry.getModule();
+            ObjectName primaryReadOnlyON = ObjectNameUtil
+                    .createReadOnlyModuleON(moduleIdentifier);
+
+            // determine if current instance was recreated or reused or is new
+
+            // rules for closing resources:
+            // osgi registration - will be (re)created every time, so it needs
+            // to be closed here
+            // module jmx registration - will be (re)created every time, needs
+            // to be closed here
+            // runtime jmx registration - should be taken care of by module
+            // itself
+            // instance - is closed only if it was destroyed
+            ModuleJMXRegistrator newModuleJMXRegistrator = baseJMXRegistrator
+                    .createModuleJMXRegistrator();
+
+            if (entry.hasOldModule()) {
+                ModuleInternalInfo oldInternalInfo = entry.getOldInternalInfo();
+                DynamicReadableWrapper oldReadableConfigBean = oldInternalInfo
+                        .getReadableModule();
+                currentConfig.remove(entry.getName());
+
+                // test if old instance == new instance
+                if (oldReadableConfigBean.getInstance().equals(
+                        module.getInstance())) {
+                    // reused old instance:
+                    // wrap in readable dynamic mbean
+                    reusedInstances.add(primaryReadOnlyON);
+                } else {
+                    // recreated instance:
+                    // it is responsibility of module to call the old instance -
+                    // we just need to unregister configbean
+                    recreatedInstances.add(primaryReadOnlyON);
+                }
+                // close old osgi registration in any case
+                oldInternalInfo.getOsgiRegistration().close();
+                // close old module jmx registrator
+                oldInternalInfo.getModuleJMXRegistrator().close();
+            } else {
+                // new instance:
+                // wrap in readable dynamic mbean
+                newInstances.add(primaryReadOnlyON);
+            }
+
+            DynamicReadableWrapper newReadableConfigBean = new DynamicReadableWrapper(
+                    module, module.getInstance(), moduleIdentifier,
+                    registryMBeanServer, configMBeanServer);
+
+            // register to JMX
+            try {
+                newModuleJMXRegistrator.registerMBean(newReadableConfigBean,
+                        primaryReadOnlyON);
+            } catch (InstanceAlreadyExistsException e) {
+                throw new IllegalStateException(e);
+            }
+
+            // register to OSGi
+            OsgiRegistration osgiRegistration = beanToOsgiServiceManager
+                    .registerToOsgi(module.getClass(),
+                            newReadableConfigBean.getInstance(),
+                            entry.getName());
+
+            RootRuntimeBeanRegistratorImpl runtimeBeanRegistrator = runtimeRegistrators
+                    .get(entry.getName());
+            ModuleInternalInfo newInfo = new ModuleInternalInfo(
+                    entry.getName(), newReadableConfigBean, osgiRegistration,
+                    runtimeBeanRegistrator, newModuleJMXRegistrator,
+                    orderingIdx);
+
+            newConfigEntries.put(module, newInfo);
+            orderingIdx++;
+        }
+        currentConfig.addAll(newConfigEntries.values());
+
+        // update version
+        version = configTransactionController.getVersion();
+        return new CommitStatus(newInstances, reusedInstances,
+                recreatedInstances);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public synchronized List<ObjectName> getOpenConfigs() {
+        Map<String, ConfigTransactionControllerInternal> transactions = transactionsHolder
+                .getCurrentTransactions();
+        List<ObjectName> result = new ArrayList<>(transactions.size());
+        for (ConfigTransactionControllerInternal configTransactionController : transactions
+                .values()) {
+            result.add(configTransactionController.getControllerObjectName());
+        }
+        return result;
+    }
+
+    /**
+     * Abort open transactions and unregister read only modules. Since this
+     * class is not responsible for registering itself under
+     * {@link ConfigRegistryMXBean#OBJECT_NAME}, it will not unregister itself
+     * here.
+     */
+    @Override
+    public synchronized void close() {
+        // abort transactions
+        Map<String, ConfigTransactionControllerInternal> transactions = transactionsHolder
+                .getCurrentTransactions();
+        for (ConfigTransactionControllerInternal configTransactionController : transactions
+                .values()) {
+            try {
+                configTransactionController.abortConfig();
+            } catch (RuntimeException e) {
+                logger.warn("Ignoring exception while aborting {}",
+                        configTransactionController, e);
+            }
+        }
+
+        // destroy all live objects one after another in order of the dependency
+        // hierarchy
+        List<DestroyedModule> destroyedModules = currentConfig
+                .getModulesToBeDestroyed();
+        for (DestroyedModule destroyedModule : destroyedModules) {
+            destroyedModule.close();
+        }
+        // unregister MBeans that failed to unregister properly
+        baseJMXRegistrator.close();
+        // remove jmx servers
+        MBeanServerFactory.releaseMBeanServer(registryMBeanServer);
+        MBeanServerFactory.releaseMBeanServer(transactionsMBeanServer);
+
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public long getVersion() {
+        return version;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Set<String> getAvailableModuleNames() {
+        return new HierarchicalConfigMBeanFactoriesHolder(
+                resolver.getAllFactories()).getModuleNames();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean isHealthy() {
+        return isHealthy;
+    }
+
+    // filtering methods
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Set<ObjectName> lookupConfigBeans() {
+        return lookupConfigBeans("*", "*");
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Set<ObjectName> lookupConfigBeans(String moduleName) {
+        return lookupConfigBeans(moduleName, "*");
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public ObjectName lookupConfigBean(String moduleName, String instanceName)
+            throws InstanceNotFoundException {
+        return LookupBeansUtil.lookupConfigBean(this, moduleName, instanceName);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Set<ObjectName> lookupConfigBeans(String moduleName,
+            String instanceName) {
+        ObjectName namePattern = ObjectNameUtil.createModulePattern(moduleName,
+                instanceName);
+        return baseJMXRegistrator.queryNames(namePattern, null);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Set<ObjectName> lookupRuntimeBeans() {
+        return lookupRuntimeBeans("*", "*");
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Set<ObjectName> lookupRuntimeBeans(String moduleName,
+            String instanceName) {
+        if (moduleName == null)
+            moduleName = "*";
+        if (instanceName == null)
+            instanceName = "*";
+        ObjectName namePattern = ObjectNameUtil.createRuntimeBeanPattern(
+                moduleName, instanceName);
+        return baseJMXRegistrator.queryNames(namePattern, null);
+    }
+
+}
+
+/**
+ * Holds currently running modules
+ */
+@NotThreadSafe
+class ConfigHolder {
+    private final Map<ModuleIdentifier, ModuleInternalInfo> currentConfig = new HashMap<>();
+
+    /**
+     * Add all modules to the internal map. Also add service instance to OSGi
+     * Service Registry.
+     */
+    public void addAll(Collection<ModuleInternalInfo> configInfos) {
+        if (currentConfig.size() > 0) {
+            throw new IllegalStateException(
+                    "Error - some config entries were not removed: "
+                            + currentConfig);
+        }
+        for (ModuleInternalInfo configInfo : configInfos) {
+            add(configInfo);
+        }
+    }
+
+    private void add(ModuleInternalInfo configInfo) {
+        ModuleInternalInfo oldValue = currentConfig.put(configInfo.getName(),
+                configInfo);
+        if (oldValue != null) {
+            throw new IllegalStateException(
+                    "Cannot overwrite module with same name:"
+                            + configInfo.getName() + ":" + configInfo);
+        }
+    }
+
+    /**
+     * Remove entry from current config.
+     */
+    public void remove(ModuleIdentifier name) {
+        ModuleInternalInfo removed = currentConfig.remove(name);
+        if (removed == null) {
+            throw new IllegalStateException(
+                    "Cannot remove from ConfigHolder - name not found:" + name);
+        }
+    }
+
+    public Collection<ModuleInternalInfo> getEntries() {
+        return currentConfig.values();
+    }
+
+    public List<DestroyedModule> getModulesToBeDestroyed() {
+        List<DestroyedModule> result = new ArrayList<>();
+        for (ModuleInternalInfo moduleInternalInfo : getEntries()) {
+            result.add(moduleInternalInfo.toDestroyedModule());
+        }
+        Collections.sort(result);
+        return result;
+    }
+}
+
+/**
+ * Holds Map<transactionName, transactionController> and purges it each time its
+ * content is requested.
+ */
+@NotThreadSafe
+class TransactionsHolder {
+    /**
+     * This map keeps transaction names and
+     * {@link ConfigTransactionControllerInternal} instances, because platform
+     * MBeanServer transforms mbeans into another representation. Map is cleaned
+     * every time current transactions are requested.
+     *
+     */
+    @GuardedBy("ConfigRegistryImpl.this")
+    private final Map<String /* transactionName */, ConfigTransactionControllerInternal> transactions = new HashMap<>();
+
+    /**
+     * Can only be called from within synchronized method.
+     */
+    public void add(String transactionName,
+            ConfigTransactionControllerInternal transactionController) {
+        Object oldValue = transactions.put(transactionName,
+                transactionController);
+        if (oldValue != null) {
+            throw new IllegalStateException(
+                    "Error: two transactions with same name");
+        }
+    }
+
+    /**
+     * Purges closed transactions from transactions map. Can only be called from
+     * within synchronized method. Calling this method more than once within the
+     * method can modify the resulting map that was obtained in previous calls.
+     *
+     * @return current view on transactions map.
+     */
+    public Map<String, ConfigTransactionControllerInternal> getCurrentTransactions() {
+        // first, remove closed transaction
+        for (Iterator<Entry<String, ConfigTransactionControllerInternal>> it = transactions
+                .entrySet().iterator(); it.hasNext();) {
+            Entry<String, ConfigTransactionControllerInternal> entry = it
+                    .next();
+            if (entry.getValue().isClosed()) {
+                it.remove();
+            }
+        }
+        return Collections.unmodifiableMap(transactions);
+    }
+}
diff --git a/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/ConfigRegistryImplMXBean.java b/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/ConfigRegistryImplMXBean.java
new file mode 100644 (file)
index 0000000..bc8b6a5
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * 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.manager.impl;
+
+import org.opendaylight.controller.config.api.ConfigRegistry;
+
+/**
+ * Exposes version of config registry.
+ */
+public interface ConfigRegistryImplMXBean extends ConfigRegistry {
+    /**
+     * @return version of last committed transaction that is now used as base
+     *         version. Transactions can only be committed if their parent
+     *         version matches this value, that means, transaction must be
+     *         started after last one has been committed.
+     */
+    long getVersion();
+
+}
diff --git a/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/ConfigTransactionControllerImpl.java b/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/ConfigTransactionControllerImpl.java
new file mode 100644 (file)
index 0000000..4fe4b31
--- /dev/null
@@ -0,0 +1,401 @@
+/*
+ * 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.manager.impl;
+
+import static java.lang.String.format;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.GuardedBy;
+import javax.management.DynamicMBean;
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.InstanceNotFoundException;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+
+import org.opendaylight.controller.config.api.DependencyResolver;
+import org.opendaylight.controller.config.api.ModuleIdentifier;
+import org.opendaylight.controller.config.api.ValidationException;
+import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
+import org.opendaylight.controller.config.manager.impl.dependencyresolver.DependencyResolverManager;
+import org.opendaylight.controller.config.manager.impl.dynamicmbean.DynamicWritableWrapper;
+import org.opendaylight.controller.config.manager.impl.dynamicmbean.ReadOnlyAtomicBoolean;
+import org.opendaylight.controller.config.manager.impl.dynamicmbean.ReadOnlyAtomicBoolean.ReadOnlyAtomicBooleanImpl;
+import org.opendaylight.controller.config.manager.impl.factoriesresolver.HierarchicalConfigMBeanFactoriesHolder;
+import org.opendaylight.controller.config.manager.impl.jmx.TransactionJMXRegistrator;
+import org.opendaylight.controller.config.manager.impl.jmx.TransactionModuleJMXRegistrator;
+import org.opendaylight.controller.config.manager.impl.jmx.TransactionModuleJMXRegistrator
+        .TransactionModuleJMXRegistration;
+import org.opendaylight.controller.config.manager.impl.util.LookupBeansUtil;
+import org.opendaylight.controller.config.spi.Module;
+import org.opendaylight.controller.config.spi.ModuleFactory;
+import org.opendaylight.protocol.concepts.NamedObject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This is a JMX bean representing current transaction. It contains
+ * {@link #transactionIdentifier}, unique version and parent version for
+ * optimistic locking.
+ */
+class ConfigTransactionControllerImpl implements
+        ConfigTransactionControllerInternal,
+        ConfigTransactionControllerImplMXBean,
+        NamedObject<TransactionIdentifier> {
+    private static final Logger logger = LoggerFactory
+            .getLogger(ConfigTransactionControllerImpl.class);
+
+    private final TransactionIdentifier transactionIdentifier;
+    private final ObjectName controllerON;
+    private final TransactionJMXRegistrator transactionRegistrator;
+    private final TransactionModuleJMXRegistrator txModuleJMXRegistrator;
+    private final long parentVersion, currentVersion;
+    private final HierarchicalConfigMBeanFactoriesHolder factoriesHolder;
+    private final DependencyResolverManager dependencyResolverManager;
+    private final TransactionStatus transactionStatus;
+    private final MBeanServer transactionsMBeanServer;
+
+    /**
+     * Disables ability of {@link DynamicWritableWrapper} to change attributes
+     * during validation.
+     */
+    @GuardedBy("this")
+    private final AtomicBoolean configBeanModificationDisabled = new AtomicBoolean(
+            false);
+    private final ReadOnlyAtomicBoolean readOnlyAtomicBoolean = new ReadOnlyAtomicBooleanImpl(
+            configBeanModificationDisabled);
+    private final MBeanServer configMBeanServer;
+
+    public ConfigTransactionControllerImpl(String transactionName,
+            TransactionJMXRegistrator transactionRegistrator,
+            long parentVersion, long currentVersion,
+            List<? extends ModuleFactory> currentlyRegisteredFactories,
+            MBeanServer transactionsMBeanServer, MBeanServer configMBeanServer) {
+
+        this.transactionIdentifier = new TransactionIdentifier(transactionName);
+        this.controllerON = ObjectNameUtil
+                .createTransactionControllerON(transactionName);
+        this.transactionRegistrator = transactionRegistrator;
+        txModuleJMXRegistrator = transactionRegistrator
+                .createTransactionModuleJMXRegistrator();
+        this.parentVersion = parentVersion;
+        this.currentVersion = currentVersion;
+        this.factoriesHolder = new HierarchicalConfigMBeanFactoriesHolder(
+                currentlyRegisteredFactories);
+        this.transactionStatus = new TransactionStatus();
+        this.dependencyResolverManager = new DependencyResolverManager(
+                transactionName, transactionStatus);
+        this.transactionsMBeanServer = transactionsMBeanServer;
+        this.configMBeanServer = configMBeanServer;
+    }
+
+    @Override
+    public synchronized void copyExistingModule(
+            ModuleInternalInfo oldConfigBeanInfo)
+            throws InstanceAlreadyExistsException {
+        transactionStatus.checkNotCommitStarted();
+        transactionStatus.checkNotAborted();
+        ModuleIdentifier moduleIdentifier = oldConfigBeanInfo.getName();
+        dependencyResolverManager.assertNotExists(moduleIdentifier);
+
+        ModuleFactory moduleFactory = factoriesHolder
+                .findByModuleName(moduleIdentifier.getFactoryName());
+
+        Module module;
+        DependencyResolver dependencyResolver = dependencyResolverManager
+                .getOrCreate(moduleIdentifier);
+        try {
+            module = moduleFactory.createModule(
+                    moduleIdentifier.getInstanceName(), dependencyResolver,
+                    oldConfigBeanInfo.getReadableModule());
+        } catch (Exception e) {
+            throw new IllegalStateException(format(
+                    "Error while copying old configuration from %s to %s",
+                    oldConfigBeanInfo, moduleFactory), e);
+        }
+        putConfigBeanToJMXAndInternalMaps(moduleIdentifier, module,
+                moduleFactory, oldConfigBeanInfo);
+    }
+
+    @Override
+    public synchronized ObjectName createModule(String factoryName,
+            String instanceName) throws InstanceAlreadyExistsException {
+
+        transactionStatus.checkNotCommitStarted();
+        transactionStatus.checkNotAborted();
+        ModuleIdentifier moduleIdentifier = new ModuleIdentifier(factoryName,
+                instanceName);
+        dependencyResolverManager.assertNotExists(moduleIdentifier);
+
+        // find factory
+        ModuleFactory moduleFactory = factoriesHolder
+                .findByModuleName(factoryName);
+        DependencyResolver dependencyResolver = dependencyResolverManager
+                .getOrCreate(moduleIdentifier);
+        Module module = moduleFactory.createModule(instanceName,
+                dependencyResolver);
+        return putConfigBeanToJMXAndInternalMaps(moduleIdentifier, module,
+                moduleFactory, null);
+    }
+
+    private synchronized ObjectName putConfigBeanToJMXAndInternalMaps(
+            ModuleIdentifier moduleIdentifier, Module module,
+            ModuleFactory moduleFactory,
+            @Nullable ModuleInternalInfo maybeOldConfigBeanInfo)
+            throws InstanceAlreadyExistsException {
+
+        DynamicMBean writableDynamicWrapper = new DynamicWritableWrapper(
+                module, moduleIdentifier, transactionIdentifier,
+                readOnlyAtomicBoolean, transactionsMBeanServer,
+                configMBeanServer);
+
+        ObjectName writableON = ObjectNameUtil.createTransactionModuleON(
+                transactionIdentifier.getName(), moduleIdentifier);
+        // put wrapper to jmx
+        TransactionModuleJMXRegistration transactionModuleJMXRegistration = txModuleJMXRegistrator
+                .registerMBean(writableDynamicWrapper, writableON);
+        ModuleInternalTransactionalInfo moduleInternalTransactionalInfo = new ModuleInternalTransactionalInfo(
+                moduleIdentifier, module, moduleFactory,
+                maybeOldConfigBeanInfo, transactionModuleJMXRegistration);
+
+        dependencyResolverManager.put(moduleInternalTransactionalInfo);
+        return writableON;
+    }
+
+    @Override
+    public void destroyModule(ObjectName objectName)
+            throws InstanceNotFoundException {
+        String foundTransactionName = ObjectNameUtil
+                .getTransactionName(objectName);
+        if (transactionIdentifier.getName().equals(foundTransactionName) == false) {
+            throw new IllegalArgumentException("Wrong transaction name "
+                    + objectName);
+        }
+        ObjectNameUtil.checkDomain(objectName);
+        transactionStatus.checkNotAborted();
+        ModuleIdentifier moduleIdentifier = ObjectNameUtil.fromON(objectName,
+                ObjectNameUtil.TYPE_MODULE);
+        ModuleInternalTransactionalInfo removedTInfo = dependencyResolverManager
+                .destroyModule(moduleIdentifier);
+        // remove from jmx
+        removedTInfo.getTransactionModuleJMXRegistration().close();
+    }
+
+    @Override
+    public long getParentVersion() {
+        return parentVersion;
+    }
+
+    @Override
+    public long getVersion() {
+        return currentVersion;
+    }
+
+    @Override
+    public synchronized void validateConfig() throws ValidationException {
+        if (configBeanModificationDisabled.get())
+            throw new IllegalStateException("Cannot start validation");
+        configBeanModificationDisabled.set(true);
+        try {
+            validate_noLocks();
+        } finally {
+            configBeanModificationDisabled.set(false);
+        }
+    }
+
+    private void validate_noLocks() throws ValidationException {
+        transactionStatus.checkNotAborted();
+        logger.info("Validating transaction {}", transactionIdentifier);
+        // call validate()
+        List<ValidationException> collectedExceptions = new ArrayList<>();
+        for (Entry<ModuleIdentifier, Module> entry : dependencyResolverManager
+                .getAllModules().entrySet()) {
+            ModuleIdentifier name = entry.getKey();
+            Module module = entry.getValue();
+            try {
+                module.validate();
+            } catch (Exception e) {
+                logger.warn("Validation exception in {}", getTransactionName(),
+                        e);
+                collectedExceptions.add(ValidationException
+                        .createForSingleException(name, e));
+            }
+        }
+        if (collectedExceptions.size() > 0) {
+            throw ValidationException
+                    .createFromCollectedValidationExceptions(collectedExceptions);
+        }
+        logger.info("Validated transaction {}", transactionIdentifier);
+    }
+
+    /**
+     * If this method passes validation, it will grab
+     * {@link TransactionStatus#secondPhaseCommitStarted} lock. This lock will
+     * prevent calling @{link #validateBeforeCommitAndLockTransaction},
+     * effectively only allowing to call {@link #secondPhaseCommit} after
+     * successful return of this method.
+     */
+    @Override
+    public synchronized CommitInfo validateBeforeCommitAndLockTransaction()
+            throws ValidationException {
+        transactionStatus.checkNotAborted();
+        transactionStatus.checkNotCommitStarted();
+        configBeanModificationDisabled.set(true);
+        try {
+            validate_noLocks();
+        } catch (ValidationException e) {
+            logger.info("Commit failed on validation");
+            configBeanModificationDisabled.set(false); // recoverable error
+            throw e;
+        }
+        // errors in this state are not recoverable. modules are not mutable
+        // anymore.
+        transactionStatus.setSecondPhaseCommitStarted();
+        return dependencyResolverManager.toCommitInfo();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public synchronized List<ModuleIdentifier> secondPhaseCommit() {
+        transactionStatus.checkNotAborted();
+        transactionStatus.checkCommitStarted();
+        if (configBeanModificationDisabled.get() == false) {
+            throw new IllegalStateException(
+                    "Internal error - validateBeforeCommitAndLockTransaction should be called "
+                            + "to obtain a lock");
+        }
+
+        logger.info("Committing transaction {}", transactionIdentifier);
+
+        // call getInstance()
+        for (Entry<ModuleIdentifier, Module> entry : dependencyResolverManager
+                .getAllModules().entrySet()) {
+            Module module = entry.getValue();
+            ModuleIdentifier name = entry.getKey();
+            try {
+                logger.debug("About to commit {} in transaction {}",
+                        transactionIdentifier, name);
+                module.getInstance();
+            } catch (Exception e) {
+                logger.error("Commit failed on {} in transaction {}", name,
+                        transactionIdentifier, e);
+                internalAbort();
+                throw new RuntimeException(
+                        format("Error - getInstance() failed for %s in transaction %s",
+                                name, transactionIdentifier), e);
+            }
+        }
+
+        // count dependency order
+
+        logger.info("Committed configuration {}", transactionIdentifier);
+        transactionStatus.setCommitted();
+        // unregister this and all modules from jmx
+        close();
+
+        return dependencyResolverManager.getSortedModuleIdentifiers();
+    }
+
+    @Override
+    public synchronized void abortConfig() {
+        transactionStatus.checkNotCommitStarted();
+        transactionStatus.checkNotAborted();
+        internalAbort();
+    }
+
+    private void internalAbort() {
+        transactionStatus.setAborted();
+        close();
+    }
+
+    private void close() {
+        transactionRegistrator.close();
+    }
+
+    @Override
+    public ObjectName getControllerObjectName() {
+        return controllerON;
+    }
+
+    @Override
+    public String getTransactionName() {
+        return transactionIdentifier.getName();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Set<ObjectName> lookupConfigBeans() {
+        return lookupConfigBeans("*", "*");
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Set<ObjectName> lookupConfigBeans(String moduleName) {
+        return lookupConfigBeans(moduleName, "*");
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public ObjectName lookupConfigBean(String moduleName, String instanceName)
+            throws InstanceNotFoundException {
+        return LookupBeansUtil.lookupConfigBean(this, moduleName, instanceName);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Set<ObjectName> lookupConfigBeans(String moduleName,
+            String instanceName) {
+        ObjectName namePattern = ObjectNameUtil.createModulePattern(moduleName,
+                instanceName, transactionIdentifier.getName());
+        return txModuleJMXRegistrator.queryNames(namePattern, null);
+    }
+
+    @Override
+    public Set<String> getAvailableModuleNames() {
+        return factoriesHolder.getModuleNames();
+    }
+
+    @Override
+    public boolean isClosed() {
+        return transactionStatus.isAbortedOrCommitted();
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("transactionName=");
+        sb.append(getTransactionName());
+        return sb.toString();
+    }
+
+    // @VisibleForTesting
+
+    TransactionModuleJMXRegistrator getTxModuleJMXRegistrator() {
+        return txModuleJMXRegistrator;
+    }
+
+    @Override
+    public TransactionIdentifier getName() {
+        return transactionIdentifier;
+    }
+}
diff --git a/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/ConfigTransactionControllerImplMXBean.java b/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/ConfigTransactionControllerImplMXBean.java
new file mode 100644 (file)
index 0000000..b35c1dc
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * 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.manager.impl;
+
+import org.opendaylight.controller.config.api.ConfigTransactionController;
+
+/**
+ * Exposes optimistic locking details about transaction.
+ */
+public interface ConfigTransactionControllerImplMXBean extends
+        ConfigTransactionController {
+
+    /**
+     * Get version of config registry when this transaction was created.
+     */
+    long getParentVersion();
+
+    /**
+     * Get version of current transaction.
+     */
+    long getVersion();
+
+}
diff --git a/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/ConfigTransactionControllerInternal.java b/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/ConfigTransactionControllerInternal.java
new file mode 100644 (file)
index 0000000..58d3bc1
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * 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.manager.impl;
+
+import java.util.List;
+
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.ObjectName;
+
+import org.opendaylight.controller.config.api.ModuleIdentifier;
+import org.opendaylight.controller.config.api.ValidationException;
+
+/**
+ * Defines contract between {@link ConfigTransactionControllerImpl} (producer)
+ * and {@link ConfigRegistryImpl} (consumer).
+ */
+interface ConfigTransactionControllerInternal extends
+        ConfigTransactionControllerImplMXBean {
+
+    /**
+     * Copy already committed module to current transaction.
+     */
+    void copyExistingModule(ModuleInternalInfo oldConfigBeanInfo)
+            throws InstanceAlreadyExistsException;
+
+    /**
+     * Call {@link org.opendaylight.controller.config.spi.Module#validate()} on
+     * all beans in transaction. Lock transaction after successful validation.
+     * This method can be called multiple times if validation fails, but cannot
+     * be called after it did not throw exception.
+     *
+     * @throws {@link RuntimeException} if validation fails. It is safe to run
+     *         this method in future
+     * @return CommitInfo
+     */
+    CommitInfo validateBeforeCommitAndLockTransaction()
+            throws ValidationException;
+
+    /**
+     * Call {@link org.opendaylight.controller.config.spi.Module#getInstance()}
+     * on all beans in transaction. This method can be only called once.
+     *
+     * @throws {@link RuntimeException} commit fails, indicates bug in config
+     *         bean
+     * @return ordered list of module identifiers that respects dependency
+     *         order.
+     */
+    List<ModuleIdentifier> secondPhaseCommit();
+
+    /**
+     * @return ObjectName of this transaction controller
+     */
+    ObjectName getControllerObjectName();
+
+    /**
+     * @return true iif transaction was committed or aborted.
+     */
+    boolean isClosed();
+
+}
diff --git a/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/DestroyedModule.java b/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/DestroyedModule.java
new file mode 100644 (file)
index 0000000..47f34e2
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * 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.manager.impl;
+
+import org.opendaylight.controller.config.api.ModuleIdentifier;
+import org.opendaylight.controller.config.manager.impl.jmx.ModuleJMXRegistrator;
+import org.opendaylight.controller.config.manager.impl.osgi.BeanToOsgiServiceManager.OsgiRegistration;
+import org.opendaylight.protocol.concepts.NamedObject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Transfer object representing already committed module that needs to be
+ * destroyed. Implements comparable in order to preserve order in which modules
+ * were created. Module instances should be closed in order defined by the
+ * compareTo method.
+ */
+public class DestroyedModule implements NamedObject<ModuleIdentifier>,
+        AutoCloseable, Comparable<DestroyedModule> {
+    private static final Logger logger = LoggerFactory
+            .getLogger(DestroyedModule.class);
+
+    private final ModuleIdentifier name;
+    private final AutoCloseable instance;
+    private final ModuleJMXRegistrator oldJMXRegistrator;
+    private final OsgiRegistration osgiRegistration;
+    private final int orderingIdx;
+
+    DestroyedModule(ModuleIdentifier name, AutoCloseable instance,
+            ModuleJMXRegistrator oldJMXRegistrator,
+            OsgiRegistration osgiRegistration, int orderingIdx) {
+        this.name = name;
+        this.instance = instance;
+        this.oldJMXRegistrator = oldJMXRegistrator;
+        this.osgiRegistration = osgiRegistration;
+        this.orderingIdx = orderingIdx;
+    }
+
+    @Override
+    public ModuleIdentifier getName() {
+        return name;
+    }
+
+    @Override
+    public void close() {
+        logger.info("Destroying {}", name);
+        try {
+            instance.close();
+        } catch (Exception e) {
+            logger.error("Error while closing instance of {}", name, e);
+        }
+        try {
+            oldJMXRegistrator.close();
+        } catch (Exception e) {
+            logger.error("Error while closing jmx registrator of {}", name, e);
+        }
+        try {
+            osgiRegistration.close();
+        } catch (Exception e) {
+            logger.error("Error while closing osgi registration of {}", name, e);
+        }
+    }
+
+    @Override
+    public int compareTo(DestroyedModule o) {
+        return Integer.compare(orderingIdx, o.orderingIdx);
+    }
+}
diff --git a/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/ModuleInternalInfo.java b/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/ModuleInternalInfo.java
new file mode 100644 (file)
index 0000000..fefc886
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * 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.manager.impl;
+
+import javax.annotation.Nullable;
+
+import org.opendaylight.controller.config.api.ModuleIdentifier;
+import org.opendaylight.controller.config.manager.impl.dynamicmbean.DynamicReadableWrapper;
+import org.opendaylight.controller.config.manager.impl.jmx.ModuleJMXRegistrator;
+import org.opendaylight.controller.config.manager.impl.jmx.RootRuntimeBeanRegistratorImpl;
+import org.opendaylight.controller.config.manager.impl.osgi.BeanToOsgiServiceManager.OsgiRegistration;
+import org.opendaylight.protocol.concepts.NamedObject;
+
+/**
+ * Provides metadata about Module from controller to registry.
+ */
+public class ModuleInternalInfo implements NamedObject<ModuleIdentifier>,
+        Comparable<ModuleInternalInfo> {
+
+    private final ModuleIdentifier name;
+    // this registrator is passed to runtime bean registrator and config
+    // registry to register read only module.
+    // writable modules are registered using TransactionJMXRegistrator
+    @Nullable
+    private final DynamicReadableWrapper readableModule;
+
+    private final RootRuntimeBeanRegistratorImpl runtimeBeanRegistrator;
+    // added when bean instance is registered to Osgi
+    // can be unregistered using this registration
+    private final OsgiRegistration osgiRegistration;
+    private final ModuleJMXRegistrator moduleJMXRegistrator;
+    private final int orderingIdx;
+
+    public ModuleInternalInfo(ModuleIdentifier name,
+            @Nullable DynamicReadableWrapper readableModule,
+            OsgiRegistration osgiRegistration,
+            RootRuntimeBeanRegistratorImpl runtimeBeanRegistrator,
+            ModuleJMXRegistrator moduleJMXRegistrator, int orderingIdx) {
+
+        if (osgiRegistration == null) {
+            throw new IllegalArgumentException(
+                    "Parameter 'osgiRegistration' is missing");
+        }
+        if (runtimeBeanRegistrator == null) {
+            throw new IllegalArgumentException(
+                    "Parameter 'runtimeBeanRegistrator' is missing");
+        }
+        this.readableModule = readableModule;
+        this.osgiRegistration = osgiRegistration;
+        this.runtimeBeanRegistrator = runtimeBeanRegistrator;
+        this.name = name;
+        this.moduleJMXRegistrator = moduleJMXRegistrator;
+        this.orderingIdx = orderingIdx;
+    }
+
+    public DynamicReadableWrapper getReadableModule() {
+        return readableModule;
+    }
+
+    public ModuleJMXRegistrator getModuleJMXRegistrator() {
+        return moduleJMXRegistrator;
+    }
+
+    /**
+     *
+     * @return iif an running instance exists in the system.
+     */
+    public boolean hasReadableModule() {
+        return readableModule != null;
+    }
+
+    @Override
+    public String toString() {
+        return "ModuleInternalInfo [name=" + name + "]";
+    }
+
+    public RootRuntimeBeanRegistratorImpl getRuntimeBeanRegistrator() {
+        return runtimeBeanRegistrator;
+    }
+
+    public OsgiRegistration getOsgiRegistration() {
+        return osgiRegistration;
+    }
+
+    @Override
+    public ModuleIdentifier getName() {
+        return name;
+    }
+
+    /**
+     * Get index representing dependency ordering within a transaction.
+     */
+    public int getOrderingIdx() {
+        return orderingIdx;
+    }
+
+    /**
+     * Compare using orderingIdx
+     */
+    @Override
+    public int compareTo(ModuleInternalInfo o) {
+        return Integer.compare(orderingIdx, o.orderingIdx);
+    }
+
+    public DestroyedModule toDestroyedModule() {
+        return new DestroyedModule(getName(),
+                getReadableModule().getInstance(), getModuleJMXRegistrator(),
+                getOsgiRegistration(), getOrderingIdx());
+    }
+}
diff --git a/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/ModuleInternalTransactionalInfo.java b/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/ModuleInternalTransactionalInfo.java
new file mode 100644 (file)
index 0000000..c71c3bb
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * 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.manager.impl;
+
+import javax.annotation.Nullable;
+
+import org.opendaylight.controller.config.api.ModuleIdentifier;
+import org.opendaylight.controller.config.manager.impl.dynamicmbean.DynamicReadableWrapper;
+import org.opendaylight.controller.config.manager.impl.jmx.TransactionModuleJMXRegistrator;
+import org.opendaylight.controller.config.manager.impl.jmx.TransactionModuleJMXRegistrator
+        .TransactionModuleJMXRegistration;
+import org.opendaylight.controller.config.spi.Module;
+import org.opendaylight.controller.config.spi.ModuleFactory;
+import org.opendaylight.protocol.concepts.NamedObject;
+
+public class ModuleInternalTransactionalInfo implements
+        NamedObject<ModuleIdentifier> {
+    private final ModuleIdentifier name;
+    private final Module module;
+    private final ModuleFactory moduleFactory;
+    @Nullable
+    private final ModuleInternalInfo maybeOldInternalInfo;
+    private final TransactionModuleJMXRegistration transactionModuleJMXRegistration;
+
+    ModuleInternalTransactionalInfo(ModuleIdentifier name, Module module,
+            ModuleFactory moduleFactory,
+            ModuleInternalInfo maybeOldInternalInfo,
+            TransactionModuleJMXRegistration transactionModuleJMXRegistration) {
+        this.name = name;
+        this.module = module;
+        this.moduleFactory = moduleFactory;
+        this.maybeOldInternalInfo = maybeOldInternalInfo;
+        this.transactionModuleJMXRegistration = transactionModuleJMXRegistration;
+    }
+
+    @Override
+    public ModuleIdentifier getName() {
+        return name;
+    }
+
+    public boolean hasOldModule() {
+        return maybeOldInternalInfo != null;
+    }
+
+    public DestroyedModule toDestroyedModule() {
+        if (maybeOldInternalInfo == null) {
+            throw new IllegalStateException("Cannot destoy uncommitted module");
+        }
+        DynamicReadableWrapper oldModule = maybeOldInternalInfo
+                .getReadableModule();
+        return new DestroyedModule(name, oldModule.getInstance(),
+                maybeOldInternalInfo.getModuleJMXRegistrator(),
+                maybeOldInternalInfo.getOsgiRegistration(),
+                maybeOldInternalInfo.getOrderingIdx());
+    }
+
+    public Module getModule() {
+        return module;
+    }
+
+    public ModuleFactory getModuleFactory() {
+        return moduleFactory;
+    }
+
+    @Nullable
+    public ModuleInternalInfo getOldInternalInfo() {
+        if (maybeOldInternalInfo == null)
+            throw new NullPointerException();
+        return maybeOldInternalInfo;
+    }
+
+    public TransactionModuleJMXRegistration getTransactionModuleJMXRegistration() {
+        return transactionModuleJMXRegistration;
+    }
+}
diff --git a/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/TransactionIdentifier.java b/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/TransactionIdentifier.java
new file mode 100644 (file)
index 0000000..3e2b892
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * 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.manager.impl;
+
+import org.opendaylight.protocol.concepts.Identifier;
+
+public class TransactionIdentifier implements Identifier {
+    private final String name;
+
+    public TransactionIdentifier(String name) {
+        this.name = name;
+    }
+
+    @Override
+    public String toString() {
+        return "TransactionIdentifier{" + "name='" + name + '\'' + '}';
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o)
+            return true;
+        if (o == null || getClass() != o.getClass())
+            return false;
+
+        TransactionIdentifier that = (TransactionIdentifier) o;
+
+        if (name != null ? !name.equals(that.name) : that.name != null)
+            return false;
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        return name != null ? name.hashCode() : 0;
+    }
+}
diff --git a/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/TransactionStatus.java b/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/TransactionStatus.java
new file mode 100644 (file)
index 0000000..a250620
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * 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.manager.impl;
+
+import javax.annotation.concurrent.GuardedBy;
+
+public class TransactionStatus {
+    @GuardedBy("this")
+    private boolean secondPhaseCommitStarted = false;
+    // switches to true during abort or commit failure
+    @GuardedBy("this")
+    private boolean aborted;
+    // switches to true during second phase commit
+    @GuardedBy("this")
+    private boolean committed;
+
+    public synchronized boolean isSecondPhaseCommitStarted() {
+        return secondPhaseCommitStarted;
+    }
+
+    synchronized void setSecondPhaseCommitStarted() {
+        this.secondPhaseCommitStarted = true;
+    }
+
+    public synchronized boolean isAborted() {
+        return aborted;
+    }
+
+    synchronized void setAborted() {
+        this.aborted = true;
+    }
+
+    public synchronized boolean isCommitted() {
+        return committed;
+    }
+
+    synchronized void setCommitted() {
+        this.committed = true;
+    }
+
+    public synchronized boolean isAbortedOrCommitted() {
+        return aborted || committed;
+    }
+
+    public synchronized void checkNotCommitStarted() {
+        if (secondPhaseCommitStarted == true)
+            throw new IllegalStateException("Commit was triggered");
+    }
+
+    public synchronized void checkCommitStarted() {
+        if (secondPhaseCommitStarted == false)
+            throw new IllegalStateException("Commit was not triggered");
+    }
+
+    public synchronized void checkNotAborted() {
+        if (aborted == true)
+            throw new IllegalStateException("Configuration was aborted");
+    }
+
+    public synchronized void checkNotCommitted() {
+        if (committed == true) {
+            throw new IllegalStateException(
+                    "Cannot use this method after second phase commit");
+        }
+    }
+
+    public synchronized void checkCommitted() {
+        if (committed == false) {
+            throw new IllegalStateException(
+                    "Cannot use this method before second phase commit");
+        }
+    }
+}
diff --git a/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/dependencyresolver/DependencyResolverImpl.java b/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/dependencyresolver/DependencyResolverImpl.java
new file mode 100644 (file)
index 0000000..87b2e1f
--- /dev/null
@@ -0,0 +1,219 @@
+/*
+ * 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.manager.impl.dependencyresolver;
+
+import static java.lang.String.format;
+
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+import javax.annotation.concurrent.GuardedBy;
+import javax.management.ObjectName;
+
+import org.opendaylight.controller.config.api.DependencyResolver;
+import org.opendaylight.controller.config.api.JmxAttribute;
+import org.opendaylight.controller.config.api.JmxAttributeValidationException;
+import org.opendaylight.controller.config.api.ModuleIdentifier;
+import org.opendaylight.controller.config.api.annotations.AbstractServiceInterface;
+import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
+import org.opendaylight.controller.config.manager.impl.TransactionStatus;
+import org.opendaylight.controller.config.spi.Module;
+import org.opendaylight.controller.config.spi.ModuleFactory;
+import org.opendaylight.protocol.concepts.NamedObject;
+
+/**
+ * Protect {@link org.opendaylight.controller.config.spi.Module#getInstance()}
+ * by creating proxy that would throw exception if those methods are called
+ * during validation. Tracks dependencies for ordering purposes.
+ */
+final class DependencyResolverImpl implements DependencyResolver,
+        NamedObject<ModuleIdentifier>, Comparable<DependencyResolverImpl> {
+    private final ModulesHolder modulesHolder;
+    private final ModuleIdentifier name;
+    private final TransactionStatus transactionStatus;
+    @GuardedBy("this")
+    private final Set<ModuleIdentifier> dependencies = new HashSet<>();
+
+    DependencyResolverImpl(ModuleIdentifier currentModule,
+            TransactionStatus transactionStatus, ModulesHolder modulesHolder) {
+        this.name = currentModule;
+        this.transactionStatus = transactionStatus;
+        this.modulesHolder = modulesHolder;
+    }
+
+    @Override
+    public ModuleIdentifier getName() {
+        return name;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void validateDependency(
+            Class<? extends AbstractServiceInterface> expectedServiceInterface,
+            ObjectName dependentModuleReadOnlyON, JmxAttribute jmxAttribute) {
+
+        transactionStatus.checkNotCommitted();
+        if (expectedServiceInterface == null) {
+            throw new NullPointerException(
+                    "Parameter 'expectedServiceInterface' is null");
+        }
+        if (jmxAttribute == null)
+            throw new NullPointerException("Parameter 'jmxAttribute' is null");
+
+        JmxAttributeValidationException.checkNotNull(dependentModuleReadOnlyON,
+                "is null, " + "expected dependency implementing "
+                        + expectedServiceInterface, jmxAttribute);
+
+        // check that objectName belongs to this transaction - this should be
+        // stripped
+        // in DynamicWritableWrapper
+        boolean hasTransaction = ObjectNameUtil
+                .getTransactionName(dependentModuleReadOnlyON) != null;
+        JmxAttributeValidationException.checkCondition(
+                hasTransaction == false,
+                format("ObjectName should not contain "
+                        + "transaction name. %s set to %s. ", jmxAttribute,
+                        dependentModuleReadOnlyON), jmxAttribute);
+
+        ModuleIdentifier moduleIdentifier = ObjectNameUtil.fromON(dependentModuleReadOnlyON, ObjectNameUtil
+                .TYPE_MODULE);
+
+        ModuleFactory foundFactory = modulesHolder.findModuleFactory(moduleIdentifier, jmxAttribute);
+
+        boolean implementsSI = foundFactory
+                .isModuleImplementingServiceInterface(expectedServiceInterface);
+        if (implementsSI == false) {
+            String message = format(
+                    "Found module factory does not expose expected service interface. "
+                            + "Module name is %s : %s, expected service interface %s, dependent module ON %s , "
+                            + "attribute %s",
+                    foundFactory.getImplementationName(), foundFactory,
+                    expectedServiceInterface, dependentModuleReadOnlyON,
+                    jmxAttribute);
+            throw new JmxAttributeValidationException(message, jmxAttribute);
+        }
+        synchronized (this) {
+            dependencies.add(moduleIdentifier);
+        }
+    }
+
+    @Override
+    public void validateDependency(
+            Class<? extends AbstractServiceInterface> expectedServiceInterface,
+            ObjectName objectName, String attributeNameForErrorReporting) {
+        validateDependency(expectedServiceInterface, objectName,
+                new JmxAttribute(attributeNameForErrorReporting));
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public <T> T resolveInstance(Class<T> expectedType, ObjectName dependentON,
+            JmxAttribute jmxAttribute) {
+        if (expectedType == null || dependentON == null || jmxAttribute == null) {
+            throw new IllegalArgumentException(format(
+                    "Null parameters not allowed, got {} {} {}", expectedType,
+                    dependentON, jmxAttribute));
+        }
+
+        transactionStatus.checkCommitStarted();
+        transactionStatus.checkNotCommitted();
+
+        ModuleIdentifier dependentModuleIdentifier = ObjectNameUtil.fromON(
+                dependentON, ObjectNameUtil.TYPE_MODULE);
+        Module module = modulesHolder.findModule(dependentModuleIdentifier,
+                jmxAttribute);
+        synchronized (this) {
+            dependencies.add(dependentModuleIdentifier);
+        }
+        AutoCloseable instance = module.getInstance();
+        if (instance == null) {
+            String message = format(
+                    "Error while %s resolving instance %s. getInstance() returned null. "
+                            + "Expected type %s , attribute %s", name,
+                    dependentModuleIdentifier, expectedType, jmxAttribute);
+            throw new JmxAttributeValidationException(message, jmxAttribute);
+        }
+        try {
+            T result = expectedType.cast(instance);
+            return result;
+        } catch (ClassCastException e) {
+            String message = format(
+                    "Instance cannot be cast to expected type. Instance class is %s , "
+                            + "expected type %s , attribute %s",
+                    instance.getClass(), expectedType, jmxAttribute);
+            throw new JmxAttributeValidationException(message, e, jmxAttribute);
+        }
+    }
+
+    @Deprecated
+    @Override
+    public <T> T resolveInstance(Class<T> expectedType, ObjectName objectName) {
+        return resolveInstance(expectedType, objectName, new JmxAttribute(
+                "unknown attribute"));
+    }
+
+    @Override
+    public int compareTo(DependencyResolverImpl o) {
+        transactionStatus.checkCommitted();
+        return Integer.compare(getMaxDependencyDepth(),
+                o.getMaxDependencyDepth());
+    }
+
+    private Integer maxDependencyDepth;
+
+    int getMaxDependencyDepth() {
+        if (maxDependencyDepth == null) {
+            throw new IllegalStateException("Dependency depth was not computed");
+        }
+        return maxDependencyDepth;
+    }
+
+    public void countMaxDependencyDepth(DependencyResolverManager manager) {
+        transactionStatus.checkCommitted();
+        if (maxDependencyDepth == null) {
+            maxDependencyDepth = getMaxDepth(this, manager,
+                    new LinkedHashSet<ModuleIdentifier>());
+        }
+    }
+
+    private static int getMaxDepth(DependencyResolverImpl impl,
+            DependencyResolverManager manager,
+            LinkedHashSet<ModuleIdentifier> chainForDetectingCycles) {
+        int maxDepth = 0;
+        LinkedHashSet<ModuleIdentifier> chainForDetectingCycles2 = new LinkedHashSet<>(
+                chainForDetectingCycles);
+        chainForDetectingCycles2.add(impl.getName());
+        for (ModuleIdentifier dependencyName : impl.dependencies) {
+            DependencyResolverImpl dependentDRI = manager
+                    .getOrCreate(dependencyName);
+            if (chainForDetectingCycles2.contains(dependencyName)) {
+                throw new IllegalStateException(format(
+                        "Cycle detected, {} contains {}",
+                        chainForDetectingCycles2, dependencyName));
+            }
+            int subDepth;
+            if (dependentDRI.maxDependencyDepth != null) {
+                subDepth = dependentDRI.maxDependencyDepth;
+            } else {
+                subDepth = getMaxDepth(dependentDRI, manager,
+                        chainForDetectingCycles2);
+                dependentDRI.maxDependencyDepth = subDepth;
+            }
+            if (subDepth + 1 > maxDepth) {
+                maxDepth = subDepth + 1;
+            }
+        }
+        impl.maxDependencyDepth = maxDepth;
+        return maxDepth;
+    }
+}
diff --git a/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/dependencyresolver/DependencyResolverManager.java b/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/dependencyresolver/DependencyResolverManager.java
new file mode 100644 (file)
index 0000000..34b3093
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * 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.manager.impl.dependencyresolver;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.annotation.concurrent.GuardedBy;
+import javax.management.InstanceAlreadyExistsException;
+
+import org.opendaylight.controller.config.api.JmxAttribute;
+import org.opendaylight.controller.config.api.ModuleIdentifier;
+import org.opendaylight.controller.config.manager.impl.CommitInfo;
+import org.opendaylight.controller.config.manager.impl.ModuleInternalTransactionalInfo;
+import org.opendaylight.controller.config.manager.impl.TransactionStatus;
+import org.opendaylight.controller.config.spi.Module;
+import org.opendaylight.controller.config.spi.ModuleFactory;
+
+/**
+ * Holds information about modules being created and destroyed within this
+ * transaction. Observes usage of DependencyResolver within modules to figure
+ * out dependency tree.
+ */
+public class DependencyResolverManager implements TransactionHolder {
+    @GuardedBy("this")
+    private final Map<ModuleIdentifier, DependencyResolverImpl> moduleIdentifiersToDependencyResolverMap = new HashMap<>();
+    private final ModulesHolder modulesHolder;
+    private final TransactionStatus transactionStatus;
+
+    public DependencyResolverManager(String transactionName,
+            TransactionStatus transactionStatus) {
+        this.modulesHolder = new ModulesHolder(transactionName);
+        this.transactionStatus = transactionStatus;
+    }
+
+    public synchronized DependencyResolverImpl getOrCreate(ModuleIdentifier name) {
+        DependencyResolverImpl dependencyResolver = moduleIdentifiersToDependencyResolverMap
+                .get(name);
+        if (dependencyResolver == null) {
+            transactionStatus.checkNotCommitted();
+            dependencyResolver = new DependencyResolverImpl(name,
+                    transactionStatus, modulesHolder);
+            moduleIdentifiersToDependencyResolverMap.put(name,
+                    dependencyResolver);
+        }
+        return dependencyResolver;
+    }
+
+    /**
+     * Get all dependency resolvers, including those that belong to destroyed
+     * things?
+     */
+    private List<DependencyResolverImpl> getAllSorted() {
+        transactionStatus.checkCommitted();
+        List<DependencyResolverImpl> sorted = new ArrayList<>(
+                moduleIdentifiersToDependencyResolverMap.values());
+        for (DependencyResolverImpl dri : sorted) {
+            dri.countMaxDependencyDepth(this);
+        }
+        Collections.sort(sorted);
+        return sorted;
+    }
+
+    public List<ModuleIdentifier> getSortedModuleIdentifiers() {
+        List<ModuleIdentifier> result = new ArrayList<>(
+                moduleIdentifiersToDependencyResolverMap.size());
+        for (DependencyResolverImpl dri : getAllSorted()) {
+            ModuleIdentifier driName = dri.getName();
+            result.add(driName);
+        }
+        return result;
+    }
+
+    @Override
+    public ModuleInternalTransactionalInfo destroyModule(
+            ModuleIdentifier moduleIdentifier) {
+        transactionStatus.checkNotCommitted();
+        ModuleInternalTransactionalInfo found = modulesHolder
+                .destroyModule(moduleIdentifier);
+        moduleIdentifiersToDependencyResolverMap.remove(moduleIdentifier);
+        return found;
+    }
+
+    // protect write access
+    @Override
+    public void put(
+            ModuleInternalTransactionalInfo moduleInternalTransactionalInfo) {
+        transactionStatus.checkNotCommitted();
+        modulesHolder.put(moduleInternalTransactionalInfo);
+    }
+
+    // wrapped methods:
+
+    @Override
+    public CommitInfo toCommitInfo() {
+        return modulesHolder.toCommitInfo();
+    }
+
+    @Override
+    public Module findModule(ModuleIdentifier moduleIdentifier,
+            JmxAttribute jmxAttributeForReporting) {
+        return modulesHolder.findModule(moduleIdentifier,
+                jmxAttributeForReporting);
+    }
+
+    @Override
+    public ModuleFactory findModuleFactory(ModuleIdentifier moduleIdentifier,
+            JmxAttribute jmxAttributeForReporting) {
+        return modulesHolder.findModuleFactory(moduleIdentifier,
+                jmxAttributeForReporting);
+    }
+
+    @Override
+    public Map<ModuleIdentifier, Module> getAllModules() {
+        return modulesHolder.getAllModules();
+    }
+
+    @Override
+    public void assertNotExists(ModuleIdentifier moduleIdentifier)
+            throws InstanceAlreadyExistsException {
+        modulesHolder.assertNotExists(moduleIdentifier);
+    }
+}
diff --git a/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/dependencyresolver/ModulesHolder.java b/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/dependencyresolver/ModulesHolder.java
new file mode 100644 (file)
index 0000000..7747e55
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * 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.manager.impl.dependencyresolver;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.annotation.concurrent.GuardedBy;
+import javax.management.InstanceAlreadyExistsException;
+
+import org.opendaylight.controller.config.api.JmxAttribute;
+import org.opendaylight.controller.config.api.JmxAttributeValidationException;
+import org.opendaylight.controller.config.api.ModuleIdentifier;
+import org.opendaylight.controller.config.manager.impl.CommitInfo;
+import org.opendaylight.controller.config.manager.impl.DestroyedModule;
+import org.opendaylight.controller.config.manager.impl.ModuleInternalTransactionalInfo;
+import org.opendaylight.controller.config.spi.Module;
+import org.opendaylight.controller.config.spi.ModuleFactory;
+
+/**
+ * Represents modules to be committed.
+ */
+class ModulesHolder implements TransactionHolder {
+    private final String transactionName;
+    @GuardedBy("this")
+    private final Map<ModuleIdentifier, ModuleInternalTransactionalInfo> commitMap = new HashMap<>();
+
+    @GuardedBy("this")
+    private final Set<ModuleInternalTransactionalInfo> unorderedDestroyedFromPreviousTransactions = new HashSet<>();
+
+    ModulesHolder(String transactionName) {
+        this.transactionName = transactionName;
+    }
+
+    @Override
+    public CommitInfo toCommitInfo() {
+        List<DestroyedModule> orderedDestroyedFromPreviousTransactions = new ArrayList<>(
+                unorderedDestroyedFromPreviousTransactions.size());
+        for (ModuleInternalTransactionalInfo toBeDestroyed : unorderedDestroyedFromPreviousTransactions) {
+            orderedDestroyedFromPreviousTransactions.add(toBeDestroyed
+                    .toDestroyedModule());
+        }
+        Collections.sort(orderedDestroyedFromPreviousTransactions);
+        return new CommitInfo(orderedDestroyedFromPreviousTransactions,
+                commitMap);
+    }
+
+    private ModuleInternalTransactionalInfo findModuleInternalTransactionalInfo(
+            ModuleIdentifier moduleIdentifier,
+            JmxAttribute jmxAttributeForReporting) {
+        ModuleInternalTransactionalInfo moduleInternalTransactionalInfo = commitMap
+                .get(moduleIdentifier);
+        JmxAttributeValidationException.checkNotNull(
+                moduleInternalTransactionalInfo, "Module " + moduleIdentifier
+                        + "" + " not found in transaction " + transactionName,
+                jmxAttributeForReporting);
+        return moduleInternalTransactionalInfo;
+    }
+
+    @Override
+    public Module findModule(ModuleIdentifier moduleIdentifier,
+            JmxAttribute jmxAttributeForReporting) {
+        return findModuleInternalTransactionalInfo(moduleIdentifier,
+                jmxAttributeForReporting).getModule();
+    }
+
+    @Override
+    public ModuleFactory findModuleFactory(ModuleIdentifier moduleIdentifier,
+            JmxAttribute jmxAttributeForReporting) {
+        return findModuleInternalTransactionalInfo(moduleIdentifier,
+                jmxAttributeForReporting).getModuleFactory();
+    }
+
+    @Override
+    public Map<ModuleIdentifier, Module> getAllModules() {
+        Map<ModuleIdentifier, Module> result = new HashMap<>();
+        for (ModuleInternalTransactionalInfo entry : commitMap.values()) {
+            ModuleIdentifier name = entry.getName();
+            result.put(name, entry.getModule());
+        }
+        return result;
+    }
+
+    @Override
+    public void put(
+            ModuleInternalTransactionalInfo moduleInternalTransactionalInfo) {
+        commitMap.put(moduleInternalTransactionalInfo.getName(),
+                moduleInternalTransactionalInfo);
+    }
+
+    @Override
+    public ModuleInternalTransactionalInfo destroyModule(
+            ModuleIdentifier moduleIdentifier) {
+        ModuleInternalTransactionalInfo found = commitMap
+                .remove(moduleIdentifier);
+        if (found == null)
+            throw new IllegalStateException("Not found:" + moduleIdentifier);
+        if (found.hasOldModule()) {
+            unorderedDestroyedFromPreviousTransactions.add(found);
+        }
+        return found;
+    }
+
+    @Override
+    public void assertNotExists(ModuleIdentifier moduleIdentifier)
+            throws InstanceAlreadyExistsException {
+        if (commitMap.containsKey(moduleIdentifier)) {
+            throw new InstanceAlreadyExistsException(
+                    "There is an instance registered with name "
+                            + moduleIdentifier);
+        }
+    }
+}
diff --git a/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/dependencyresolver/TransactionHolder.java b/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/dependencyresolver/TransactionHolder.java
new file mode 100644 (file)
index 0000000..f81e747
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * 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.manager.impl.dependencyresolver;
+
+import java.util.Map;
+
+import javax.management.InstanceAlreadyExistsException;
+
+import org.opendaylight.controller.config.api.JmxAttribute;
+import org.opendaylight.controller.config.api.ModuleIdentifier;
+import org.opendaylight.controller.config.manager.impl.CommitInfo;
+import org.opendaylight.controller.config.manager.impl.ModuleInternalTransactionalInfo;
+import org.opendaylight.controller.config.spi.Module;
+import org.opendaylight.controller.config.spi.ModuleFactory;
+
+interface TransactionHolder {
+    CommitInfo toCommitInfo();
+
+    Module findModule(ModuleIdentifier moduleIdentifier,
+            JmxAttribute jmxAttributeForReporting);
+
+    ModuleFactory findModuleFactory(ModuleIdentifier moduleIdentifier,
+            JmxAttribute jmxAttributeForReporting);
+
+    Map<ModuleIdentifier, Module> getAllModules();
+
+    void put(ModuleInternalTransactionalInfo moduleInternalTransactionalInfo);
+
+    ModuleInternalTransactionalInfo destroyModule(
+            ModuleIdentifier moduleIdentifier);
+
+    void assertNotExists(ModuleIdentifier moduleIdentifier)
+            throws InstanceAlreadyExistsException;
+
+}
diff --git a/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/dynamicmbean/AbstractDynamicWrapper.java b/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/dynamicmbean/AbstractDynamicWrapper.java
new file mode 100644 (file)
index 0000000..b7c1570
--- /dev/null
@@ -0,0 +1,324 @@
+/*
+ * 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.manager.impl.dynamicmbean;
+
+import static java.lang.String.format;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.management.Attribute;
+import javax.management.AttributeList;
+import javax.management.AttributeNotFoundException;
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.InstanceNotFoundException;
+import javax.management.IntrospectionException;
+import javax.management.ListenerNotFoundException;
+import javax.management.MBeanAttributeInfo;
+import javax.management.MBeanConstructorInfo;
+import javax.management.MBeanException;
+import javax.management.MBeanInfo;
+import javax.management.MBeanNotificationInfo;
+import javax.management.MBeanOperationInfo;
+import javax.management.MBeanRegistrationException;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerDelegate;
+import javax.management.MBeanServerNotification;
+import javax.management.NotCompliantMBeanException;
+import javax.management.Notification;
+import javax.management.NotificationListener;
+import javax.management.ObjectName;
+import javax.management.ReflectionException;
+
+import org.opendaylight.controller.config.api.ModuleIdentifier;
+import org.opendaylight.controller.config.api.annotations.Description;
+import org.opendaylight.controller.config.api.annotations.RequireInterface;
+import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
+import org.opendaylight.controller.config.manager.impl.util.InterfacesHelper;
+import org.opendaylight.controller.config.spi.Module;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Contains common code for readable/rw dynamic mbean wrappers. Routes all
+ * requests (getAttribute, setAttribute, invoke) into the actual instance, but
+ * provides additional functionality - namely it disallows setting attribute on
+ * a read only wrapper.
+ *
+ */
+abstract class AbstractDynamicWrapper implements DynamicMBeanModuleWrapper {
+    private static final Logger logger = LoggerFactory
+            .getLogger(AbstractDynamicWrapper.class);
+
+    protected final boolean writable;
+    protected final Module module;
+
+    private final MBeanInfo mbeanInfo;
+    protected final ObjectName objectNameInternal;
+    protected final Map<String, AttributeHolder> attributeHolderMap;
+    protected final ModuleIdentifier moduleIdentifier;
+    protected final MBeanServer internalServer;
+
+    public AbstractDynamicWrapper(Module module, boolean writable,
+            ModuleIdentifier moduleIdentifier,
+            ObjectName thisWrapperObjectName, MBeanOperationInfo[] dOperations,
+            MBeanServer internalServer, MBeanServer configMBeanServer) {
+
+        this.writable = writable;
+        this.module = module;
+        this.moduleIdentifier = moduleIdentifier;
+        this.internalServer = internalServer;
+        this.objectNameInternal = thisWrapperObjectName;
+        // register the actual instance into an mbean server.
+        registerActualModule(module, thisWrapperObjectName, objectNameInternal,
+                internalServer, configMBeanServer);
+        Set<Class<?>> jmxInterfaces = InterfacesHelper.getMXInterfaces(module
+                .getClass());
+        this.attributeHolderMap = buildMBeanInfo(module, writable,
+                moduleIdentifier, jmxInterfaces, internalServer,
+                objectNameInternal);
+        this.mbeanInfo = generateMBeanInfo(module.getClass().getName(), module,
+                attributeHolderMap, dOperations, jmxInterfaces);
+    }
+
+    /**
+     * Register module into an internal mbean server, attach listener to the
+     * platform mbean server. Wait until this wrapper gets unregistered, in that
+     * case unregister the module and remove listener.
+     */
+    private final NotificationListener registerActualModule(Module module,
+            final ObjectName thisWrapperObjectName,
+            final ObjectName objectNameInternal,
+            final MBeanServer internalServer,
+            final MBeanServer configMBeanServer) {
+
+        try {
+            internalServer.registerMBean(module, objectNameInternal);
+        } catch (InstanceAlreadyExistsException | MBeanRegistrationException
+                | NotCompliantMBeanException | IllegalStateException e) {
+            throw new IllegalStateException(
+                    "Error occured during mbean registration ", e);
+        }
+
+        NotificationListener listener = new NotificationListener() {
+            @Override
+            public void handleNotification(Notification n, Object handback) {
+                if (n instanceof MBeanServerNotification
+                        && n.getType()
+                                .equals(MBeanServerNotification.UNREGISTRATION_NOTIFICATION)) {
+                    if (((MBeanServerNotification) n).getMBeanName().equals(
+                            thisWrapperObjectName)) {
+                        try {
+                            internalServer.unregisterMBean(objectNameInternal);
+                            configMBeanServer.removeNotificationListener(
+                                    MBeanServerDelegate.DELEGATE_NAME, this);
+                        } catch (MBeanRegistrationException
+                                | ListenerNotFoundException
+                                | InstanceNotFoundException e) {
+                            throw new IllegalStateException(e);
+                        }
+                    }
+                }
+            }
+        };
+        try {
+            configMBeanServer.addNotificationListener(
+                    MBeanServerDelegate.DELEGATE_NAME, listener, null, null);
+        } catch (InstanceNotFoundException e) {
+            throw new RuntimeException("Could not add notification listener", e);
+        }
+        return listener;
+    }
+
+    private static MBeanInfo generateMBeanInfo(String className, Module module,
+            Map<String, AttributeHolder> attributeHolderMap,
+            MBeanOperationInfo[] dOperations, Set<Class<?>> jmxInterfaces) {
+
+        String dDescription = findDescription(module.getClass(), jmxInterfaces);
+        MBeanConstructorInfo[] dConstructors = new MBeanConstructorInfo[0];
+        List<MBeanAttributeInfo> attributes = new ArrayList<>(
+                attributeHolderMap.size());
+        for (AttributeHolder attributeHolder : attributeHolderMap.values()) {
+            attributes.add(attributeHolder.toMBeanAttributeInfo());
+        }
+        return new MBeanInfo(className, dDescription,
+                attributes.toArray(new MBeanAttributeInfo[0]), dConstructors,
+                dOperations, new MBeanNotificationInfo[0]);
+    }
+
+    static String findDescription(Class<?> clazz, Set<Class<?>> jmxInterfaces) {
+        List<Description> descriptions = AnnotationsHelper
+                .findClassAnnotationInSuperClassesAndIfcs(clazz, Description.class, jmxInterfaces);
+        return AnnotationsHelper.aggregateDescriptions(descriptions);
+    }
+
+    protected static MBeanOperationInfo[] getEmptyOperations() {
+        return new MBeanOperationInfo[0];
+    }
+
+    // inspect all exported interfaces ending with MXBean, extract getters &
+    // setters into attribute holder
+    private static Map<String, AttributeHolder> buildMBeanInfo(Module module,
+            boolean writable, ModuleIdentifier moduleIdentifier,
+            Set<Class<?>> jmxInterfaces, MBeanServer internalServer,
+            ObjectName internalObjectName) {
+
+        // internal variables for describing MBean elements
+        Set<Method> methods = new HashSet<>();
+
+        for (Class<?> exportedClass : jmxInterfaces) {
+            Method[] ifcMethods = exportedClass.getMethods();
+            methods.addAll(Arrays.asList(ifcMethods));
+        }
+        // TODO: fix reflection, not used
+        MBeanInfo internalInfo;
+        try {
+            internalInfo = internalServer.getMBeanInfo(internalObjectName);
+        } catch (InstanceNotFoundException | ReflectionException
+                | IntrospectionException e) {
+            throw new RuntimeException("MBean info not found", e);
+        }
+
+        Map<String, MBeanAttributeInfo> attributeMap = new HashMap<>();
+        for (MBeanAttributeInfo a : internalInfo.getAttributes()) {
+            attributeMap.put(a.getName(), a);
+        }
+        Map<String, AttributeHolder> attributeHolderMap = new HashMap<>();
+        for (Method method : methods) {
+
+            if (method.getParameterTypes().length == 1
+                    && method.getName().startsWith("set")) {
+                Method setter;
+                String attribName = method.getName().substring(3);
+                try {
+                    setter = module.getClass().getMethod(method.getName(),
+                            method.getParameterTypes());
+                } catch (NoSuchMethodException e) {
+                    throw new RuntimeException("No such method on "
+                            + moduleIdentifier, e);
+                }
+                RequireInterface ifc = AttributeHolder
+                        .findRequireInterfaceAnnotation(setter, jmxInterfaces);
+                String description = null;
+                if (ifc != null) {
+                    description = AttributeHolder.findDescription(setter,
+                            jmxInterfaces);
+                }
+                AttributeHolder attributeHolder = new AttributeHolder(
+                        attribName, module, attributeMap.get(attribName)
+                                .getType(), writable, ifc, description);
+                attributeHolderMap.put(attribName, attributeHolder);
+            }
+        }
+        return attributeHolderMap;
+    }
+
+    // DynamicMBean methods
+
+    @Override
+    public MBeanInfo getMBeanInfo() {
+        return mbeanInfo;
+    }
+
+    @Override
+    public Object getAttribute(String attributeName)
+            throws AttributeNotFoundException, MBeanException,
+            ReflectionException {
+        if (attributeName.equals("MBeanInfo")) {
+            return getMBeanInfo();
+        }
+
+        Object obj = null;
+        try {
+            obj = internalServer
+                    .getAttribute(objectNameInternal, attributeName);
+        } catch (InstanceNotFoundException e) {
+            new MBeanException(e);
+        }
+        if (obj instanceof ObjectName) {
+            AttributeHolder attributeHolder = attributeHolderMap
+                    .get(attributeName);
+            if (attributeHolder.getRequireInterfaceOrNull() != null) {
+                obj = fixObjectName((ObjectName) obj);
+            }
+            return obj;
+        }
+        return obj;
+
+    }
+
+    protected ObjectName fixObjectName(ObjectName on) {
+        if (!ObjectNameUtil.ON_DOMAIN.equals(on.getDomain()))
+            throw new IllegalArgumentException("Wrong domain, expected "
+                    + ObjectNameUtil.ON_DOMAIN + " setter on " + on);
+        // if on contains transaction name, remove it
+        String transactionName = ObjectNameUtil.getTransactionName(on);
+        if (transactionName != null)
+            return ObjectNameUtil.withoutTransactionName(on);
+        else
+            return on;
+    }
+
+    @Override
+    public AttributeList getAttributes(String[] attributes) {
+        AttributeList result = new AttributeList();
+        for (String attributeName : attributes) {
+            try {
+                Object value = getAttribute(attributeName);
+                result.add(new Attribute(attributeName, value));
+
+            } catch (Exception e) {
+                logger.debug("Getting attribute {} failed", attributeName, e);
+            }
+        }
+        return result;
+    }
+
+    @Override
+    public Object invoke(String actionName, Object[] params, String[] signature)
+            throws MBeanException, ReflectionException {
+        if ("getAttribute".equals(actionName) && params.length == 1
+                && signature[0].equals(String.class.getName())) {
+            try {
+                return getAttribute((String) params[0]);
+            } catch (AttributeNotFoundException e) {
+                throw new MBeanException(e, "Attribute not found on "
+                        + moduleIdentifier);
+            }
+        } else if ("getAttributes".equals(actionName) && params.length == 1
+                && signature[0].equals(String[].class.getName())) {
+            return getAttributes((String[]) params[0]);
+        } else if ("setAttributes".equals(actionName) && params.length == 1
+                && signature[0].equals(AttributeList.class.getName())) {
+            return setAttributes((AttributeList) params[0]);
+        } else {
+            logger.debug("Operation not found {} ", actionName);
+            throw new UnsupportedOperationException(
+                    format("Operation not found on %s. Method invoke is only supported for getInstance and getAttribute(s) "
+                            + "method, got actionName %s, params %s, signature %s ",
+                            moduleIdentifier, actionName, params, signature));
+        }
+    }
+
+    @Override
+    public final int hashCode() {
+        return module.hashCode();
+    }
+
+    @Override
+    public final boolean equals(Object other) {
+        return module.equals(other);
+    }
+
+}
diff --git a/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/dynamicmbean/AnnotationsHelper.java b/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/dynamicmbean/AnnotationsHelper.java
new file mode 100644 (file)
index 0000000..64664f7
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * 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.manager.impl.dynamicmbean;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+import org.opendaylight.controller.config.api.annotations.Description;
+
+class AnnotationsHelper {
+
+    /**
+     * Look for annotation specified by annotationType on method. First observe
+     * method's class, then its super classes, then all provided interfaces.
+     * Used for finding @Description and @RequireInterface
+     *
+     * @param <T>
+     *            generic type of annotation
+     * @return list of found annotations
+     */
+    static <T extends Annotation> List<T> findMethodAnnotationInSuperClassesAndIfcs(
+            final Method setter, Class<T> annotationType,
+            Set<Class<?>> inspectedInterfaces) {
+        List<T> result = new ArrayList<T>();
+        Class<?> inspectedClass = setter.getDeclaringClass();
+        do {
+            try {
+                Method foundSetter = inspectedClass.getMethod(setter.getName(),
+                        setter.getParameterTypes());
+                T annotation = foundSetter.getAnnotation(annotationType);
+                if (annotation != null) {
+                    result.add(annotation);
+                }
+                // we need to go deeper
+                inspectedClass = inspectedClass.getSuperclass();
+            } catch (NoSuchMethodException e) {
+                inspectedClass = Object.class; // no need to go further
+            }
+        } while (inspectedClass.equals(Object.class) == false);
+        // inspect interfaces
+        for (Class<?> ifc : inspectedInterfaces) {
+            if (ifc.isInterface() == false) {
+                throw new IllegalArgumentException(ifc + " is not an interface");
+            }
+            try {
+                Method foundSetter = ifc.getMethod(setter.getName(),
+                        setter.getParameterTypes());
+                T annotation = foundSetter.getAnnotation(annotationType);
+                if (annotation != null) {
+                    result.add(annotation);
+                }
+            } catch (NoSuchMethodException e) {
+
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Look for annotation specified by annotationType on type. First observe
+     * class clazz, then its super classes, then all exported interfaces with
+     * their super types. Used for finding @Description of modules.
+     *
+     * @return list of found annotations
+     */
+    static <T extends Annotation> List<T> findClassAnnotationInSuperClassesAndIfcs(
+            Class<?> clazz, Class<T> annotationType, Set<Class<?>> interfaces) {
+        List<T> result = new ArrayList<T>();
+        Class<?> declaringClass = clazz;
+        do {
+            T annotation = declaringClass.getAnnotation(annotationType);
+            if (annotation != null) {
+                result.add(annotation);
+            }
+            declaringClass = declaringClass.getSuperclass();
+        } while (declaringClass.equals(Object.class) == false);
+        // inspect interfaces
+        for (Class<?> ifc : interfaces) {
+            if (ifc.isInterface() == false) {
+                throw new IllegalArgumentException(ifc + " is not an interface");
+            }
+            T annotation = ifc.getAnnotation(annotationType);
+            if (annotation != null) {
+                result.add(annotation);
+            }
+        }
+        return result;
+    }
+
+    /**
+     * @return empty string if no annotation is found, or list of descriptions
+     *         separated by newline
+     */
+    static String aggregateDescriptions(List<Description> descriptions) {
+        StringBuilder builder = new StringBuilder();
+        for (Description d : descriptions) {
+            if (builder.length() != 0) {
+                builder.append("\n");
+            }
+            builder.append(d.value());
+
+        }
+        return builder.toString();
+    }
+}
diff --git a/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/dynamicmbean/AttributeHolder.java b/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/dynamicmbean/AttributeHolder.java
new file mode 100644 (file)
index 0000000..109ab10
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * 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.manager.impl.dynamicmbean;
+
+import java.lang.reflect.Method;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.Immutable;
+import javax.management.MBeanAttributeInfo;
+import javax.management.ObjectName;
+
+import org.opendaylight.controller.config.api.annotations.Description;
+import org.opendaylight.controller.config.api.annotations.RequireInterface;
+
+@Immutable
+class AttributeHolder {
+
+    private final String name;
+    private final String description;
+    private final Object object;
+    private final boolean writable;
+
+    @Nullable
+    private final RequireInterface requireInterfaceAnnotation;
+    private final String attributeType;
+
+    public AttributeHolder(String name, Object object, String returnType,
+            boolean writable,
+            @Nullable RequireInterface requireInterfaceAnnotation,
+            String description) {
+        if (name == null) {
+            throw new NullPointerException();
+        }
+        this.name = name;
+        if (object == null) {
+            throw new NullPointerException();
+        }
+        this.object = object;
+        this.writable = writable;
+        this.requireInterfaceAnnotation = requireInterfaceAnnotation;
+        this.attributeType = returnType;
+        this.description = description;
+    }
+
+    public MBeanAttributeInfo toMBeanAttributeInfo() {
+        MBeanAttributeInfo info = new MBeanAttributeInfo(name, attributeType,
+                description, true, true, false);
+        return info;
+    }
+
+    /**
+     * @return annotation if setter sets ObjectName or ObjectName[], and is
+     *         annotated. Return null otherwise.
+     */
+    RequireInterface getRequireInterfaceOrNull() {
+        return requireInterfaceAnnotation;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public Object getObject() {
+        return object;
+    }
+
+    public String getAttributeType() {
+        return attributeType;
+    }
+
+    public boolean isWritable() {
+        return writable;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    /**
+     * Find @Description annotations in method class and all its exported
+     * interfaces.
+     *
+     * @param setter
+     * @param jmxInterfaces
+     * @return empty string if no annotation is found, or list of descriptions
+     *         separated by newline
+     */
+    static String findDescription(Method setter, Set<Class<?>> jmxInterfaces) {
+        List<Description> descriptions = AnnotationsHelper
+                .findMethodAnnotationInSuperClassesAndIfcs(setter, Description.class, jmxInterfaces);
+        return AnnotationsHelper.aggregateDescriptions(descriptions);
+    }
+
+    /**
+     * Find @RequireInterface annotation by searching method class and all
+     * exported interfaces.
+     *
+     * @param setter
+     * @param inspectedInterfaces
+     * @throws IllegalStateException
+     *             if more than one value is specified by found annotations
+     * @throws IllegalArgumentException
+     *             if set of exported interfaces contains non interface type
+     * @return null if no annotation is found, otherwise return the annotation
+     */
+    static RequireInterface findRequireInterfaceAnnotation(final Method setter,
+            Set<Class<?>> inspectedInterfaces) {
+
+        // only allow setX(ObjectName y) or setX(ObjectName[] y) to continue
+        if (setter.getParameterTypes().length != 1
+                || (setter.getParameterTypes()[0].equals(ObjectName.class) == false && setter
+                        .getParameterTypes()[0].equals(ObjectName[].class) == false)) {
+            return null;
+        }
+
+        List<RequireInterface> foundRequireInterfaces = AnnotationsHelper
+                .findMethodAnnotationInSuperClassesAndIfcs(setter, RequireInterface.class, inspectedInterfaces);
+        // make sure the list if not empty contains always annotation with same
+        // value
+        Set<Class<?>> foundValues = new HashSet<Class<?>>();
+        for (RequireInterface ri : foundRequireInterfaces) {
+            foundValues.add(ri.value());
+        }
+        if (foundValues.size() == 0) {
+            return null;
+        } else if (foundValues.size() > 1) {
+            throw new IllegalStateException("Error finding @RequireInterface. "
+                    + "More than one value specified as required interface "
+                    + foundValues + " of " + setter + " of "
+                    + setter.getDeclaringClass());
+        } else {
+            return foundRequireInterfaces.get(0);
+        }
+    }
+}
diff --git a/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/dynamicmbean/DynamicMBeanModuleWrapper.java b/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/dynamicmbean/DynamicMBeanModuleWrapper.java
new file mode 100644 (file)
index 0000000..d095839
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * 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.manager.impl.dynamicmbean;
+
+import javax.management.DynamicMBean;
+
+/**
+ * Each {@link org.opendaylight.controller.config.spi.Module} in JMX registry
+ * will be wrapped in this class.
+ */
+public interface DynamicMBeanModuleWrapper extends DynamicMBean {
+
+}
diff --git a/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/dynamicmbean/DynamicReadableWrapper.java b/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/dynamicmbean/DynamicReadableWrapper.java
new file mode 100644 (file)
index 0000000..3a24940
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * 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.manager.impl.dynamicmbean;
+
+import javax.management.Attribute;
+import javax.management.AttributeList;
+import javax.management.AttributeNotFoundException;
+import javax.management.InvalidAttributeValueException;
+import javax.management.MBeanException;
+import javax.management.MBeanServer;
+import javax.management.ReflectionException;
+
+import org.opendaylight.controller.config.api.DynamicMBeanWithInstance;
+import org.opendaylight.controller.config.api.ModuleIdentifier;
+import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
+import org.opendaylight.controller.config.spi.Module;
+
+/**
+ * Wraps {@link org.opendaylight.controller.config.spi.Module} in a
+ * {@link DynamicMBeanWithInstance}. Setting attributes is disabled.
+ */
+public class DynamicReadableWrapper extends AbstractDynamicWrapper implements
+        DynamicMBeanWithInstance {
+    private final AutoCloseable instance;
+
+    /**
+     * @param module
+     * @param instance
+     *            for recreating Module.
+     *
+     */
+    public DynamicReadableWrapper(Module module, AutoCloseable instance,
+            ModuleIdentifier moduleIdentifier, MBeanServer internalServer,
+            MBeanServer configMBeanServer) {
+        super(module, false, moduleIdentifier, ObjectNameUtil
+                .createReadOnlyModuleON(moduleIdentifier),
+                getEmptyOperations(), internalServer, configMBeanServer);
+        this.instance = instance;
+    }
+
+    @Override
+    public Module getModule() {
+        return module;
+    }
+
+    @Override
+    public AutoCloseable getInstance() {
+        return instance;
+    }
+
+    @Override
+    public Object invoke(String actionName, Object[] params, String[] signature)
+            throws MBeanException, ReflectionException {
+        if ("getInstance".equals(actionName)
+                && (params == null || params.length == 0)
+                && (signature == null || signature.length == 0)) {
+            return getInstance();
+        }
+        return super.invoke(actionName, params, signature);
+    }
+
+    @Override
+    public Object getAttribute(String attributeName)
+            throws AttributeNotFoundException, MBeanException,
+            ReflectionException {
+        if (attributeName.equals("getInstance")) {
+            return getInstance();
+        }
+        return super.getAttribute(attributeName);
+    }
+
+    @Override
+    public void setAttribute(Attribute attribute)
+            throws AttributeNotFoundException, InvalidAttributeValueException,
+            MBeanException, ReflectionException {
+        throw new UnsupportedOperationException(
+                "setAttributes is not supported on " + moduleIdentifier);
+    }
+
+    @Override
+    public AttributeList setAttributes(AttributeList attributes) {
+        throw new UnsupportedOperationException(
+                "setAttributes is not supported on " + moduleIdentifier);
+    }
+}
diff --git a/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/dynamicmbean/DynamicWritableWrapper.java b/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/dynamicmbean/DynamicWritableWrapper.java
new file mode 100644 (file)
index 0000000..9495ca6
--- /dev/null
@@ -0,0 +1,153 @@
+/*
+ * 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.manager.impl.dynamicmbean;
+
+import java.lang.reflect.Method;
+
+import javax.annotation.concurrent.ThreadSafe;
+import javax.management.Attribute;
+import javax.management.AttributeList;
+import javax.management.AttributeNotFoundException;
+import javax.management.DynamicMBean;
+import javax.management.InstanceNotFoundException;
+import javax.management.InvalidAttributeValueException;
+import javax.management.MBeanException;
+import javax.management.MBeanOperationInfo;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import javax.management.ReflectionException;
+
+import org.opendaylight.controller.config.api.ModuleIdentifier;
+import org.opendaylight.controller.config.api.ValidationException;
+import org.opendaylight.controller.config.api.annotations.RequireInterface;
+import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
+import org.opendaylight.controller.config.manager.impl.TransactionIdentifier;
+import org.opendaylight.controller.config.spi.Module;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Wraps {@link org.opendaylight.controller.config.spi.Module} instance in a
+ * {@link DynamicMBean} interface. Inspects dependency attributes, identified by
+ * ObjectName getter/setter and {@link RequireInterface} annotation. Used to
+ * simplify client calls - to set a dependency, only instance name is needed.
+ * This class creates new writable String attribute for each dependency with
+ * 'Name' suffix backed by the actual ObjectName attribute.
+ * <p>
+ * Thread safety - setting attributes is synchronized on 'this'. Synchronization
+ * of {@link org.opendaylight.controller.config.spi.Module#validate()} and
+ * {@link org.opendaylight.controller.config.spi.Module#getInstance()} is also
+ * guaranteed by
+ * {@link org.opendaylight.controller.config.manager.impl.ConfigTransactionControllerInternal}
+ * so the actual {@link org.opendaylight.controller.config.spi.Module} needs not
+ * to be thread safe.
+ * </p>
+ */
+@ThreadSafe
+public class DynamicWritableWrapper extends AbstractDynamicWrapper {
+    private static final Logger logger = LoggerFactory
+            .getLogger(DynamicWritableWrapper.class);
+
+    private final ReadOnlyAtomicBoolean configBeanModificationDisabled;
+
+    public DynamicWritableWrapper(Module module,
+            ModuleIdentifier moduleIdentifier,
+            TransactionIdentifier transactionIdentifier,
+            ReadOnlyAtomicBoolean configBeanModificationDisabled,
+            MBeanServer internalServer, MBeanServer configMBeanServer) {
+        super(module, true, moduleIdentifier, ObjectNameUtil
+                .createTransactionModuleON(transactionIdentifier.getName(), moduleIdentifier), getOperations(moduleIdentifier),
+                internalServer, configMBeanServer);
+        this.configBeanModificationDisabled = configBeanModificationDisabled;
+    }
+
+    private static MBeanOperationInfo[] getOperations(
+            ModuleIdentifier moduleIdentifier) {
+        Method validationMethod;
+        try {
+            validationMethod = DynamicWritableWrapper.class.getMethod(
+                    "validate", new Class<?>[0]);
+        } catch (NoSuchMethodException e) {
+            throw new IllegalStateException("No such method exception on "
+                    + moduleIdentifier, e);
+        }
+        return new MBeanOperationInfo[] { new MBeanOperationInfo("Validation",
+                validationMethod) };
+    }
+
+    @Override
+    public synchronized void setAttribute(Attribute attribute)
+            throws AttributeNotFoundException, InvalidAttributeValueException,
+            MBeanException, ReflectionException {
+        if (configBeanModificationDisabled.get() == true)
+            throw new IllegalStateException("Operation is not allowed now");
+
+        if (attribute.getName().equals("Attribute")) {
+            setAttribute((Attribute) attribute.getValue());
+            return;
+        }
+
+        try {
+            if (attribute.getValue() instanceof ObjectName) {
+                AttributeHolder attributeHolder = attributeHolderMap
+                        .get(attribute.getName());
+                if (attributeHolder.getRequireInterfaceOrNull() != null) {
+                    attribute = new Attribute(attribute.getName(),
+                            fixObjectName((ObjectName) attribute.getValue()));
+                } else {
+                    attribute = new Attribute(attribute.getName(),
+                            attribute.getValue());
+                }
+            }
+            internalServer.setAttribute(objectNameInternal, attribute);
+        } catch (InstanceNotFoundException e) {
+            throw new MBeanException(e);
+        }
+
+    }
+
+    @Override
+    public AttributeList setAttributes(AttributeList attributes) {
+        AttributeList result = new AttributeList();
+        for (Object attributeObject : attributes) {
+            Attribute attribute = (Attribute) attributeObject;
+            try {
+                setAttribute(attribute);
+                result.add(attribute);
+            } catch (Exception e) {
+                logger.warn("Setting attribute {} failed on {}",
+                        attribute.getName(), moduleIdentifier, e);
+                throw new IllegalArgumentException(
+                        "Setting attribute failed - " + attribute.getName()
+                                + " on " + moduleIdentifier, e);
+            }
+        }
+        return result;
+    }
+
+    @Override
+    public Object invoke(String actionName, Object[] params, String[] signature)
+            throws MBeanException, ReflectionException {
+        if ("validate".equals(actionName)
+                && (params == null || params.length == 0)
+                && (signature == null || signature.length == 0)) {
+            try {
+                validate();
+            } catch (Exception e) {
+                throw ValidationException.createForSingleException(
+                        moduleIdentifier, e);
+            }
+            return Void.TYPE;
+        }
+        return super.invoke(actionName, params, signature);
+    }
+
+    public void validate() {
+        module.validate();
+    }
+}
diff --git a/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/dynamicmbean/ReadOnlyAtomicBoolean.java b/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/dynamicmbean/ReadOnlyAtomicBoolean.java
new file mode 100644 (file)
index 0000000..deccde0
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * 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.manager.impl.dynamicmbean;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+public interface ReadOnlyAtomicBoolean {
+    boolean get();
+
+    public static class ReadOnlyAtomicBooleanImpl implements
+            ReadOnlyAtomicBoolean {
+        private final AtomicBoolean atomicBoolean;
+
+        public ReadOnlyAtomicBooleanImpl(AtomicBoolean atomicBoolean) {
+            super();
+            this.atomicBoolean = atomicBoolean;
+        }
+
+        @Override
+        public boolean get() {
+            return atomicBoolean.get();
+        }
+
+    }
+}
diff --git a/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/factoriesresolver/HierarchicalConfigMBeanFactoriesHolder.java b/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/factoriesresolver/HierarchicalConfigMBeanFactoriesHolder.java
new file mode 100644 (file)
index 0000000..8f1c69e
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * 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.manager.impl.factoriesresolver;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.opendaylight.controller.config.spi.ModuleFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Hold sorted ConfigMBeanFactories by their module names. Check that module
+ * names are globally unique.
+ */
+public class HierarchicalConfigMBeanFactoriesHolder {
+    private static final Logger logger = LoggerFactory
+            .getLogger(HierarchicalConfigMBeanFactoriesHolder.class);
+
+    private final Map<String, ModuleFactory> moduleNamesToConfigBeanFactories;
+    private final Set<String> moduleNames;
+
+    /**
+     * Create instance.
+     *
+     * @throws IllegalArgumentException
+     *             if unique constraint on module names is violated
+     */
+    public HierarchicalConfigMBeanFactoriesHolder(
+            List<? extends ModuleFactory> list) {
+        Map<String, ModuleFactory> moduleNamesToConfigBeanFactories = new HashMap<>();
+        StringBuffer errors = new StringBuffer();
+        for (ModuleFactory factory : list) {
+            String moduleName = factory.getImplementationName();
+            if (moduleName == null || moduleName.isEmpty()) {
+                throw new IllegalStateException(
+                        "Invalid implementation name for " + factory);
+            }
+            logger.debug("Reading factory {} {}", moduleName, factory);
+            String error = null;
+            ModuleFactory conflicting = moduleNamesToConfigBeanFactories
+                    .get(moduleName);
+            if (conflicting != null) {
+                error = String
+                        .format("Module name is not unique. Found two conflicting factories with same name '%s': " +
+                                "\n\t%s\n\t%s\n", moduleName, conflicting, factory);
+
+            }
+
+            if (error == null) {
+                moduleNamesToConfigBeanFactories.put(moduleName, factory);
+            } else {
+                errors.append(error);
+            }
+
+        }
+        if (errors.length() > 0) {
+            throw new IllegalArgumentException(errors.toString());
+        }
+        this.moduleNamesToConfigBeanFactories = Collections
+                .unmodifiableMap(moduleNamesToConfigBeanFactories);
+        moduleNames = Collections.unmodifiableSet(new TreeSet<>(
+                moduleNamesToConfigBeanFactories.keySet()));
+    }
+
+    /**
+     * Get ModuleFactory by their name.
+     *
+     * @throws IllegalArgumentException
+     *             if factory is not found
+     */
+    public ModuleFactory findByModuleName(String moduleName) {
+        ModuleFactory result = moduleNamesToConfigBeanFactories.get(moduleName);
+        if (result == null) {
+            throw new IllegalArgumentException(
+                    "ModuleFactory not found with module name: " + moduleName);
+        }
+        return result;
+    }
+
+    public Set<String> getModuleNames() {
+        return moduleNames;
+    }
+
+}
diff --git a/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/factoriesresolver/ModuleFactoriesResolver.java b/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/factoriesresolver/ModuleFactoriesResolver.java
new file mode 100644 (file)
index 0000000..9678d70
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * 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.manager.impl.factoriesresolver;
+
+import java.util.List;
+
+import org.opendaylight.controller.config.spi.ModuleFactory;
+
+/**
+ * {@link org.opendaylight.controller.config.manager.impl.ConfigTransactionControllerImpl}
+ * receives list of factories using this interface. For testing, this could be
+ * implemented as hard coded list of objects, for OSGi this would look for all
+ * services in OSGi Service Registry are registered under
+ * {@link org.opendaylight.controller.config.spi.ModuleFactory} name.
+ */
+public interface ModuleFactoriesResolver {
+
+    List<? extends ModuleFactory> getAllFactories();
+
+}
diff --git a/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/jmx/BaseJMXRegistrator.java b/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/jmx/BaseJMXRegistrator.java
new file mode 100644 (file)
index 0000000..be64238
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * 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.manager.impl.jmx;
+
+import java.util.Set;
+
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import javax.management.QueryExp;
+
+import org.opendaylight.controller.config.api.ModuleIdentifier;
+
+public class BaseJMXRegistrator implements AutoCloseable {
+
+    private final InternalJMXRegistrator internalJMXRegistrator;
+
+    public BaseJMXRegistrator(MBeanServer configMBeanServer) {
+        internalJMXRegistrator = new InternalJMXRegistrator(configMBeanServer);
+    }
+
+    public BaseJMXRegistrator(InternalJMXRegistrator internalJMXRegistrator) {
+        this.internalJMXRegistrator = internalJMXRegistrator;
+    }
+
+    public TransactionJMXRegistrator createTransactionJMXRegistrator(
+            String transactionName) {
+        return new TransactionJMXRegistrator(
+                internalJMXRegistrator.createChild(), transactionName);
+    }
+
+    public ModuleJMXRegistrator createModuleJMXRegistrator() {
+        return new ModuleJMXRegistrator(internalJMXRegistrator.createChild());
+    }
+
+    public RootRuntimeBeanRegistratorImpl createRuntimeBeanRegistrator(
+            ModuleIdentifier moduleIdentifier) {
+        return new RootRuntimeBeanRegistratorImpl(internalJMXRegistrator,
+                moduleIdentifier);
+    }
+
+    public Set<ObjectName> queryNames(ObjectName name, QueryExp query) {
+        return internalJMXRegistrator.queryNames(name, query);
+    }
+
+    public Set<ObjectName> getRegisteredObjectNames() {
+        return internalJMXRegistrator.getRegisteredObjectNames();
+    }
+
+    @Override
+    public void close() {
+        internalJMXRegistrator.close();
+    }
+}
diff --git a/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/jmx/ConfigRegistryJMXRegistrator.java b/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/jmx/ConfigRegistryJMXRegistrator.java
new file mode 100644 (file)
index 0000000..ec7fc9c
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * 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.manager.impl.jmx;
+
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.MBeanServer;
+
+import org.opendaylight.controller.config.api.jmx.ConfigRegistryMXBean;
+import org.opendaylight.controller.config.manager.impl.ConfigRegistryImplMXBean;
+
+/**
+ * This registrator is used only to register Config Registry to JMX.
+ *
+ */
+public class ConfigRegistryJMXRegistrator implements AutoCloseable {
+    private final InternalJMXRegistrator internalJMXRegistrator;
+
+    public ConfigRegistryJMXRegistrator(MBeanServer configMBeanServer) {
+        internalJMXRegistrator = new InternalJMXRegistrator(configMBeanServer);
+    }
+
+    public AutoCloseable registerToJMX(ConfigRegistryImplMXBean configRegistry)
+            throws InstanceAlreadyExistsException {
+        return internalJMXRegistrator.registerMBean(configRegistry,
+                ConfigRegistryMXBean.OBJECT_NAME);
+    }
+
+    @Override
+    public void close() {
+        internalJMXRegistrator.close();
+    }
+}
diff --git a/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/jmx/HierarchicalRuntimeBeanRegistrationImpl.java b/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/jmx/HierarchicalRuntimeBeanRegistrationImpl.java
new file mode 100644 (file)
index 0000000..49fad61
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * 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.manager.impl.jmx;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.ObjectName;
+
+import org.opendaylight.controller.config.api.ModuleIdentifier;
+import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
+import org.opendaylight.controller.config.api.runtime.HierarchicalRuntimeBeanRegistration;
+import org.opendaylight.controller.config.api.runtime.RuntimeBean;
+
+public class HierarchicalRuntimeBeanRegistrationImpl implements
+        HierarchicalRuntimeBeanRegistration {
+    private final ModuleIdentifier moduleIdentifier;
+    private final InternalJMXRegistrator internalJMXRegistrator;
+    private final Map<String, String> properties;
+
+    public HierarchicalRuntimeBeanRegistrationImpl(
+            ModuleIdentifier moduleIdentifier,
+            InternalJMXRegistrator internalJMXRegistrator,
+            Map<String, String> properties) {
+        this.moduleIdentifier = moduleIdentifier;
+        this.internalJMXRegistrator = internalJMXRegistrator;
+        this.properties = properties;
+    }
+
+    @Override
+    public ObjectName getObjectName() {
+        return ObjectNameUtil.createRuntimeBeanName(
+                moduleIdentifier.getFactoryName(),
+                moduleIdentifier.getInstanceName(), properties);
+    }
+
+    @Override
+    public HierarchicalRuntimeBeanRegistrationImpl register(String key,
+            String value, RuntimeBean mxBean) {
+        Map<String, String> currentProperties = new HashMap<>(properties);
+        currentProperties.put(key, value);
+        ObjectName on = ObjectNameUtil.createRuntimeBeanName(
+                moduleIdentifier.getFactoryName(),
+                moduleIdentifier.getInstanceName(), currentProperties);
+        InternalJMXRegistrator child = internalJMXRegistrator.createChild();
+        try {
+            child.registerMBean(mxBean, on);
+        } catch (InstanceAlreadyExistsException e) {
+            throw RootRuntimeBeanRegistratorImpl.sanitize(e, moduleIdentifier,
+                    on);
+        }
+        return new HierarchicalRuntimeBeanRegistrationImpl(moduleIdentifier,
+                child, currentProperties);
+    }
+
+    @Override
+    public void close() {
+        internalJMXRegistrator.close();
+    }
+}
diff --git a/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/jmx/InternalJMXRegistrator.java b/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/jmx/InternalJMXRegistrator.java
new file mode 100644 (file)
index 0000000..5d77156
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+ * 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.manager.impl.jmx;
+
+import java.io.Closeable;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.annotation.concurrent.GuardedBy;
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.JMX;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import javax.management.QueryExp;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class InternalJMXRegistrator implements Closeable {
+    private static final Logger logger = LoggerFactory
+            .getLogger(InternalJMXRegistrator.class);
+    private final MBeanServer configMBeanServer;
+
+    public InternalJMXRegistrator(MBeanServer configMBeanServer) {
+        this.configMBeanServer = configMBeanServer;
+    }
+
+    static class InternalJMXRegistration implements AutoCloseable {
+        private final InternalJMXRegistrator internalJMXRegistrator;
+        private final ObjectName on;
+
+        InternalJMXRegistration(InternalJMXRegistrator internalJMXRegistrator,
+                ObjectName on) {
+            this.internalJMXRegistrator = internalJMXRegistrator;
+            this.on = on;
+        }
+
+        @Override
+        public void close() {
+            internalJMXRegistrator.unregisterMBean(on);
+        }
+    }
+
+    @GuardedBy("this")
+    private final Set<ObjectName> registeredObjectNames = new HashSet<>();
+    private final List<InternalJMXRegistrator> children = new ArrayList<>();
+
+    public synchronized InternalJMXRegistration registerMBean(Object object,
+            ObjectName on) throws InstanceAlreadyExistsException {
+        try {
+            configMBeanServer.registerMBean(object, on);
+        } catch (InstanceAlreadyExistsException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+        registeredObjectNames.add(on);
+        return new InternalJMXRegistration(this, on);
+    }
+
+    private synchronized void unregisterMBean(ObjectName on) {
+        // first check that on was registered using this instance
+        boolean removed = registeredObjectNames.remove(on);
+        if (!removed)
+            throw new IllegalStateException(
+                    "Cannot unregister - ObjectName not found in 'registeredObjectNames': "
+                            + on);
+        try {
+            configMBeanServer.unregisterMBean(on);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public InternalJMXRegistrator createChild() {
+        InternalJMXRegistrator child = new InternalJMXRegistrator(
+                configMBeanServer);
+        children.add(child);
+        return child;
+    }
+
+    /**
+     * Allow close to be called multiple times.
+     */
+    @Override
+    public synchronized void close() {
+        // close children
+        for (InternalJMXRegistrator child : children) {
+            child.close();
+        }
+        // close registered ONs
+        for (ObjectName on : registeredObjectNames) {
+            try {
+                configMBeanServer.unregisterMBean(on);
+            } catch (Exception e) {
+                logger.warn("Ignoring error while unregistering {}", on, e);
+            }
+        }
+        registeredObjectNames.clear();
+    }
+
+    public <T> T newMBeanProxy(ObjectName objectName, Class<T> interfaceClass) {
+        return JMX.newMBeanProxy(configMBeanServer, objectName, interfaceClass);
+    }
+
+    public <T> T newMBeanProxy(ObjectName objectName, Class<T> interfaceClass,
+            boolean notificationBroadcaster) {
+        return JMX.newMBeanProxy(configMBeanServer, objectName, interfaceClass,
+                notificationBroadcaster);
+    }
+
+    public <T> T newMXBeanProxy(ObjectName objectName, Class<T> interfaceClass) {
+        return JMX
+                .newMXBeanProxy(configMBeanServer, objectName, interfaceClass);
+    }
+
+    public <T> T newMXBeanProxy(ObjectName objectName, Class<T> interfaceClass,
+            boolean notificationBroadcaster) {
+        return JMX.newMXBeanProxy(configMBeanServer, objectName,
+                interfaceClass, notificationBroadcaster);
+    }
+
+    public Set<ObjectName> getRegisteredObjectNames() {
+        return Collections.unmodifiableSet(registeredObjectNames);
+    }
+
+    public Set<ObjectName> queryNames(ObjectName name, QueryExp query) {
+        Set<ObjectName> result = configMBeanServer.queryNames(name, query);
+        // keep only those that were registered using this instance
+        return getSameNames(result);
+    }
+
+    private Set<ObjectName> getSameNames(Set<ObjectName> superSet) {
+        Set<ObjectName> result = new HashSet<>(superSet);
+        result.retainAll(registeredObjectNames);
+        for (InternalJMXRegistrator child : children) {
+            result.addAll(child.getSameNames(superSet));
+        }
+        return result;
+    }
+
+}
diff --git a/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/jmx/ModuleJMXRegistrator.java b/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/jmx/ModuleJMXRegistrator.java
new file mode 100644 (file)
index 0000000..34c0436
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * 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.manager.impl.jmx;
+
+import java.io.Closeable;
+
+import javax.annotation.concurrent.ThreadSafe;
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.ObjectName;
+
+import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
+import org.opendaylight.controller.config.manager.impl.jmx.InternalJMXRegistrator.InternalJMXRegistration;
+
+/**
+ * This subclass is used for registering readable module into JMX, it is also
+ * used as underlying provider in {@link RuntimeBeanRegistratorImpl}. Closing
+ * the instance thus unregisters all JMX beans related to the module excluding
+ * currently open transactions.
+ */
+@ThreadSafe
+public class ModuleJMXRegistrator implements Closeable {
+    private final InternalJMXRegistrator childJMXRegistrator;
+
+    public ModuleJMXRegistrator(InternalJMXRegistrator internalJMXRegistrator) {
+        this.childJMXRegistrator = internalJMXRegistrator.createChild();
+    }
+
+    static class ModuleJMXRegistration implements AutoCloseable {
+        private final InternalJMXRegistration internalJMXRegistration;
+
+        ModuleJMXRegistration(InternalJMXRegistration registration) {
+            this.internalJMXRegistration = registration;
+        }
+
+        @Override
+        public void close() {
+            internalJMXRegistration.close();
+        }
+    }
+
+    public ModuleJMXRegistration registerMBean(Object object, ObjectName on)
+            throws InstanceAlreadyExistsException {
+        ObjectNameUtil.checkType(on, ObjectNameUtil.TYPE_MODULE);
+        if (ObjectNameUtil.getTransactionName(on) != null)
+            throw new IllegalArgumentException(
+                    "Transaction name not expected in " + on);
+        return new ModuleJMXRegistration(childJMXRegistrator.registerMBean(
+                object, on));
+    }
+
+    @Override
+    public void close() {
+        childJMXRegistrator.close();
+    }
+
+}
diff --git a/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/jmx/RootRuntimeBeanRegistratorImpl.java b/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/jmx/RootRuntimeBeanRegistratorImpl.java
new file mode 100644 (file)
index 0000000..55bfbf5
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * 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.manager.impl.jmx;
+
+import java.util.Collections;
+
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.ObjectName;
+
+import org.opendaylight.controller.config.api.ModuleIdentifier;
+import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
+import org.opendaylight.controller.config.api.runtime.RootRuntimeBeanRegistrator;
+import org.opendaylight.controller.config.api.runtime.RuntimeBean;
+
+public class RootRuntimeBeanRegistratorImpl implements
+        RootRuntimeBeanRegistrator {
+    private final InternalJMXRegistrator internalJMXRegistrator;
+    private final ModuleIdentifier moduleIdentifier;
+    private final ObjectName defaultRuntimeON;
+
+    public RootRuntimeBeanRegistratorImpl(
+            InternalJMXRegistrator internalJMXRegistrator,
+            ModuleIdentifier moduleIdentifier) {
+        this.internalJMXRegistrator = internalJMXRegistrator;
+        this.moduleIdentifier = moduleIdentifier;
+        defaultRuntimeON = ObjectNameUtil.createRuntimeBeanName(
+                moduleIdentifier.getFactoryName(),
+                moduleIdentifier.getInstanceName(),
+                Collections.<String, String> emptyMap());
+    }
+
+    @Override
+    public HierarchicalRuntimeBeanRegistrationImpl registerRoot(
+            RuntimeBean mxBean) {
+        try {
+            internalJMXRegistrator.registerMBean(mxBean, defaultRuntimeON);
+        } catch (InstanceAlreadyExistsException e) {
+            throw sanitize(e, moduleIdentifier, defaultRuntimeON);
+        }
+        return new HierarchicalRuntimeBeanRegistrationImpl(moduleIdentifier,
+                internalJMXRegistrator, Collections.<String, String> emptyMap());
+    }
+
+    @Override
+    public void close() {
+        internalJMXRegistrator.close();
+    }
+
+    static IllegalStateException sanitize(InstanceAlreadyExistsException e,
+            ModuleIdentifier moduleIdentifier, ObjectName on) {
+        throw new IllegalStateException("Could not register runtime bean in "
+                + moduleIdentifier + " under name " + on, e);
+
+    }
+}
diff --git a/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/jmx/TransactionJMXRegistrator.java b/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/jmx/TransactionJMXRegistrator.java
new file mode 100644 (file)
index 0000000..523cbc5
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * 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.manager.impl.jmx;
+
+import java.io.Closeable;
+import java.util.Set;
+
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.ObjectName;
+import javax.management.QueryExp;
+
+import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
+import org.opendaylight.controller.config.manager.impl.jmx.InternalJMXRegistrator.InternalJMXRegistration;
+
+/**
+ * Contains constraints on passed {@link ObjectName} parameters. Only allow (un)
+ * registration of ObjectNames that have expected transaction name.
+ */
+public class TransactionJMXRegistrator implements Closeable {
+    private final InternalJMXRegistrator childJMXRegistrator;
+    private final String transactionName;
+
+    TransactionJMXRegistrator(InternalJMXRegistrator internalJMXRegistrator,
+            String transactionName) {
+        this.childJMXRegistrator = internalJMXRegistrator.createChild();
+        this.transactionName = transactionName;
+    }
+
+    public static class TransactionJMXRegistration implements AutoCloseable {
+        private final InternalJMXRegistration registration;
+
+        TransactionJMXRegistration(InternalJMXRegistration registration) {
+            this.registration = registration;
+        }
+
+        @Override
+        public void close() {
+            registration.close();
+        }
+    }
+
+    public TransactionJMXRegistration registerMBean(Object object, ObjectName on)
+            throws InstanceAlreadyExistsException {
+        if (!transactionName.equals(ObjectNameUtil.getTransactionName(on)))
+            throw new IllegalArgumentException(
+                    "Transaction name mismatch between expected "
+                            + transactionName + " " + "and " + on);
+        ObjectNameUtil.checkType(on, ObjectNameUtil.TYPE_CONFIG_TRANSACTION);
+        return new TransactionJMXRegistration(
+                childJMXRegistrator.registerMBean(object, on));
+    }
+
+    public Set<ObjectName> queryNames(ObjectName name, QueryExp query) {
+        return childJMXRegistrator.queryNames(name, query);
+    }
+
+    public TransactionModuleJMXRegistrator createTransactionModuleJMXRegistrator() {
+        return new TransactionModuleJMXRegistrator(childJMXRegistrator,
+                transactionName);
+    }
+
+    @Override
+    public void close() {
+        childJMXRegistrator.close();
+    }
+}
diff --git a/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/jmx/TransactionModuleJMXRegistrator.java b/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/jmx/TransactionModuleJMXRegistrator.java
new file mode 100644 (file)
index 0000000..546adb0
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * 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.manager.impl.jmx;
+
+import java.io.Closeable;
+import java.util.Set;
+
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.ObjectName;
+import javax.management.QueryExp;
+
+import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
+import org.opendaylight.controller.config.manager.impl.jmx.InternalJMXRegistrator.InternalJMXRegistration;
+
+public class TransactionModuleJMXRegistrator implements Closeable {
+    private final InternalJMXRegistrator childJMXRegistrator;
+    private final String transactionName;
+
+    public TransactionModuleJMXRegistrator(
+            InternalJMXRegistrator internalJMXRegistrator,
+            String transactionName) {
+        this.childJMXRegistrator = internalJMXRegistrator.createChild();
+        this.transactionName = transactionName;
+    }
+
+    public static class TransactionModuleJMXRegistration implements
+            AutoCloseable {
+        private final InternalJMXRegistration registration;
+
+        TransactionModuleJMXRegistration(InternalJMXRegistration registration) {
+            this.registration = registration;
+        }
+
+        @Override
+        public void close() {
+            registration.close();
+        }
+    }
+
+    public TransactionModuleJMXRegistration registerMBean(Object object,
+            ObjectName on) throws InstanceAlreadyExistsException {
+        if (!transactionName.equals(ObjectNameUtil.getTransactionName(on)))
+            throw new IllegalArgumentException(
+                    "Transaction name mismatch between expected "
+                            + transactionName + " " + "and " + on);
+        ObjectNameUtil.checkType(on, ObjectNameUtil.TYPE_MODULE);
+        return new TransactionModuleJMXRegistration(
+                childJMXRegistrator.registerMBean(object, on));
+    }
+
+    public Set<ObjectName> queryNames(ObjectName name, QueryExp query) {
+        return childJMXRegistrator.queryNames(name, query);
+    }
+
+    @Override
+    public void close() {
+        childJMXRegistrator.close();
+    }
+}
diff --git a/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/osgi/BeanToOsgiServiceManager.java b/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/osgi/BeanToOsgiServiceManager.java
new file mode 100644 (file)
index 0000000..42f8b87
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * 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.manager.impl.osgi;
+
+import java.util.Dictionary;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Set;
+
+import org.opendaylight.controller.config.api.ModuleIdentifier;
+import org.opendaylight.controller.config.api.annotations.ServiceInterfaceAnnotation;
+import org.opendaylight.controller.config.manager.impl.util.InterfacesHelper;
+import org.opendaylight.controller.config.spi.Module;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * Registers instantiated beans as OSGi services and unregisters these services
+ * if beans are destroyed.
+ */
+public class BeanToOsgiServiceManager {
+    // name of properties submitted to osgi
+    static final String INSTANCE_NAME_OSGI_PROP = "instanceName";
+    static final String IMPLEMENTATION_NAME_OSGI_PROP = "implementationName";
+
+    private final BundleContext bundleContext;
+
+    public BeanToOsgiServiceManager(BundleContext context) {
+        this.bundleContext = context;
+    }
+
+    /**
+     * To be called for every created, reconfigured and recreated config bean.
+     * It is expected that before using this method OSGi service registry will
+     * be cleaned from previous registrations.
+     */
+    public OsgiRegistration registerToOsgi(
+            Class<? extends Module> configBeanClass, AutoCloseable instance,
+            ModuleIdentifier moduleIdentifier) {
+        try {
+            final Set<Class<?>> configuresInterfaces = InterfacesHelper
+                    .getOsgiRegistrationTypes(configBeanClass);
+            checkInstanceImplementing(instance, configuresInterfaces);
+
+            // bundleContext.registerService blows up with empty 'clazzes'
+            if (configuresInterfaces.isEmpty() == false) {
+                final Dictionary<String, ?> propertiesForOsgi = getPropertiesForOsgi(moduleIdentifier);
+                final ServiceRegistration<?> serviceRegistration = bundleContext
+                        .registerService(classesToNames(configuresInterfaces), instance, propertiesForOsgi);
+                return new OsgiRegistration(serviceRegistration);
+            } else {
+                return new OsgiRegistration();
+            }
+        } catch (IllegalStateException e) {
+            throw new IllegalStateException(
+                    "Error while registering instance into OSGi Service Registry: "
+                            + moduleIdentifier, e);
+        }
+    }
+
+    private static String[] classesToNames(Set<Class<?>> cfgs) {
+        String[] result = new String[cfgs.size()];
+        int i = 0;
+        for (Class<?> cfg : cfgs) {
+            result[i] = cfg.getName();
+            i++;
+        }
+        return result;
+    }
+
+    private void checkInstanceImplementing(AutoCloseable instance,
+            Set<Class<?>> configures) {
+        Set<Class<?>> missing = new HashSet<>();
+        for (Class<?> requiredIfc : configures) {
+            if (requiredIfc.isInstance(instance) == false) {
+                missing.add(requiredIfc);
+            }
+        }
+        if (missing.isEmpty() == false) {
+            throw new IllegalStateException(
+                    instance.getClass()
+                            + " does not implement following interfaces as announced by "
+                            + ServiceInterfaceAnnotation.class.getName()
+                            + " annotation :" + missing);
+        }
+    }
+
+    private static Dictionary<String, ?> getPropertiesForOsgi(
+            ModuleIdentifier moduleIdentifier) {
+        Hashtable<String, String> table = new Hashtable<>();
+        table.put(IMPLEMENTATION_NAME_OSGI_PROP,
+                moduleIdentifier.getFactoryName());
+        table.put(INSTANCE_NAME_OSGI_PROP, moduleIdentifier.getInstanceName());
+        return table;
+    }
+
+    public static class OsgiRegistration implements AutoCloseable {
+        private final ServiceRegistration<?> serviceRegistration;
+
+        public OsgiRegistration(ServiceRegistration<?> serviceRegistration) {
+            this.serviceRegistration = serviceRegistration;
+        }
+
+        public OsgiRegistration() {
+            this.serviceRegistration = null;
+        }
+
+        @Override
+        public void close() {
+            if (serviceRegistration != null) {
+                serviceRegistration.unregister();
+            }
+        }
+    }
+
+}
diff --git a/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/osgi/BundleContextBackedModuleFactoriesResolver.java b/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/osgi/BundleContextBackedModuleFactoriesResolver.java
new file mode 100644 (file)
index 0000000..77bfc49
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * 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.manager.impl.osgi;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.opendaylight.controller.config.manager.impl.factoriesresolver.ModuleFactoriesResolver;
+import org.opendaylight.controller.config.spi.ModuleFactory;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * Retrieves list of currently registered Module Factories using bundlecontext.
+ */
+public class BundleContextBackedModuleFactoriesResolver implements
+        ModuleFactoriesResolver {
+    private final BundleContext bundleContext;
+
+    public BundleContextBackedModuleFactoriesResolver(
+            BundleContext bundleContext) {
+        this.bundleContext = bundleContext;
+    }
+
+    @Override
+    public List<? extends ModuleFactory> getAllFactories() {
+        Collection<ServiceReference<ModuleFactory>> serviceReferences;
+        try {
+            serviceReferences = bundleContext.getServiceReferences(
+                    ModuleFactory.class, null);
+        } catch (InvalidSyntaxException e) {
+            throw new IllegalStateException(e);
+        }
+        List<ModuleFactory> result = new ArrayList<>(serviceReferences.size());
+        for (ServiceReference<ModuleFactory> serviceReference : serviceReferences) {
+            ModuleFactory service = bundleContext.getService(serviceReference);
+            // null if the service is not registered, the service object
+            // returned by a ServiceFactory does not
+            // implement the classes under which it was registered or the
+            // ServiceFactory threw an exception.
+            if (service != null) {
+                result.add(service);
+            }
+        }
+        return result;
+    }
+}
diff --git a/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/osgi/ConfigManagerActivator.java b/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/osgi/ConfigManagerActivator.java
new file mode 100644 (file)
index 0000000..81b9ea9
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * 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.manager.impl.osgi;
+
+import java.lang.management.ManagementFactory;
+
+import javax.management.MBeanServer;
+
+import org.opendaylight.controller.config.manager.impl.ConfigRegistryImpl;
+import org.opendaylight.controller.config.manager.impl.jmx.ConfigRegistryJMXRegistrator;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ConfigManagerActivator implements BundleActivator {
+    private static final Logger logger = LoggerFactory
+            .getLogger(ConfigManagerActivator.class);
+
+    private ExtenderBundleTracker extenderBundleTracker;
+    private ConfigRegistryImpl configRegistry;
+    private ConfigRegistryJMXRegistrator configRegistryJMXRegistrator;
+
+    @Override
+    public void start(BundleContext context) throws Exception {
+        extenderBundleTracker = new ExtenderBundleTracker(context);
+        extenderBundleTracker.open();
+        BundleContextBackedModuleFactoriesResolver bundleContextBackedModuleFactoriesResolver = new BundleContextBackedModuleFactoriesResolver(
+                context);
+
+        MBeanServer configMBeanServer = ManagementFactory
+                .getPlatformMBeanServer();
+        configRegistry = new ConfigRegistryImpl(
+                bundleContextBackedModuleFactoriesResolver, context,
+                configMBeanServer);
+        // register config registry to jmx
+
+        configRegistryJMXRegistrator = new ConfigRegistryJMXRegistrator(
+                configMBeanServer);
+        configRegistryJMXRegistrator.registerToJMX(configRegistry);
+    }
+
+    @Override
+    public void stop(BundleContext context) throws Exception {
+        try {
+            configRegistry.close();
+        } catch (Exception e) {
+            logger.warn("Exception while closing config registry", e);
+        }
+        try {
+            extenderBundleTracker.close();
+        } catch (Exception e) {
+            logger.warn("Exception while closing extender", e);
+        }
+        try {
+            configRegistryJMXRegistrator.close();
+        } catch (Exception e) {
+            logger.warn(
+                    "Exception while closing config registry jmx registrator",
+                    e);
+        }
+    }
+}
diff --git a/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/osgi/ExtenderBundleTracker.java b/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/osgi/ExtenderBundleTracker.java
new file mode 100644 (file)
index 0000000..22a1216
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * 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.manager.impl.osgi;
+
+import static java.lang.String.format;
+
+import java.io.InputStream;
+import java.net.URL;
+import java.util.List;
+
+import org.apache.commons.io.IOUtils;
+import org.opendaylight.controller.config.spi.ModuleFactory;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.util.tracker.BundleTracker;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * OSGi extender that listens for bundle activation events. Reads file
+ * META-INF/services/org.opendaylight.controller.config.spi.ModuleFactory, each
+ * line should contain an implementation of ModuleFactory interface. Creates new
+ * instance with default constructor and registers it into OSGi service
+ * registry. There is no need for listening for implementing removedBundle as
+ * the services are unregistered automatically. Code based on
+ * http://www.toedter.com/blog/?p=236
+ */
+
+public class ExtenderBundleTracker extends BundleTracker<Object> {
+
+    private static final Logger logger = LoggerFactory
+            .getLogger(ExtenderBundleTracker.class);
+
+    public ExtenderBundleTracker(BundleContext context) {
+        super(context, Bundle.ACTIVE, null);
+        logger.trace("Registered as extender with context {}", context);
+    }
+
+    @Override
+    public Object addingBundle(Bundle bundle, BundleEvent event) {
+        URL resource = bundle.getEntry("META-INF/services/"
+                + ModuleFactory.class.getName());
+        logger.trace(
+                "Got addingBundle event of bundle {}, resource {}, event {}",
+                bundle, resource, event);
+        if (resource != null) {
+            try (InputStream inputStream = resource.openStream()) {
+                List<String> lines = IOUtils.readLines(inputStream);
+                for (String factoryClassName : lines) {
+                    registerFactory(factoryClassName, bundle);
+                }
+            } catch (Exception e) {
+                logger.error("Error while reading {}, stopping bundle {}",
+                        resource, bundle, e);
+                stopBundleQuietly(bundle);
+                throw new RuntimeException(e);
+            }
+
+        }
+        return bundle;
+    }
+
+    private static void stopBundleQuietly(Bundle bundle) {
+        try {
+            bundle.stop();
+        } catch (BundleException e2) {
+            logger.warn(
+                    "Ignoring fact that bundle.stop failed on {}, reason {}",
+                    bundle, e2.toString());
+        }
+    }
+
+    // TODO:test
+    private static ServiceRegistration<?> registerFactory(
+            String factoryClassName, Bundle bundle) {
+        String errorMessage;
+        try {
+            Class<?> clazz = bundle.loadClass(factoryClassName);
+            if (ModuleFactory.class.isAssignableFrom(clazz)) {
+                try {
+                    logger.debug("Registering {} in bundle {}",
+                            clazz.getName(), bundle);
+                    return bundle.getBundleContext().registerService(
+                            ModuleFactory.class.getName(), clazz.newInstance(),
+                            null);
+                } catch (InstantiationException e) {
+                    errorMessage = logMessage(
+                            "Could not instantiate {} in bundle {}, reason {}",
+                            factoryClassName, bundle, e);
+                } catch (IllegalAccessException e) {
+                    errorMessage = logMessage(
+                            "Illegal access during instatiation of class {} in bundle {}, reason {}",
+                            factoryClassName, bundle, e);
+                }
+            } else {
+                errorMessage = logMessage(
+                        "Class {} does not implement {} in bundle {}", clazz,
+                        ModuleFactory.class, bundle);
+            }
+        } catch (ClassNotFoundException e) {
+            errorMessage = logMessage(
+                    "Could not find class {} in bunde {}, reason {}",
+                    factoryClassName, bundle, e);
+        }
+        throw new IllegalStateException(errorMessage);
+    }
+
+    public static String logMessage(String slfMessage, Object... params) {
+        logger.info(slfMessage, params);
+        String formatMessage = slfMessage.replaceAll("\\{\\}", "%s");
+        return format(formatMessage, params);
+    }
+}
diff --git a/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/util/InterfacesHelper.java b/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/util/InterfacesHelper.java
new file mode 100644 (file)
index 0000000..6cd8554
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * 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.manager.impl.util;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import javax.management.JMX;
+
+import org.opendaylight.controller.config.api.annotations.AbstractServiceInterface;
+import org.opendaylight.controller.config.api.annotations.ServiceInterfaceAnnotation;
+import org.opendaylight.controller.config.spi.Module;
+
+public class InterfacesHelper {
+
+    public static Set<Class<?>> getAllInterfaces(Class<?> clazz) {
+        if (clazz.isInterface()) {
+            throw new IllegalArgumentException(clazz
+                    + " should not be an interface");
+        }
+        // getInterfaces gets interfaces implemented directly by this class
+        Set<Class<?>> toBeInspected = new HashSet<>();
+        while (clazz.equals(Object.class) == false) {
+            toBeInspected.addAll(Arrays.asList(clazz.getInterfaces()));
+            // get parent class
+            clazz = clazz.getSuperclass();
+        }
+        // each interface can extend other interfaces
+        Set<Class<?>> inspected = new HashSet<>();
+        while (toBeInspected.size() > 0) {
+            Iterator<Class<?>> iterator = toBeInspected.iterator();
+            Class<?> ifc = iterator.next();
+            iterator.remove();
+            toBeInspected.addAll(Arrays.asList(ifc.getInterfaces()));
+            inspected.add(ifc);
+        }
+        return inspected;
+    }
+
+    /**
+     * Get interfaces that this class is derived from that are JMX interfaces.
+     */
+    public static Set<Class<?>> getMXInterfaces(
+            Class<? extends Module> configBeanClass) {
+        Set<Class<?>> allInterfaces = getAllInterfaces(configBeanClass);
+        Set<Class<?>> result = new HashSet<>();
+        for (Class<?> clazz : allInterfaces) {
+            if (JMX.isMXBeanInterface(clazz)) {
+                result.add(clazz);
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Get all implemented interfaces that have
+     * {@link org.opendaylight.controller.config.api.annotations.ServiceInterfaceAnnotation}
+     * annotation.
+     */
+    public static Set<Class<?>> getServiceInterfaces(
+            Class<? extends Module> configBeanClass) {
+        Set<Class<?>> allInterfaces = getAllInterfaces(configBeanClass);
+        Set<Class<?>> result = new HashSet<>();
+        for (Class<?> clazz : allInterfaces) {
+            if (AbstractServiceInterface.class.isAssignableFrom(clazz)) {
+                ServiceInterfaceAnnotation annotation = clazz
+                        .getAnnotation(ServiceInterfaceAnnotation.class);
+                if (annotation != null) {
+                    result.add(clazz);
+                }
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Get OSGi registration types under which config bean instance should be
+     * registered. This is specified in
+     * {@link org.opendaylight.controller.config.api.annotations.ServiceInterfaceAnnotation#osgiRegistrationType()}
+     */
+    public static Set<Class<?>> getOsgiRegistrationTypes(
+            Class<? extends Module> configBeanClass) {
+        Set<Class<?>> serviceInterfaces = getServiceInterfaces(configBeanClass);
+        Set<Class<?>> result = new HashSet<>();
+        for (Class<?> clazz : serviceInterfaces) {
+            ServiceInterfaceAnnotation annotation = clazz
+                    .getAnnotation(ServiceInterfaceAnnotation.class);
+            result.add(annotation.osgiRegistrationType());
+        }
+        return result;
+    }
+
+}
diff --git a/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/util/LookupBeansUtil.java b/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/util/LookupBeansUtil.java
new file mode 100644 (file)
index 0000000..fdde0b2
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * 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.manager.impl.util;
+
+import java.util.Set;
+
+import javax.management.InstanceNotFoundException;
+import javax.management.ObjectName;
+
+import org.opendaylight.controller.config.api.LookupRegistry;
+
+public class LookupBeansUtil {
+
+    public static ObjectName lookupConfigBean(LookupRegistry lookupRegistry,
+            String moduleName, String instanceName)
+            throws InstanceNotFoundException {
+        Set<ObjectName> objectNames = lookupRegistry.lookupConfigBeans(
+                moduleName, instanceName);
+        if (objectNames.size() == 0) {
+            throw new InstanceNotFoundException("No instance found");
+        } else if (objectNames.size() > 1) {
+            throw new InstanceNotFoundException("Too many instances found");
+        }
+        return objectNames.iterator().next();
+    }
+
+}
diff --git a/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/ConfigRegistryImplTest.java b/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/ConfigRegistryImplTest.java
new file mode 100644 (file)
index 0000000..928f2c1
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * 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.manager;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verifyZeroInteractions;
+
+import java.lang.management.ManagementFactory;
+
+import org.junit.Test;
+import org.opendaylight.controller.config.manager.impl.AbstractLockedPlatformMBeanServerTest;
+import org.opendaylight.controller.config.manager.impl.ConfigRegistryImpl;
+import org.opendaylight.controller.config.manager.impl.factoriesresolver.HardcodedModuleFactoriesResolver;
+import org.opendaylight.controller.config.manager.impl.factoriesresolver.ModuleFactoriesResolver;
+import org.opendaylight.controller.config.manager.testingservices.threadpool.TestingFixedThreadPoolModuleFactory;
+import org.opendaylight.controller.config.spi.ModuleFactory;
+import org.osgi.framework.BundleContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ConfigRegistryImplTest extends
+        AbstractLockedPlatformMBeanServerTest {
+    private static final Logger logger = LoggerFactory
+            .getLogger(ConfigRegistryImplTest.class);
+
+    @Test
+    public void testFailOnTwoFactoriesExportingSameImpl() {
+        ModuleFactory factory = new TestingFixedThreadPoolModuleFactory();
+        ModuleFactoriesResolver resolver = new HardcodedModuleFactoriesResolver(
+                factory, factory);
+
+        BundleContext context = mock(BundleContext.class);
+
+        ConfigRegistryImpl configRegistry = new ConfigRegistryImpl(resolver,
+                context, ManagementFactory.getPlatformMBeanServer());
+        try {
+            configRegistry.beginConfig();
+            fail();
+        } catch (IllegalArgumentException e) {
+            assertTrue(
+                    e.getMessage(),
+                    e.getMessage()
+                            .startsWith("Module name is not unique. Found two conflicting factories with same name " +
+                                    "'fixed':"));
+            verifyZeroInteractions(context);
+        } finally {
+            try {
+                configRegistry.close();
+            } catch (Exception e) {
+                // ignore
+                logger.warn("Ignoring exception", e);
+            }
+        }
+    }
+
+}
diff --git a/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/impl/AbstractConfigTest.java b/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/impl/AbstractConfigTest.java
new file mode 100644 (file)
index 0000000..3eaa9b1
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ * 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.manager.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
+import java.io.Closeable;
+import java.lang.management.ManagementFactory;
+import java.util.Dictionary;
+import java.util.Set;
+
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+
+import org.junit.After;
+import org.mockito.Matchers;
+import org.opendaylight.controller.config.api.jmx.CommitStatus;
+import org.opendaylight.controller.config.manager.impl.factoriesresolver.ModuleFactoriesResolver;
+import org.opendaylight.controller.config.manager.impl.jmx.BaseJMXRegistrator;
+import org.opendaylight.controller.config.manager.impl.jmx.ConfigRegistryJMXRegistrator;
+import org.opendaylight.controller.config.manager.impl.jmx.InternalJMXRegistrator;
+import org.opendaylight.controller.config.spi.Module;
+import org.opendaylight.controller.config.util.ConfigRegistryJMXClient;
+import org.opendaylight.controller.config.util.ConfigTransactionJMXClient;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * Each test that relies on
+ * {@link org.opendaylight.controller.config.manager.impl.ConfigRegistryImpl}
+ * needs to subclass this test.
+ * {@link org.opendaylight.controller.config.manager.impl.ConfigRegistryImpl} is
+ * registered to platform MBean Server using
+ * {@link #initConfigTransactionManagerImpl(org.opendaylight.controller.config.manager.impl.factoriesresolver.ModuleFactoriesResolver)}
+ * typically during setting up the each test.
+ */
+public abstract class AbstractConfigTest extends
+        AbstractLockedPlatformMBeanServerTest {
+    protected ConfigRegistryJMXRegistrator configRegistryJMXRegistrator;
+    protected ConfigRegistryImpl configRegistry;
+    protected ConfigRegistryJMXClient configRegistryClient;
+    protected BaseJMXRegistrator baseJmxRegistrator;
+    protected InternalJMXRegistrator internalJmxRegistrator;
+
+    // this method should be called in @Before
+    protected void initConfigTransactionManagerImpl(
+            ModuleFactoriesResolver resolver) {
+        final MBeanServer platformMBeanServer = ManagementFactory
+                .getPlatformMBeanServer();
+
+        configRegistryJMXRegistrator = new ConfigRegistryJMXRegistrator(
+                platformMBeanServer);
+        BundleContext context = mock(BundleContext.class);
+        ServiceRegistration<?> mockedServiceRegistration = mock(ServiceRegistration.class);
+        doNothing().when(mockedServiceRegistration).unregister();
+        doReturn(mockedServiceRegistration).when(context).registerService(
+                Matchers.any(String[].class), any(Closeable.class),
+                any(Dictionary.class));
+        internalJmxRegistrator = new InternalJMXRegistrator(platformMBeanServer);
+        baseJmxRegistrator = new BaseJMXRegistrator(internalJmxRegistrator);
+
+        configRegistry = new ConfigRegistryImpl(resolver, context,
+                platformMBeanServer, baseJmxRegistrator);
+        try {
+            configRegistryJMXRegistrator.registerToJMX(configRegistry);
+        } catch (InstanceAlreadyExistsException e) {
+            throw new RuntimeException(e);
+        }
+        configRegistryClient = new ConfigRegistryJMXClient(platformMBeanServer);
+    }
+
+    @After
+    public final void cleanUpConfigTransactionManagerImpl() {
+        configRegistryJMXRegistrator.close();
+        configRegistry.close();
+    }
+
+    /**
+     * Can be called in @After of tests if some other cleanup is needed that
+     * would be discarded by closing config beans in this method
+     */
+    protected void destroyAllConfigBeans() throws Exception {
+        ConfigTransactionJMXClient transaction = configRegistryClient
+                .createTransaction();
+        Set<ObjectName> all = transaction.lookupConfigBeans();
+        // workaround for getting same Module more times
+        while (all.size() > 0) {
+            transaction.destroyModule(all.iterator().next());
+            all = transaction.lookupConfigBeans();
+        }
+        transaction.commit();
+    }
+
+    protected void assertSame(ObjectName oN1, ObjectName oN2) {
+        assertEquals(oN1.getKeyProperty("instanceName"),
+                oN2.getKeyProperty("instanceName"));
+        assertEquals(oN1.getKeyProperty("interfaceName"),
+                oN2.getKeyProperty("interfaceName"));
+    }
+
+    protected void assertStatus(CommitStatus status, int expectedNewInstances,
+            int expectedRecreatedInstances, int expectedReusedInstances) {
+        assertEquals(expectedNewInstances, status.getNewInstances().size());
+        assertEquals(expectedRecreatedInstances, status.getRecreatedInstances()
+                .size());
+        assertEquals(expectedReusedInstances, status.getReusedInstances()
+                .size());
+    }
+
+    @Deprecated
+    protected ObjectName createTestConfigBean(
+            ConfigTransactionJMXClient transaction, String implementationName,
+            String name, Class<?> clz) throws InstanceAlreadyExistsException {
+        ObjectName nameCreated = transaction.createModule(implementationName,
+                name);
+        transaction.newMXBeanProxy(nameCreated, clz);
+        return nameCreated;
+    }
+
+    protected ObjectName createTestConfigBean(
+            ConfigTransactionJMXClient transaction, String implementationName,
+            String name) throws InstanceAlreadyExistsException {
+        ObjectName nameCreated = transaction.createModule(implementationName,
+                name);
+        return nameCreated;
+    }
+
+    protected void assertBeanCount(int i, String configMXBeanName) {
+        assertEquals(i, configRegistry.lookupConfigBeans(configMXBeanName)
+                .size());
+    }
+
+    protected void assertBeanExists(int count, String moduleName,
+            String instanceName) {
+        assertEquals(1,
+                configRegistry.lookupConfigBeans(moduleName, instanceName)
+                        .size());
+    }
+
+    /**
+     *
+     * @param configBeanClass
+     *            Empty constructor class of config bean to be instantiated
+     *            whenever create
+     * @param implementationName
+     * @return
+     */
+    protected ClassBasedModuleFactory createClassBasedCBF(
+            Class<? extends Module> configBeanClass, String implementationName) {
+        return new ClassBasedModuleFactory(implementationName, configBeanClass);
+    }
+}
diff --git a/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/impl/AbstractConfigWithJolokiaTest.java b/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/impl/AbstractConfigWithJolokiaTest.java
new file mode 100644 (file)
index 0000000..de26220
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * 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.manager.impl;
+
+import org.junit.After;
+import org.junit.Before;
+import org.opendaylight.controller.config.manager.impl.factoriesresolver.ModuleFactoriesResolver;
+import org.opendaylight.controller.config.util.JolokiaHelper;
+import org.opendaylight.controller.config.util.jolokia.ConfigRegistryJolokiaClient;
+
+public class AbstractConfigWithJolokiaTest extends AbstractConfigTest {
+    protected String jolokiaURL;
+    protected ConfigRegistryJolokiaClient configRegistryJolokiaClient;
+
+    @Before
+    public void initJolokia() {
+        jolokiaURL = JolokiaHelper.startTestingJolokia();
+    }
+
+    // this method should be called in @Before
+    @Override
+    protected void initConfigTransactionManagerImpl(
+            ModuleFactoriesResolver resolver) {
+        super.initConfigTransactionManagerImpl(resolver);
+        configRegistryJolokiaClient = new ConfigRegistryJolokiaClient(
+                jolokiaURL);
+    }
+
+    @After
+    public void cleanUpJolokia() {
+        JolokiaHelper.stopJolokia();
+    }
+}
diff --git a/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/impl/AbstractLockedPlatformMBeanServerTest.java b/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/impl/AbstractLockedPlatformMBeanServerTest.java
new file mode 100644 (file)
index 0000000..2e97c49
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * 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.manager.impl;
+
+import java.lang.management.ManagementFactory;
+import java.util.concurrent.locks.ReentrantLock;
+
+import javax.management.MBeanServer;
+
+import org.junit.After;
+import org.junit.Before;
+
+/**
+ * Each test that works with platform MBeanServer should extend this class.
+ */
+public abstract class AbstractLockedPlatformMBeanServerTest {
+    private static final ReentrantLock lock = new ReentrantLock();
+    protected static MBeanServer platformMBeanServer = ManagementFactory
+            .getPlatformMBeanServer();
+
+    @Before
+    public void acquireLock() {
+        lock.lock();
+    }
+
+    @After
+    public void unlock() {
+        lock.unlock();
+    }
+
+    public static class SimpleBean implements SimpleBeanMBean {
+
+    }
+
+    public static interface SimpleBeanMBean {
+
+    }
+
+}
diff --git a/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/impl/AbstractMockedModule.java b/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/impl/AbstractMockedModule.java
new file mode 100644 (file)
index 0000000..89ac9a3
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * 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.manager.impl;
+
+import org.opendaylight.controller.config.api.DynamicMBeanWithInstance;
+import org.opendaylight.controller.config.api.ModuleIdentifier;
+import org.opendaylight.controller.config.spi.Module;
+
+public abstract class AbstractMockedModule implements Module {
+
+    protected final AutoCloseable instance;
+
+    public AbstractMockedModule() throws Exception {
+        instance = prepareMockedInstance();
+    }
+
+    protected abstract AutoCloseable prepareMockedInstance() throws Exception;
+
+    public AbstractMockedModule(DynamicMBeanWithInstance old) {
+        instance = old.getInstance();
+    }
+
+    @Override
+    public void validate() {
+    }
+
+    @Override
+    public AutoCloseable getInstance() {
+        return instance;
+    }
+
+    @Override
+    public ModuleIdentifier getName() {
+        return new ModuleIdentifier(getClass().getCanonicalName(), "mock");
+    }
+
+}
diff --git a/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/impl/ClassBasedModuleFactory.java b/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/impl/ClassBasedModuleFactory.java
new file mode 100644 (file)
index 0000000..afc79a5
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * 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.manager.impl;
+
+import java.lang.reflect.Constructor;
+import java.util.Arrays;
+import java.util.List;
+
+import org.opendaylight.controller.config.api.DependencyResolver;
+import org.opendaylight.controller.config.api.DynamicMBeanWithInstance;
+import org.opendaylight.controller.config.api.annotations.AbstractServiceInterface;
+import org.opendaylight.controller.config.spi.Module;
+import org.opendaylight.controller.config.spi.ModuleFactory;
+
+import com.google.common.base.Preconditions;
+import com.google.common.base.Throwables;
+
+/**
+ * Creates new Config beans by calling {@link Class#newInstance()} on provided
+ * config bean class.
+ *
+ */
+public class ClassBasedModuleFactory implements ModuleFactory {
+    private final String implementationName;
+    private final Class<? extends Module> configBeanClass;
+
+    /**
+     * @param implementationName
+     * @param configBeanClass
+     *            class that will be instantiated when createModule is called.
+     *            This class must implement Module interface and all exported
+     *            interfaces.
+     */
+    public ClassBasedModuleFactory(String implementationName,
+            Class<? extends Module> configBeanClass) {
+        this.implementationName = implementationName;
+        this.configBeanClass = configBeanClass;
+    }
+
+    @Override
+    public String getImplementationName() {
+        return implementationName;
+    }
+
+    @Override
+    public Module createModule(String instanceName,
+            DependencyResolver dependencyResolver, DynamicMBeanWithInstance old)
+            throws Exception {
+        Preconditions.checkNotNull(dependencyResolver);
+        Preconditions.checkNotNull(old);
+        Constructor<? extends Module> declaredConstructor;
+        try {
+            declaredConstructor = configBeanClass
+                    .getDeclaredConstructor(DynamicMBeanWithInstance.class);
+        } catch (NoSuchMethodException e) {
+            throw new IllegalStateException(
+                    "Did not find constructor with parameters (DynamicMBeanWithInstance) in "
+                            + configBeanClass, e);
+        }
+        Preconditions.checkState(declaredConstructor != null);
+        return declaredConstructor.newInstance(old);
+    }
+
+    @Override
+    public Module createModule(String instanceName,
+            DependencyResolver dependencyResolver) {
+        try {
+            return configBeanClass.newInstance();
+        } catch (Exception e) {
+            throw Throwables.propagate(e);
+        }
+    }
+
+    @Override
+    public boolean isModuleImplementingServiceInterface(
+            Class<? extends AbstractServiceInterface> serviceInterface) {
+        Class<?>[] classes = configBeanClass.getInterfaces();
+        List<Class<?>> ifc = Arrays.asList(classes);
+        if (ifc.contains(serviceInterface)) {
+            return true;
+        }
+        for (Class<?> c : classes) {
+            ifc = Arrays.asList(c.getInterfaces());
+            if (ifc.contains(serviceInterface)) {
+                return true;
+            }
+        }
+        return false;
+    }
+}
diff --git a/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/impl/ConfigRegistryImplLookupTest.java b/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/impl/ConfigRegistryImplLookupTest.java
new file mode 100644 (file)
index 0000000..6dddc62
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ * 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.manager.impl;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+import java.lang.management.ManagementFactory;
+import java.lang.reflect.Field;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.ObjectName;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.config.api.ModuleIdentifier;
+import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
+import org.opendaylight.controller.config.api.runtime.RuntimeBean;
+import org.opendaylight.controller.config.manager.impl.jmx.BaseJMXRegistrator;
+import org.opendaylight.controller.config.manager.impl.jmx.RootRuntimeBeanRegistratorImpl;
+import org.opendaylight.controller.config.manager.impl.runtimembean.TestingRuntimeBean;
+
+import com.google.common.collect.Sets;
+
+public class ConfigRegistryImplLookupTest extends
+        AbstractLockedPlatformMBeanServerTest {
+
+    private ConfigRegistryImpl configRegistryImpl;
+    private BaseJMXRegistrator baseJMXRegistrator;
+
+    private static final String moduleNameA = "moduleA";
+    private static final String moduleNameB = "moduleB";
+
+    private static final String instanceNameA = "instA";
+    private static final String instanceNameB = "instB";
+    private static final String instanceNameC = "instC";
+
+    private static final ObjectName name1 = ObjectNameUtil
+            .createReadOnlyModuleON(moduleNameA, instanceNameA);
+    private static final ObjectName name2 = ObjectNameUtil
+            .createReadOnlyModuleON(moduleNameA, instanceNameB);
+    private static final ObjectName name3 = ObjectNameUtil
+            .createReadOnlyModuleON(moduleNameA, instanceNameC);
+    private static final ObjectName name4 = ObjectNameUtil
+            .createReadOnlyModuleON(moduleNameB, instanceNameA);
+
+    private static final ObjectName name5 = ObjectNameUtil
+            .createRuntimeBeanName(moduleNameA, instanceNameA, Collections.<String, String>emptyMap());
+    private static final ObjectName name6 = ObjectNameUtil
+            .createRuntimeBeanName(moduleNameA, instanceNameB, Collections.<String, String>emptyMap());
+    private static final ObjectName name8 = ObjectNameUtil
+            .createRuntimeBeanName(moduleNameB, instanceNameA, Collections.<String, String>emptyMap());
+
+    private static final ObjectName name9 = ObjectNameUtil
+            .createTransactionModuleON("transaction", moduleNameA, instanceNameA);
+
+    @Before
+    public void setUp() throws Exception {
+        configRegistryImpl = new ConfigRegistryImpl(null, null,
+                ManagementFactory.getPlatformMBeanServer());
+        Field field = configRegistryImpl.getClass().getDeclaredField(
+                "baseJMXRegistrator");
+        field.setAccessible(true);
+        baseJMXRegistrator = (BaseJMXRegistrator) field.get(configRegistryImpl);
+
+        registerModuleBean(new TestingRuntimeBean(), baseJMXRegistrator, name1);
+        registerModuleBean(new TestingRuntimeBean(), baseJMXRegistrator, name2);
+        registerModuleBean(new TestingRuntimeBean(), baseJMXRegistrator, name3);
+        registerModuleBean(new TestingRuntimeBean(), baseJMXRegistrator, name4);
+
+        registerRuntimeBean(new TestingRuntimeBean(), baseJMXRegistrator, name5);
+        registerRuntimeBean(new TestingRuntimeBean(), baseJMXRegistrator, name6);
+        registerRuntimeBean(new TestingRuntimeBean(), baseJMXRegistrator, name8);
+
+        baseJMXRegistrator.createTransactionJMXRegistrator("transaction")
+                .createTransactionModuleJMXRegistrator()
+                .registerMBean(new TestingRuntimeBean(), name9);
+
+    }
+
+    private void registerModuleBean(TestingRuntimeBean testingRuntimeBean,
+            BaseJMXRegistrator baseJMXRegistrator, ObjectName objectName)
+            throws InstanceAlreadyExistsException {
+        baseJMXRegistrator.createModuleJMXRegistrator().registerMBean(
+                testingRuntimeBean, objectName);
+    }
+
+    private void registerRuntimeBean(RuntimeBean object,
+            BaseJMXRegistrator baseJMXRegistrator, ObjectName runtimeON)
+            throws InstanceAlreadyExistsException {
+        String factoryName = ObjectNameUtil.getFactoryName(runtimeON);
+        String instanceName = ObjectNameUtil.getInstanceName(runtimeON);
+        Map<String, String> properties = ObjectNameUtil
+                .getAdditionalPropertiesOfRuntimeBeanName(runtimeON);
+
+        RootRuntimeBeanRegistratorImpl runtimeBeanRegistrator = baseJMXRegistrator
+                .createRuntimeBeanRegistrator(new ModuleIdentifier(factoryName, instanceName));
+
+        assertThat(properties.isEmpty(), is(true));
+
+        runtimeBeanRegistrator.registerRoot(object);
+    }
+
+    @After
+    public void cleanUp() {
+        baseJMXRegistrator.close();
+    }
+
+    @Test
+    public void testLookupConfigBeans() throws Exception {
+        Set<ObjectName> beans = configRegistryImpl.lookupConfigBeans();
+        assertEquals(Sets.newHashSet(name1, name2, name3, name4), beans);
+        beans = configRegistryImpl.lookupConfigBeans();
+        assertEquals(Sets.newHashSet(name1, name2, name3, name4), beans);
+    }
+
+    @Test
+    public void testLookupConfigBeanWithModuleName() throws Exception {
+        Set<ObjectName> bean = configRegistryImpl
+                .lookupConfigBeans(moduleNameA);
+        assertEquals(Sets.newHashSet(name1, name2, name3), bean);
+    }
+
+    @Test
+    public void testLookupConfigBeanWithModuleNameAndInstanceName()
+            throws Exception {
+        Set<ObjectName> bean = configRegistryImpl.lookupConfigBeans(
+                moduleNameA, instanceNameA);
+        assertEquals(Sets.newHashSet(name1), bean);
+    }
+
+    @Test
+    public void testLookupRuntimeBeans() throws Exception {
+        Set<ObjectName> beans = configRegistryImpl.lookupRuntimeBeans();
+        assertEquals(Sets.newHashSet(name5, name6, name8), beans);
+        beans = configRegistryImpl.lookupRuntimeBeans(null, null);
+        assertEquals(Sets.newHashSet(name5, name6, name8), beans);
+    }
+
+    @Test
+    public void testLookupRuntimeBeansWithIFcNameAndImplName() throws Exception {
+        Set<ObjectName> beans = configRegistryImpl.lookupRuntimeBeans(
+                moduleNameA, instanceNameA);
+        assertEquals(Sets.newHashSet(name5), beans);
+    }
+
+}
diff --git a/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/impl/ConfigTransactionControllerImplTest.java b/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/impl/ConfigTransactionControllerImplTest.java
new file mode 100644 (file)
index 0000000..593f99f
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * 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.manager.impl;
+
+import static org.junit.Assert.assertEquals;
+
+import java.lang.management.ManagementFactory;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.ObjectName;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
+import org.opendaylight.controller.config.manager.impl.jmx.BaseJMXRegistrator;
+import org.opendaylight.controller.config.manager.impl.jmx.TransactionJMXRegistrator;
+import org.opendaylight.controller.config.manager.impl.jmx.TransactionModuleJMXRegistrator;
+import org.opendaylight.controller.config.manager.impl.runtimembean.TestingRuntimeBean;
+import org.opendaylight.controller.config.spi.ModuleFactory;
+
+import com.google.common.collect.Sets;
+
+public class ConfigTransactionControllerImplTest extends
+        AbstractLockedPlatformMBeanServerTest {
+
+    private BaseJMXRegistrator baseJMXRegistrator;
+
+    private ConfigTransactionControllerImpl testedTxController;
+    private MBeanServer transactionsMBeanServer;
+
+    private static final String transactionName123 = "testTX1";
+    private static final String transactionName4 = "testTX2";
+
+    private static final String moduleName124 = "module124";
+    private static final String moduleName3 = "module3";
+
+    private static final String instanceName134 = "instA";
+    private static final String instanceName2 = "instB";
+
+    private static final ObjectName name1 = ObjectNameUtil
+            .createTransactionModuleON(transactionName123, moduleName124, instanceName134);
+    private static final ObjectName name2 = ObjectNameUtil
+            .createTransactionModuleON(transactionName123, moduleName124, instanceName2);
+    private static final ObjectName name3 = ObjectNameUtil
+            .createTransactionModuleON(transactionName123, moduleName3, instanceName134);
+    private static final ObjectName name4 = ObjectNameUtil
+            .createTransactionModuleON(transactionName4, moduleName124, instanceName134);
+
+    @Before
+    public void setUp() throws Exception {
+        baseJMXRegistrator = new BaseJMXRegistrator(
+                ManagementFactory.getPlatformMBeanServer());
+        transactionsMBeanServer = MBeanServerFactory.createMBeanServer();
+        List<? extends ModuleFactory> currentlyRegisteredFactories = new ArrayList<>();
+        TransactionJMXRegistrator jmxRegistrator123 = baseJMXRegistrator
+                .createTransactionJMXRegistrator(transactionName123);
+
+        testedTxController = new ConfigTransactionControllerImpl(
+                transactionName123, jmxRegistrator123, 1, 1,
+                currentlyRegisteredFactories, transactionsMBeanServer,
+                ManagementFactory.getPlatformMBeanServer());
+        TransactionModuleJMXRegistrator transactionModuleJMXRegistrator123 = testedTxController
+                .getTxModuleJMXRegistrator();
+        transactionModuleJMXRegistrator123.registerMBean(
+                new TestingRuntimeBean(), name1);
+        transactionModuleJMXRegistrator123.registerMBean(
+                new TestingRuntimeBean(), name2);
+        transactionModuleJMXRegistrator123.registerMBean(
+                new TestingRuntimeBean(), name3);
+        TransactionJMXRegistrator jmxRegistrator4 = baseJMXRegistrator
+                .createTransactionJMXRegistrator(transactionName4);
+        jmxRegistrator4.createTransactionModuleJMXRegistrator().registerMBean(
+                new TestingRuntimeBean(), name4);
+    }
+
+    @After
+    public void cleanUp() {
+        baseJMXRegistrator.close();
+        MBeanServerFactory.releaseMBeanServer(transactionsMBeanServer);
+    }
+
+    /**
+     * Tests if lookup method returns all beans with defined transaction name
+     *
+     * @throws Exception
+     */
+    @Test
+    public void testLookupConfigBeans() {
+        Set<ObjectName> beans = testedTxController.lookupConfigBeans();
+        assertEquals(Sets.newHashSet(name1, name2, name3), beans);
+    }
+
+    @Test
+    public void testLookupConfigBeansWithModuleName() {
+        Set<ObjectName> beans = testedTxController
+                .lookupConfigBeans(moduleName124);
+        assertEquals(Sets.newHashSet(name1, name2), beans);
+    }
+
+    @Test
+    public void lookupConfigBeansWithModuleNameAndImplName() throws Exception {
+        Set<ObjectName> beans = testedTxController.lookupConfigBeans(
+                moduleName124, instanceName134);
+        assertEquals(Sets.newHashSet(name1), beans);
+    }
+
+}
diff --git a/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/impl/ConfigTransactionManagerImplTest.java b/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/impl/ConfigTransactionManagerImplTest.java
new file mode 100644 (file)
index 0000000..eaaee5e
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * 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.manager.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+
+import javax.management.InstanceAlreadyExistsException;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
+import org.opendaylight.controller.config.api.jmx.constants.ConfigRegistryConstants;
+import org.opendaylight.controller.config.manager.impl.factoriesresolver.HardcodedModuleFactoriesResolver;
+import org.opendaylight.controller.config.util.ConfigTransactionJMXClient;
+import org.opendaylight.controller.config.util.jolokia.ConfigTransactionJolokiaClient;
+
+public class ConfigTransactionManagerImplTest extends
+        AbstractConfigWithJolokiaTest {
+
+    @Before
+    public void setUp() {
+        super.initConfigTransactionManagerImpl(new HardcodedModuleFactoriesResolver());
+
+    }
+
+    @Test
+    public void testSingleton() {
+        ConfigRegistryImpl mockedRegistry = mock(ConfigRegistryImpl.class);
+        try {
+            configRegistryJMXRegistrator.registerToJMX(mockedRegistry);
+            fail();
+        } catch (Exception e) {
+            assertTrue(e instanceof InstanceAlreadyExistsException);
+        }
+    }
+
+    @Test
+    public void testCleanUp() {
+        super.cleanUpConfigTransactionManagerImpl();
+        setUp();
+    }
+
+    @Test
+    public void testRemoteCallsUsingJMX() throws Exception {
+        ConfigTransactionJMXClient transaction = configRegistryClient
+                .createTransaction();
+        transaction.commit();
+    }
+
+    @Test
+    public void testRemoteCallsUsingJolokia() throws Exception {
+
+        ConfigTransactionJolokiaClient transactionClient = configRegistryJolokiaClient
+                .createTransaction();
+
+        assertEquals("ConfigTransaction-0-1",
+                ObjectNameUtil.getTransactionName(transactionClient
+                        .getTransactionON()));
+
+        assertEquals(
+                ConfigRegistryConstants.ON_DOMAIN
+                        + ":TransactionName=ConfigTransaction-0-1,type=ConfigTransaction",
+                transactionClient.getTransactionON().getCanonicalName());
+
+        // commit
+        transactionClient.commit();
+
+    }
+}
diff --git a/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/impl/dependencyresolver/DependencyResolverManagerTest.java b/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/impl/dependencyresolver/DependencyResolverManagerTest.java
new file mode 100644 (file)
index 0000000..a79cb83
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * 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.manager.impl.dependencyresolver;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.config.api.JmxAttribute;
+import org.opendaylight.controller.config.api.ModuleIdentifier;
+import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
+import org.opendaylight.controller.config.manager.impl.ModuleInternalTransactionalInfo;
+import org.opendaylight.controller.config.manager.impl.TransactionStatus;
+import org.opendaylight.controller.config.spi.Module;
+
+public class DependencyResolverManagerTest {
+
+    final ModuleIdentifier apspName = new ModuleIdentifier("apsp", "apsp"); // depends
+                                                                            // on:
+    final ModuleIdentifier threadPoolName = new ModuleIdentifier("threadpool",
+            "threadpool"); // depends on:
+    final ModuleIdentifier threadFactoryName = new ModuleIdentifier(
+            "threadfactory", "threadfactory");
+
+    private DependencyResolverManager tested;
+    TransactionStatus transactionStatus;
+
+    @Before
+    public void setUp() {
+        transactionStatus = mock(TransactionStatus.class);
+        tested = new DependencyResolverManager("txName", transactionStatus);
+        doNothing().when(transactionStatus).checkCommitStarted();
+        doNothing().when(transactionStatus).checkNotCommitted();
+    }
+
+    @Test
+    public void testOrdering() {
+        DependencyResolverImpl apspDRI = tested.getOrCreate(apspName);
+        mockGetInstance(tested, apspName);
+        DependencyResolverImpl threadPoolDRI = tested
+                .getOrCreate(threadPoolName);
+        mockGetInstance(tested, threadPoolName);
+        tested.getOrCreate(threadFactoryName);
+        mockGetInstance(tested, threadFactoryName);
+
+        // set threadfactory as dependency of threadpool
+        declareDependency(threadPoolDRI, threadFactoryName);
+        // set threadpool as dependency of apsp
+        declareDependency(apspDRI, threadPoolName);
+
+        // switch to second phase committed
+        reset(transactionStatus);
+        doNothing().when(transactionStatus).checkCommitted();
+        List<ModuleIdentifier> sortedModuleIdentifiers = tested
+                .getSortedModuleIdentifiers();
+        assertEquals(
+                Arrays.asList(threadFactoryName, threadPoolName, apspName),
+                sortedModuleIdentifiers);
+
+    }
+
+    /**
+     * Simulate dependentResolver resolving its dependency identified by
+     * dependentName.
+     */
+    private void declareDependency(DependencyResolverImpl dependerResolver,
+            ModuleIdentifier dependentName) {
+        JmxAttribute dummyAttribute = new JmxAttribute("dummy");
+        dependerResolver.resolveInstance(Object.class,
+                ObjectNameUtil.createReadOnlyModuleON(dependentName),
+                dummyAttribute);
+    }
+
+    private static void mockGetInstance(DependencyResolverManager tested,
+            ModuleIdentifier moduleIdentifier) {
+        ModuleInternalTransactionalInfo mock = mock(ModuleInternalTransactionalInfo.class);
+        doReturn(moduleIdentifier).when(mock).getName();
+        doReturn(mockedModule()).when(mock).getModule();
+        tested.put(mock);
+    }
+
+    private static Module mockedModule() {
+        Module mockedModule = mock(Module.class);
+        doReturn(mock(AutoCloseable.class)).when(mockedModule).getInstance();
+        return mockedModule;
+    }
+
+}
diff --git a/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/impl/dynamicmbean/AbstractDynamicWrapperTest.java b/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/impl/dynamicmbean/AbstractDynamicWrapperTest.java
new file mode 100644 (file)
index 0000000..e411778
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * 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.manager.impl.dynamicmbean;
+
+import static org.junit.Assert.assertEquals;
+
+import java.lang.management.ManagementFactory;
+
+import javax.management.Attribute;
+import javax.management.AttributeList;
+import javax.management.DynamicMBean;
+import javax.management.JMX;
+import javax.management.MBeanInfo;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.ObjectName;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.config.api.ModuleIdentifier;
+import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
+import org.opendaylight.controller.config.manager.impl.AbstractLockedPlatformMBeanServerTest;
+import org.opendaylight.controller.config.manager.testingservices.threadpool.TestingFixedThreadPool;
+import org.opendaylight.controller.config.manager.testingservices.threadpool.TestingFixedThreadPoolConfigMXBean;
+import org.opendaylight.controller.config.manager.testingservices.threadpool.TestingFixedThreadPoolModule;
+import org.opendaylight.controller.config.manager.testingservices.threadpool.TestingFixedThreadPoolModuleFactory;
+import org.opendaylight.controller.config.spi.Module;
+
+public abstract class AbstractDynamicWrapperTest extends
+        AbstractLockedPlatformMBeanServerTest {
+    protected final MBeanServer platformMBeanServer = ManagementFactory
+            .getPlatformMBeanServer();
+    private static final String moduleName = "impl";
+    protected final ObjectName threadPoolDynamicWrapperON = ObjectNameUtil
+            .createReadOnlyModuleON(moduleName, "fixed1");
+    protected static final String THREAD_COUNT = "ThreadCount";
+    protected static final String TRIGGER_NEW_INSTANCE_CREATION = "TriggerNewInstanceCreation";
+
+    protected final int threadCount = 5;
+    protected TestingFixedThreadPoolModule threadPoolConfigBean;
+    private static final ModuleIdentifier moduleIdentifier = new ModuleIdentifier(
+            moduleName, "clientname2");
+
+    protected MBeanServer internalServer;
+
+    @Before
+    public void registerToJMX() throws Exception {
+        internalServer = MBeanServerFactory.createMBeanServer();
+        TestingFixedThreadPoolModuleFactory testingFixedThreadPoolConfigBeanFactory = new TestingFixedThreadPoolModuleFactory();
+        threadPoolConfigBean = testingFixedThreadPoolConfigBeanFactory
+                .createModule("", null);
+
+        threadPoolConfigBean.setThreadCount(threadCount);
+        AbstractDynamicWrapper dynamicWrapper = getDynamicWrapper(
+                threadPoolConfigBean, moduleIdentifier);
+        platformMBeanServer.registerMBean(dynamicWrapper,
+                threadPoolDynamicWrapperON);
+    }
+
+    @After
+    public void unregisterFromJMX() throws Exception {
+        TestingFixedThreadPool.cleanUp();
+        platformMBeanServer.unregisterMBean(threadPoolDynamicWrapperON);
+        MBeanServerFactory.releaseMBeanServer(internalServer);
+    }
+
+    protected abstract AbstractDynamicWrapper getDynamicWrapper(Module module,
+            ModuleIdentifier moduleIdentifier);
+
+    @Test
+    public void testReadAttributes() throws Exception {
+
+        DynamicMBean proxy = JMX.newMBeanProxy(platformMBeanServer,
+                threadPoolDynamicWrapperON, DynamicMBean.class);
+
+        assertEquals(threadCount, proxy.getAttribute(THREAD_COUNT));
+
+        assertEquals(threadPoolConfigBean.isTriggerNewInstanceCreation(),
+                proxy.getAttribute(TRIGGER_NEW_INSTANCE_CREATION));
+
+        AttributeList attributes = proxy.getAttributes(new String[] {
+                THREAD_COUNT, TRIGGER_NEW_INSTANCE_CREATION });
+        assertEquals(2, attributes.size());
+        Attribute threadCountAttr = (Attribute) attributes.get(0);
+        assertEquals(THREAD_COUNT, threadCountAttr.getName());
+        assertEquals(threadCount, threadCountAttr.getValue());
+        Attribute boolTestAttr = (Attribute) attributes.get(1);
+        assertEquals(TRIGGER_NEW_INSTANCE_CREATION, boolTestAttr.getName());
+        assertEquals(threadPoolConfigBean.isTriggerNewInstanceCreation(),
+                boolTestAttr.getValue());
+
+        MBeanInfo mBeanInfo = proxy.getMBeanInfo();
+        assertEquals(2, mBeanInfo.getAttributes().length);
+
+    }
+
+    @Test
+    public void testGettersWithMXBeanProxy() {
+        TestingFixedThreadPoolConfigMXBean proxy = JMX.newMXBeanProxy(
+                platformMBeanServer, threadPoolDynamicWrapperON,
+                TestingFixedThreadPoolConfigMXBean.class);
+        assertEquals(threadCount, proxy.getThreadCount());
+    }
+
+}
diff --git a/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/impl/dynamicmbean/AnnotationsTest.java b/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/impl/dynamicmbean/AnnotationsTest.java
new file mode 100644 (file)
index 0000000..cf6ed18
--- /dev/null
@@ -0,0 +1,280 @@
+/*
+ * 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.manager.impl.dynamicmbean;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
+
+import javax.management.ObjectName;
+
+import org.junit.Test;
+import org.opendaylight.controller.config.api.annotations.AbstractServiceInterface;
+import org.opendaylight.controller.config.api.annotations.Description;
+import org.opendaylight.controller.config.api.annotations.RequireInterface;
+import org.opendaylight.controller.config.api.annotations.ServiceInterfaceAnnotation;
+
+import com.google.common.base.Throwables;
+import com.google.common.collect.Sets;
+
+public class AnnotationsTest {
+
+    private final String setSomethingString = "setSomething";
+
+    private void assertRequireInterfaceAnnotationHasCorrectValue(
+            Class<?> clazz, String methodName,
+            Set<Class<?>> inspectedInterfaces,
+            Class<? extends AbstractServiceInterface> expectedValue) {
+        Method setter = findMethod(clazz, methodName);
+        RequireInterface found = AttributeHolder
+                .findRequireInterfaceAnnotation(setter, inspectedInterfaces);
+        if (expectedValue == null) {
+            assertNull(found);
+        } else {
+            assertNotNull(found);
+            assertEquals(expectedValue, found.value());
+        }
+    }
+
+    private Method findMethod(Class<?> clazz, String methodName) {
+        Method setter;
+        try {
+            setter = clazz.getMethod(methodName,
+                    new Class[] { ObjectName.class });
+        } catch (Exception e) {
+            throw Throwables.propagate(e);
+        }
+        return setter;
+    }
+
+    private void assertDescription(Class<?> clazz, String methodName,
+            Set<Class<?>> exportedInterfaces, String expectedValue) {
+        Method setter = findMethod(clazz, methodName);
+        String found = AttributeHolder.findDescription(setter,
+                exportedInterfaces);
+        if (expectedValue == null) {
+            assertNull(found);
+        } else {
+            assertNotNull(found);
+            assertEquals(expectedValue, found);
+        }
+    }
+
+    private void assertDescriptionOnClass(Class<?> clazz,
+            Set<Class<?>> jmxInterfaces, String expectedValue) {
+        String found = AbstractDynamicWrapper.findDescription(clazz,
+                jmxInterfaces);
+        if (expectedValue == null) {
+            assertNull(found);
+        } else {
+            assertNotNull(found);
+            assertEquals(expectedValue, found);
+        }
+    }
+
+    private void assertNoDescriptionOnClass(Class<?> clazz,
+            Set<Class<?>> jmxInterfaces) {
+        String found = AbstractDynamicWrapper.findDescription(clazz,
+                jmxInterfaces);
+        assertTrue(found.isEmpty());
+    }
+
+    static final String SIMPLE = "simple";
+    static final String SUBCLASS2 = "subclass2";
+
+    @ServiceInterfaceAnnotation(value = SIMPLE, osgiRegistrationType = Executor.class)
+    static interface SimpleSI extends AbstractServiceInterface {
+
+    }
+
+    @Description("class")
+    public static class SuperClass {
+        @RequireInterface(SimpleSI.class)
+        @Description("descr")
+        public void setSomething(ObjectName objectName) {
+
+        }
+    }
+
+    private static Set<Class<?>> emptySetOfInterfaces() {
+        return Collections.emptySet();
+    }
+
+    @Test
+    public void testFindAnnotation_directly() throws Exception {
+        assertRequireInterfaceAnnotationHasCorrectValue(SuperClass.class,
+                setSomethingString, emptySetOfInterfaces(), SimpleSI.class);
+        assertDescription(SuperClass.class, setSomethingString,
+                emptySetOfInterfaces(), "descr");
+        assertDescriptionOnClass(SuperClass.class, emptySetOfInterfaces(),
+                "class");
+    }
+
+    public static class SubClassWithout extends SuperClass {
+
+    }
+
+    @Test
+    public void testFindAnnotation_subclassWithout() throws Exception {
+        assertRequireInterfaceAnnotationHasCorrectValue(SubClassWithout.class,
+                setSomethingString, emptySetOfInterfaces(), SimpleSI.class);
+        assertDescription(SubClassWithout.class, setSomethingString,
+                emptySetOfInterfaces(), "descr");
+        assertDescriptionOnClass(SuperClass.class, emptySetOfInterfaces(),
+                "class");
+    }
+
+    public static class SubClassWithEmptyMethod extends SuperClass {
+        @Override
+        public void setSomething(ObjectName objectName) {
+
+        }
+    }
+
+    @Test
+    public void testOverridingWithoutAnnotation() throws Exception {
+        assertRequireInterfaceAnnotationHasCorrectValue(
+                SubClassWithEmptyMethod.class, setSomethingString,
+                emptySetOfInterfaces(), SimpleSI.class);
+        assertDescription(SubClassWithEmptyMethod.class, setSomethingString,
+                emptySetOfInterfaces(), "descr");
+        assertDescriptionOnClass(SubClassWithEmptyMethod.class,
+                emptySetOfInterfaces(), "class");
+    }
+
+    static interface SubSI extends SimpleSI {
+
+    }
+
+    @ServiceInterfaceAnnotation(value = SUBCLASS2, osgiRegistrationType = ExecutorService.class)
+    static interface SubSI2 extends SubSI {
+
+    }
+
+    public static class SubClassWithAnnotation extends SuperClass {
+        @Override
+        @RequireInterface(SubSI2.class)
+        @Description("descr2")
+        public void setSomething(ObjectName objectName) {
+
+        }
+    }
+
+    @Test
+    public void testFindAnnotation_SubClassWithAnnotation() throws Exception {
+        assertDescription(SubClassWithAnnotation.class, setSomethingString,
+                emptySetOfInterfaces(), "descr2\ndescr");
+        try {
+            assertRequireInterfaceAnnotationHasCorrectValue(
+                    SubClassWithAnnotation.class, setSomethingString,
+                    emptySetOfInterfaces(), SubSI2.class);
+            fail();
+        } catch (IllegalStateException e) {
+            assertTrue(
+                    e.getMessage(),
+                    e.getMessage()
+                            .startsWith("Error finding @RequireInterface. More than one value specified"));
+        }
+    }
+
+    public static interface HasSomeMethod {
+        void setSomething(ObjectName objectName);
+    }
+
+    public static class SubClassWithoutMethodWithInterface extends SuperClass
+            implements HasSomeMethod {
+
+    }
+
+    @Test
+    public void testFindAnnotation_SubClassWithoutMethodWithInterface()
+            throws Exception {
+        assertRequireInterfaceAnnotationHasCorrectValue(
+                SubClassWithoutMethodWithInterface.class, setSomethingString,
+                emptySetOfInterfaces(), SimpleSI.class);
+        assertDescription(SubClassWithoutMethodWithInterface.class,
+                setSomethingString, emptySetOfInterfaces(), "descr");
+    }
+
+    static abstract class SuperClassWithInterface implements HasSomeMethod {
+        @Override
+        @RequireInterface(SubSI2.class)
+        @Description("descr")
+        public void setSomething(ObjectName objectName) {
+
+        }
+    }
+
+    @Description("class")
+    public static class SubClassOfSuperClassWithInterface extends
+            SuperClassWithInterface {
+
+    }
+
+    @Test
+    public void testFindAnnotation_SubClassOfSuperClassWithInterface()
+            throws Exception {
+        assertRequireInterfaceAnnotationHasCorrectValue(
+                SubClassOfSuperClassWithInterface.class, setSomethingString,
+                emptySetOfInterfaces(), SubSI2.class);
+        assertDescription(SubClassOfSuperClassWithInterface.class,
+                setSomethingString, emptySetOfInterfaces(), "descr");
+        assertDescriptionOnClass(SubClassOfSuperClassWithInterface.class,
+                emptySetOfInterfaces(), "class");
+    }
+
+    @Test
+    public void testFindAnnotation2() throws Exception {
+        assertNoDescriptionOnClass(SuperClassWithInterface.class,
+                emptySetOfInterfaces());
+    }
+
+    @Description("class")
+    static interface HasSomeMethodWithAnnotations {
+
+        @RequireInterface(SubSI2.class)
+        @Description("descr")
+        void setSomething(ObjectName objectName);
+    }
+
+    static class HasSomeMethodWithAnnotationsImpl implements
+            HasSomeMethodWithAnnotations {
+        @Override
+        public void setSomething(ObjectName objectName) {
+        }
+
+    }
+
+    @Test
+    public void testHasSomeMethodWithAnnotationsImpl() {
+        HashSet<Class<?>> exportedInterfaces = Sets
+                .<Class<?>> newHashSet(HasSomeMethodWithAnnotations.class);
+        assertRequireInterfaceAnnotationHasCorrectValue(
+                HasSomeMethodWithAnnotationsImpl.class, setSomethingString,
+                exportedInterfaces, SubSI2.class);
+
+        assertDescription(HasSomeMethodWithAnnotationsImpl.class,
+                setSomethingString, exportedInterfaces, "descr");
+
+        assertDescriptionOnClass(
+                HasSomeMethodWithAnnotationsImpl.class,
+                new HashSet<Class<?>>(Arrays
+                        .asList(HasSomeMethodWithAnnotations.class)), "class");
+    }
+
+}
diff --git a/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/impl/dynamicmbean/DynamicReadableWrapperTest.java b/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/impl/dynamicmbean/DynamicReadableWrapperTest.java
new file mode 100644 (file)
index 0000000..3564489
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * 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.manager.impl.dynamicmbean;
+
+import java.lang.management.ManagementFactory;
+
+import org.opendaylight.controller.config.api.ModuleIdentifier;
+import org.opendaylight.controller.config.spi.Module;
+
+public class DynamicReadableWrapperTest extends AbstractDynamicWrapperTest {
+
+    @Override
+    protected AbstractDynamicWrapper getDynamicWrapper(Module module,
+            ModuleIdentifier moduleIdentifier) {
+        return new DynamicReadableWrapper(module, null, moduleIdentifier,
+                internalServer, ManagementFactory.getPlatformMBeanServer());
+    }
+
+}
diff --git a/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/impl/dynamicmbean/DynamicWritableWrapperTest.java b/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/impl/dynamicmbean/DynamicWritableWrapperTest.java
new file mode 100644 (file)
index 0000000..4c3e72f
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * 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.manager.impl.dynamicmbean;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import javax.management.Attribute;
+import javax.management.AttributeList;
+import javax.management.DynamicMBean;
+import javax.management.JMX;
+import javax.management.MBeanServerFactory;
+import javax.management.ObjectName;
+
+import org.junit.Test;
+import org.opendaylight.controller.config.api.ModuleIdentifier;
+import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
+import org.opendaylight.controller.config.manager.impl.TransactionIdentifier;
+import org.opendaylight.controller.config.manager.impl.dynamicmbean.ReadOnlyAtomicBoolean.ReadOnlyAtomicBooleanImpl;
+import org.opendaylight.controller.config.manager.testingservices.parallelapsp.TestingParallelAPSPConfigMXBean;
+import org.opendaylight.controller.config.manager.testingservices.parallelapsp.TestingParallelAPSPModule;
+import org.opendaylight.controller.config.manager.testingservices.parallelapsp.TestingParallelAPSPModuleFactory;
+import org.opendaylight.controller.config.manager.testingservices.threadpool.TestingFixedThreadPoolConfigMXBean;
+import org.opendaylight.controller.config.spi.Module;
+
+public class DynamicWritableWrapperTest extends AbstractDynamicWrapperTest {
+    private final int newThreadCount = 10;
+    private final AtomicBoolean atomicBoolean = new AtomicBoolean();
+    private final ReadOnlyAtomicBoolean readOnlyAtomicBoolean = new ReadOnlyAtomicBooleanImpl(
+            atomicBoolean);
+
+    @Override
+    protected AbstractDynamicWrapper getDynamicWrapper(Module module,
+            ModuleIdentifier moduleIdentifier) {
+        return new DynamicWritableWrapper(module, moduleIdentifier,
+                new TransactionIdentifier("transaction-1"),
+                readOnlyAtomicBoolean, MBeanServerFactory.createMBeanServer(),
+                platformMBeanServer);
+    }
+
+    @Test
+    public void testSetAttribute() throws Exception {
+        DynamicMBean proxy = JMX.newMBeanProxy(platformMBeanServer,
+                threadPoolDynamicWrapperON, DynamicMBean.class);
+
+        proxy.setAttribute(new Attribute(THREAD_COUNT, newThreadCount));
+
+        assertEquals(newThreadCount, proxy.getAttribute(THREAD_COUNT));
+        assertEquals(newThreadCount, threadPoolConfigBean.getThreadCount());
+
+        AttributeList attributeList = new AttributeList();
+        attributeList.add(new Attribute(THREAD_COUNT, threadCount));
+        boolean bool = true;
+        attributeList.add(new Attribute(TRIGGER_NEW_INSTANCE_CREATION, bool));
+        proxy.setAttributes(attributeList);
+
+        assertEquals(threadCount, threadPoolConfigBean.getThreadCount());
+        assertEquals(bool, threadPoolConfigBean.isTriggerNewInstanceCreation());
+    }
+
+    @Test
+    public void testSettersWithMXBeanProxy() {
+        TestingFixedThreadPoolConfigMXBean proxy = JMX.newMXBeanProxy(
+                platformMBeanServer, threadPoolDynamicWrapperON,
+                TestingFixedThreadPoolConfigMXBean.class);
+        proxy.setThreadCount(newThreadCount);
+        assertEquals(newThreadCount, threadPoolConfigBean.getThreadCount());
+    }
+
+    /*
+     * Try to call setter with ObjectName containing transaction name. Verify
+     * that ObjectName without transaction name was actually passed on the
+     * config bean.
+     */
+    @Test
+    public void testObjectNameSetterWithONContainingTransaction_shouldBeTranslatedToReadOnlyON()
+            throws Exception {
+        TestingParallelAPSPModuleFactory testingParallelAPSPConfigBeanFactory = new TestingParallelAPSPModuleFactory();
+        TestingParallelAPSPModule apspConfigBean = testingParallelAPSPConfigBeanFactory
+                .createModule("", null);
+        ModuleIdentifier moduleIdentifier2 = new ModuleIdentifier("apsp",
+                "parallel");
+        ObjectName dynON2 = ObjectNameUtil
+                .createReadOnlyModuleON(moduleIdentifier2);
+        AbstractDynamicWrapper dyn = getDynamicWrapper(apspConfigBean,
+                moduleIdentifier2);
+        platformMBeanServer.registerMBean(dyn, dynON2);
+        try {
+            TestingParallelAPSPConfigMXBean proxy = JMX.newMBeanProxy(
+                    platformMBeanServer, dynON2,
+                    TestingParallelAPSPConfigMXBean.class);
+            ObjectName withTransactionName = ObjectNameUtil
+                    .createTransactionModuleON("transaction1", "moduleName", "instanceName");
+            proxy.setThreadPool(withTransactionName);
+            ObjectName withoutTransactionName = ObjectNameUtil
+                    .withoutTransactionName(withTransactionName);
+            assertEquals(withoutTransactionName, proxy.getThreadPool());
+        } finally {
+            platformMBeanServer.unregisterMBean(dynON2);
+        }
+    }
+
+    private void setNumberOfThreads(int numberOfThreads) throws Exception {
+        DynamicMBean proxy = JMX.newMBeanProxy(platformMBeanServer,
+                threadPoolDynamicWrapperON, DynamicMBean.class);
+
+        proxy.setAttribute(new Attribute(THREAD_COUNT, numberOfThreads));
+
+    }
+
+    @Test
+    public void testDisablingOfWriteOperations() throws Exception {
+        setNumberOfThreads(newThreadCount);
+        atomicBoolean.set(true);
+        try {
+            setNumberOfThreads(newThreadCount);
+            fail();
+        } catch (IllegalStateException e) {
+            assertEquals("Operation is not allowed now", e.getMessage());
+        } finally {
+            atomicBoolean.set(false);
+        }
+
+    }
+
+}
diff --git a/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/impl/factoriesresolver/HardcodedModuleFactoriesResolver.java b/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/impl/factoriesresolver/HardcodedModuleFactoriesResolver.java
new file mode 100644 (file)
index 0000000..e489a22
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * 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.manager.impl.factoriesresolver;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.opendaylight.controller.config.spi.ModuleFactory;
+
+public class HardcodedModuleFactoriesResolver implements
+        ModuleFactoriesResolver {
+    private final List<? extends ModuleFactory> list;
+
+    public HardcodedModuleFactoriesResolver(ModuleFactory... list) {
+        this.list = Arrays.asList(list);
+    }
+
+    @Override
+    public List<? extends ModuleFactory> getAllFactories() {
+        return list;
+    }
+
+}
diff --git a/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/impl/factoriesresolver/HierarchicalConfigMBeanFactoriesHolderTest.java b/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/impl/factoriesresolver/HierarchicalConfigMBeanFactoriesHolderTest.java
new file mode 100644 (file)
index 0000000..7611b92
--- /dev/null
@@ -0,0 +1,11 @@
+/*
+ * 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.manager.impl.factoriesresolver;
+
+public class HierarchicalConfigMBeanFactoriesHolderTest {
+}
diff --git a/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/impl/runtimembean/RuntimeBeanRegistratorImplTest.java b/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/impl/runtimembean/RuntimeBeanRegistratorImplTest.java
new file mode 100644 (file)
index 0000000..ce3648d
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ * 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.manager.impl.runtimembean;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+import static org.junit.internal.matchers.StringContains.containsString;
+
+import java.lang.management.ManagementFactory;
+import java.util.Map;
+
+import javax.management.InstanceNotFoundException;
+import javax.management.ObjectName;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.config.api.ModuleIdentifier;
+import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
+import org.opendaylight.controller.config.api.runtime.HierarchicalRuntimeBeanRegistration;
+import org.opendaylight.controller.config.manager.impl.AbstractLockedPlatformMBeanServerTest;
+import org.opendaylight.controller.config.manager.impl.jmx.BaseJMXRegistrator;
+import org.opendaylight.controller.config.manager.impl.jmx.HierarchicalRuntimeBeanRegistrationImpl;
+import org.opendaylight.controller.config.manager.impl.jmx.RootRuntimeBeanRegistratorImpl;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+
+public class RuntimeBeanRegistratorImplTest extends
+        AbstractLockedPlatformMBeanServerTest {
+    static final String module1 = "module1";
+    static final String INSTANCE_NAME = "instanceName";
+    String additionalKey = "key";
+    String additionalValue = "value";
+    Map<String, String> additionalProperties = ImmutableMap.of(additionalKey,
+            additionalValue);
+
+    private BaseJMXRegistrator baseJMXRegistrator;
+    private RootRuntimeBeanRegistratorImpl tested;
+    private final ModuleIdentifier moduleIdentifier = new ModuleIdentifier(
+            module1, INSTANCE_NAME);
+
+    @Before
+    public void setUp() {
+        baseJMXRegistrator = new BaseJMXRegistrator(
+                ManagementFactory.getPlatformMBeanServer());
+        tested = baseJMXRegistrator
+                .createRuntimeBeanRegistrator(moduleIdentifier);
+    }
+
+    @After
+    public void tearDown() {
+        tested.close();
+        assertEquals(0, baseJMXRegistrator.getRegisteredObjectNames().size());
+    }
+
+    protected void checkExists(ObjectName on) throws Exception {
+        platformMBeanServer.getMBeanInfo(on);
+    }
+
+    protected void checkNotExists(ObjectName on) throws Exception {
+        try {
+            platformMBeanServer.getMBeanInfo(on);
+            fail();
+        } catch (InstanceNotFoundException e) {
+
+        }
+    }
+
+    @Test
+    public void testRegisterMBeanWithoutAdditionalProperties() throws Exception {
+        createRoot();
+    }
+
+    private HierarchicalRuntimeBeanRegistrationImpl createRoot()
+            throws Exception {
+        HierarchicalRuntimeBeanRegistrationImpl rootRegistration = tested
+                .registerRoot(new TestingRuntimeBean());
+
+        ObjectName expectedON1 = ObjectNameUtil.createRuntimeBeanName(module1,
+                INSTANCE_NAME, Maps.<String, String> newHashMap());
+
+        assertEquals(expectedON1, rootRegistration.getObjectName());
+        checkExists(rootRegistration.getObjectName());
+        return rootRegistration;
+    }
+
+    @Test
+    public void testRegisterMBeanWithAdditionalProperties() throws Exception {
+        HierarchicalRuntimeBeanRegistrationImpl rootRegistration = createRoot();
+        createAdditional(rootRegistration);
+    }
+
+    private HierarchicalRuntimeBeanRegistration createAdditional(
+            HierarchicalRuntimeBeanRegistrationImpl rootRegistration)
+            throws Exception {
+
+        HierarchicalRuntimeBeanRegistrationImpl registration = rootRegistration
+                .register(additionalKey, additionalValue, new TestingRuntimeBean());
+
+        ObjectName expectedON1 = ObjectNameUtil.createRuntimeBeanName(module1,
+                INSTANCE_NAME, additionalProperties);
+
+        assertEquals(expectedON1, registration.getObjectName());
+        checkExists(registration.getObjectName());
+        return registration;
+    }
+
+    @Test
+    public void testCloseRegistration() throws Exception {
+        HierarchicalRuntimeBeanRegistrationImpl rootRegistration = createRoot();
+        rootRegistration.close();
+        checkNotExists(rootRegistration.getObjectName());
+    }
+
+    @Test
+    public void testCloseRegistrator() throws Exception {
+        HierarchicalRuntimeBeanRegistrationImpl rootRegistration = createRoot();
+        HierarchicalRuntimeBeanRegistration childRegistration = createAdditional(rootRegistration);
+        tested.close();
+        checkNotExists(rootRegistration.getObjectName());
+        checkNotExists(childRegistration.getObjectName());
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testRegistration_overrideType() throws Exception {
+        HierarchicalRuntimeBeanRegistrationImpl rootRegistration = createRoot();
+        rootRegistration.register("type", "xxx", new TestingRuntimeBean());
+    }
+
+    @Test
+    public void testRegistrationException() throws Exception {
+        HierarchicalRuntimeBeanRegistrationImpl rootRegistration = createRoot();
+        try {
+            createRoot();
+            fail();
+        } catch (IllegalStateException e) {
+            assertThat(e.getMessage(), containsString(rootRegistration
+                    .getObjectName().toString()));
+            assertThat(e.getMessage(),
+                    containsString("Could not register runtime bean"));
+            assertThat(e.getMessage(),
+                    containsString(moduleIdentifier.toString()));
+        }
+    }
+
+    @Test
+    public void testIgnoringExceptionInClose() throws Exception {
+        HierarchicalRuntimeBeanRegistrationImpl rootRegistration = createRoot();
+        platformMBeanServer.unregisterMBean(rootRegistration.getObjectName());
+        rootRegistration.close();
+    }
+
+}
diff --git a/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/impl/runtimembean/TestingRuntimeBean.java b/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/impl/runtimembean/TestingRuntimeBean.java
new file mode 100644 (file)
index 0000000..96960d3
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * 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.manager.impl.runtimembean;
+
+public class TestingRuntimeBean implements TestingRuntimeBeanMXBean {
+
+    @Override
+    public int getStat() {
+        return 0;
+    }
+
+    @Override
+    public void setStat() {
+
+    }
+
+}
diff --git a/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/impl/runtimembean/TestingRuntimeBeanMXBean.java b/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/impl/runtimembean/TestingRuntimeBeanMXBean.java
new file mode 100644 (file)
index 0000000..3d4634f
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * 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.manager.impl.runtimembean;
+
+import org.opendaylight.controller.config.api.runtime.RuntimeBean;
+
+public interface TestingRuntimeBeanMXBean extends RuntimeBean {
+    int getStat();
+
+    void setStat();
+}
diff --git a/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/impl/util/InterfacesHelperTest.java b/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/impl/util/InterfacesHelperTest.java
new file mode 100644 (file)
index 0000000..6736ba1
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * 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.manager.impl.util;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.Set;
+
+import javax.management.MXBean;
+
+import org.junit.Test;
+import org.opendaylight.controller.config.spi.Module;
+
+import com.google.common.collect.Sets;
+
+public class InterfacesHelperTest {
+
+    interface SuperA {
+
+    }
+
+    interface SuperBMXBean {
+
+    }
+
+    interface SuperC extends SuperA, SuperBMXBean {
+
+    }
+
+    class SuperClass implements SuperC {
+
+    }
+
+    @MXBean
+    interface SubA {
+
+    }
+
+    abstract class SubClass extends SuperClass implements SubA, Module {
+
+    }
+
+    @Test
+    public void testGetAllInterfaces() {
+        Set<Class<?>> expected = Sets.<Class<?>> newHashSet(SuperA.class, SuperBMXBean.class, SuperC.class,
+                SubA.class, Module.class, org.opendaylight.protocol.concepts.NamedObject.class);
+        assertEquals(expected,
+                InterfacesHelper.getAllInterfaces(SubClass.class));
+    }
+
+    @Test
+    public void testGetMXInterfaces() {
+        Set<Class<?>> expected = Sets.<Class<?>> newHashSet(SuperBMXBean.class, SubA.class);
+        assertEquals(expected, InterfacesHelper.getMXInterfaces(SubClass.class));
+    }
+
+}
diff --git a/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/impl/util/ObjectNameUtilTest.java b/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/impl/util/ObjectNameUtilTest.java
new file mode 100644 (file)
index 0000000..0338039
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * 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.manager.impl.util;
+
+import java.util.Set;
+
+import javax.management.ObjectName;
+
+import org.junit.After;
+import org.junit.Before;
+import org.opendaylight.controller.config.manager.impl.AbstractLockedPlatformMBeanServerTest;
+
+import com.google.common.base.Throwables;
+import com.google.common.collect.Sets;
+
+public class ObjectNameUtilTest extends AbstractLockedPlatformMBeanServerTest {
+    private Set<ObjectName> unregisterONs;
+
+    @Before
+    public void initUnregisterList() {
+        unregisterONs = Sets.newHashSet();
+    }
+
+    @After
+    public void unregisterONs() {
+        Exception lastException = null;
+        for (ObjectName on : unregisterONs) {
+            try {
+                platformMBeanServer.unregisterMBean(on);
+            } catch (Exception e) {
+                lastException = e;
+            }
+        }
+        if (lastException != null) {
+            Throwables.propagate(lastException);
+        }
+    }
+}
diff --git a/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/parallelapsp/TestingAPSP.java b/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/parallelapsp/TestingAPSP.java
new file mode 100644 (file)
index 0000000..a4c1a6f
--- /dev/null
@@ -0,0 +1,14 @@
+/*
+ * 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.manager.testingservices.parallelapsp;
+
+public interface TestingAPSP {
+
+    int getMaxNumberOfThreads();
+
+}
diff --git a/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/parallelapsp/TestingParallelAPSPConfigMXBean.java b/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/parallelapsp/TestingParallelAPSPConfigMXBean.java
new file mode 100644 (file)
index 0000000..4fd2f5f
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * 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.manager.testingservices.parallelapsp;
+
+import javax.management.ObjectName;
+
+import org.opendaylight.controller.config.api.annotations.ServiceInterfaceAnnotation;
+
+@ServiceInterfaceAnnotation(value = TestingParallelAPSPConfigMXBean.NAME, osgiRegistrationType = TestingAPSP.class)
+public interface TestingParallelAPSPConfigMXBean {
+
+    static final String NAME = "apsp";
+
+    ObjectName getThreadPool();
+
+    void setThreadPool(ObjectName threadPoolName);
+
+    String getSomeParam();
+
+    void setSomeParam(String s);
+
+    // for reporting. this should be moved to runtime jmx bean
+    Integer getMaxNumberOfThreads();
+
+}
diff --git a/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/parallelapsp/TestingParallelAPSPImpl.java b/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/parallelapsp/TestingParallelAPSPImpl.java
new file mode 100644 (file)
index 0000000..0c29386
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * 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.manager.testingservices.parallelapsp;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import java.io.Closeable;
+import java.io.IOException;
+
+import javax.annotation.concurrent.NotThreadSafe;
+
+import org.opendaylight.controller.config.manager.testingservices.threadpool.TestingThreadPoolIfc;
+
+import com.google.common.base.Strings;
+
+@NotThreadSafe
+public class TestingParallelAPSPImpl implements TestingAPSP, Closeable {
+    public static final int MINIMAL_NUMBER_OF_THREADS = 10;
+    private TestingThreadPoolIfc threadPool;
+    private String someParam;
+
+    public TestingParallelAPSPImpl(TestingThreadPoolIfc threadPool,
+            String someParam) {
+        checkArgument(
+                threadPool.getMaxNumberOfThreads() >= MINIMAL_NUMBER_OF_THREADS,
+                "Parameter 'threadPool' has not enough threads");
+        checkArgument(Strings.isNullOrEmpty(someParam) == false,
+                "Parameter 'someParam' is blank");
+        this.threadPool = threadPool;
+        this.someParam = someParam;
+    }
+
+    @Override
+    public int getMaxNumberOfThreads() {
+        return threadPool.getMaxNumberOfThreads();
+    }
+
+    @Override
+    public void close() throws IOException {
+
+    }
+
+    TestingThreadPoolIfc getThreadPool() {
+        return threadPool;
+    }
+
+    void setSomeParam(String s) {
+        checkArgument(Strings.isNullOrEmpty(someParam) == false,
+                "Parameter 'someParam' is blank");
+        this.someParam = s;
+    }
+
+    public String getSomeParam() {
+        return someParam;
+    }
+
+}
diff --git a/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/parallelapsp/TestingParallelAPSPModule.java b/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/parallelapsp/TestingParallelAPSPModule.java
new file mode 100644 (file)
index 0000000..de283fe
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * 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.manager.testingservices.parallelapsp;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+
+import java.io.Closeable;
+
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.NotThreadSafe;
+import javax.management.ObjectName;
+
+import org.opendaylight.controller.config.api.DependencyResolver;
+import org.opendaylight.controller.config.api.ModuleIdentifier;
+import org.opendaylight.controller.config.api.annotations.RequireInterface;
+import org.opendaylight.controller.config.manager.testingservices.seviceinterface.TestingThreadPoolServiceInterface;
+import org.opendaylight.controller.config.manager.testingservices.threadpool.TestingThreadPoolIfc;
+import org.opendaylight.controller.config.spi.Module;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Strings;
+
+/**
+ * Represents service that has dependency to thread pool.
+ */
+@NotThreadSafe
+public class TestingParallelAPSPModule implements Module,
+        TestingParallelAPSPConfigMXBean {
+    private static final Logger logger = LoggerFactory
+            .getLogger(TestingParallelAPSPModule.class);
+
+    private final DependencyResolver dependencyResolver;
+    private final AutoCloseable oldCloseable;
+    private final TestingParallelAPSPImpl oldInstance;
+    private final ModuleIdentifier name;
+    private ObjectName threadPoolON;
+    private TestingParallelAPSPImpl instance;
+    private String someParam;
+
+    public TestingParallelAPSPModule(ModuleIdentifier name,
+            DependencyResolver dependencyResolver,
+            @Nullable AutoCloseable oldCloseable,
+            @Nullable TestingParallelAPSPImpl oldInstance) {
+        this.name = name;
+        this.dependencyResolver = dependencyResolver;
+        this.oldCloseable = oldCloseable;
+        this.oldInstance = oldInstance;
+    }
+
+    @Override
+    public ModuleIdentifier getName() {
+        return name;
+    }
+
+    @Override
+    public ObjectName getThreadPool() {
+        return threadPoolON;
+    }
+
+    @RequireInterface(TestingThreadPoolServiceInterface.class)
+    @Override
+    public void setThreadPool(ObjectName threadPoolName) {
+        this.threadPoolON = threadPoolName;
+    }
+
+    @Override
+    public String getSomeParam() {
+        return someParam;
+    }
+
+    @Override
+    public void setSomeParam(String someParam) {
+        this.someParam = someParam;
+    }
+
+    @Override
+    public Integer getMaxNumberOfThreads() {
+        if (instance == null)
+            return null;
+        return instance.getMaxNumberOfThreads();
+    }
+
+    @Override
+    public void validate() {
+        checkNotNull(threadPoolON, "Parameter 'threadPool' must be set");
+        dependencyResolver.validateDependency(
+                TestingThreadPoolServiceInterface.class, threadPoolON,
+                "threadPoolON");
+
+        checkState(Strings.isNullOrEmpty(someParam) == false,
+                "Parameter 'SomeParam' is blank");
+        // check that calling resolveInstance fails
+        try {
+            dependencyResolver.resolveInstance(TestingThreadPoolIfc.class,
+                    threadPoolON);
+            throw new RuntimeException("fail");
+        } catch (IllegalStateException e) {
+            checkState("Commit was not triggered".equals(e.getMessage()),
+                    e.getMessage());
+        }
+    }
+
+    @Override
+    public Closeable getInstance() {
+        if (instance == null) {
+            TestingThreadPoolIfc threadPoolInstance = dependencyResolver
+                    .resolveInstance(TestingThreadPoolIfc.class, threadPoolON);
+
+            if (oldInstance != null) {
+                // changing thread pool is not supported
+                boolean reuse = threadPoolInstance.equals(oldInstance
+                        .getThreadPool());
+                if (reuse) {
+                    logger.debug("Reusing old instance");
+                    instance = oldInstance;
+                    instance.setSomeParam(someParam);
+                }
+            }
+            if (instance == null) {
+                logger.debug("Creating new instance");
+                if (oldCloseable != null) {
+                    try {
+                        oldCloseable.close();
+                    } catch (Exception e) {
+                        throw new RuntimeException(e);
+                    }
+                }
+                instance = new TestingParallelAPSPImpl(threadPoolInstance,
+                        someParam);
+            }
+        }
+        return instance;
+    }
+}
diff --git a/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/parallelapsp/TestingParallelAPSPModuleFactory.java b/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/parallelapsp/TestingParallelAPSPModuleFactory.java
new file mode 100644 (file)
index 0000000..0a53d75
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * 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.manager.testingservices.parallelapsp;
+
+import javax.annotation.concurrent.ThreadSafe;
+import javax.management.ObjectName;
+
+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.spi.ModuleFactory;
+
+@ThreadSafe
+public class TestingParallelAPSPModuleFactory implements ModuleFactory {
+
+    public static final String NAME = "parallel";
+
+    @Override
+    public String getImplementationName() {
+        return NAME;
+    }
+
+    @Override
+    public TestingParallelAPSPModule createModule(String instanceName,
+            DependencyResolver dependencyResolver) {
+        return new TestingParallelAPSPModule(new ModuleIdentifier(NAME,
+                instanceName), dependencyResolver, null, null);
+    }
+
+    @Override
+    public TestingParallelAPSPModule createModule(String instanceName,
+            DependencyResolver dependencyResolver, DynamicMBeanWithInstance old)
+            throws Exception {
+        TestingParallelAPSPImpl oldInstance;
+        try {
+            oldInstance = (TestingParallelAPSPImpl) old.getInstance();
+        } catch (ClassCastException e) {
+            oldInstance = null;
+        }
+        TestingParallelAPSPModule result = new TestingParallelAPSPModule(
+                new ModuleIdentifier(NAME, instanceName), dependencyResolver,
+                old.getInstance(), oldInstance);
+        // copy attributes
+        String someParam = (String) old.getAttribute("SomeParam");
+        result.setSomeParam(someParam);
+        ObjectName threadPool = (ObjectName) old.getAttribute("ThreadPool");
+        result.setThreadPool(threadPool);
+        return result;
+    }
+
+    @Override
+    public boolean isModuleImplementingServiceInterface(
+            Class<? extends AbstractServiceInterface> serviceInterface) {
+        return false;
+    }
+}
diff --git a/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/parallelapsp/package-info.java b/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/parallelapsp/package-info.java
new file mode 100644 (file)
index 0000000..f37aa5a
--- /dev/null
@@ -0,0 +1,12 @@
+/*
+ * 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
+ */
+/**
+ * Creates simple bean that has dependency on
+ * {@link org.opendaylight.controller.config.manager.testingservices.threadpool.TestingThreadPoolIfc}.
+ */
+package org.opendaylight.controller.config.manager.testingservices.parallelapsp;
diff --git a/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/parallelapsp/test/AbstractParallelAPSPTest.java b/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/parallelapsp/test/AbstractParallelAPSPTest.java
new file mode 100644 (file)
index 0000000..7deb1ef
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * 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.manager.testingservices.parallelapsp.test;
+
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.ObjectName;
+
+import org.opendaylight.controller.config.manager.impl.AbstractConfigWithJolokiaTest;
+import org.opendaylight.controller.config.manager.testingservices.parallelapsp.TestingParallelAPSPConfigMXBean;
+import org.opendaylight.controller.config.manager.testingservices.parallelapsp.TestingParallelAPSPModuleFactory;
+import org.opendaylight.controller.config.manager.testingservices.threadpool.TestingFixedThreadPoolConfigMXBean;
+import org.opendaylight.controller.config.util.ConfigTransactionJMXClient;
+
+abstract class AbstractParallelAPSPTest extends AbstractConfigWithJolokiaTest {
+    protected final String fixed1 = "fixed1";
+    protected final String apsp1 = "apsp-parallel";
+
+    abstract String getThreadPoolImplementationName();
+
+    protected ObjectName createParallelAPSP(
+            ConfigTransactionJMXClient transaction, ObjectName threadPoolON)
+            throws InstanceAlreadyExistsException {
+        ObjectName apspName = transaction.createModule(
+                TestingParallelAPSPModuleFactory.NAME, apsp1);
+        TestingParallelAPSPConfigMXBean parallelAPSPConfigProxy = transaction
+                .newMXBeanProxy(apspName, TestingParallelAPSPConfigMXBean.class);
+        parallelAPSPConfigProxy.setSomeParam("ahoj");
+        parallelAPSPConfigProxy.setThreadPool(threadPoolON);
+        return apspName;
+    }
+
+    protected ObjectName createFixed1(ConfigTransactionJMXClient transaction,
+            int numberOfThreads) throws InstanceAlreadyExistsException {
+        ObjectName name = transaction.createModule(
+                getThreadPoolImplementationName(), fixed1);
+
+        TestingFixedThreadPoolConfigMXBean fixedConfigProxy = transaction
+                .newMXBeanProxy(name, TestingFixedThreadPoolConfigMXBean.class);
+        fixedConfigProxy.setThreadCount(numberOfThreads);
+
+        return name;
+    }
+}
diff --git a/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/parallelapsp/test/DependentWiringTest.java b/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/parallelapsp/test/DependentWiringTest.java
new file mode 100644 (file)
index 0000000..5b1a6f6
--- /dev/null
@@ -0,0 +1,178 @@
+/*
+ * 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.manager.testingservices.parallelapsp.test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.junit.internal.matchers.StringContains.containsString;
+
+import java.util.Map;
+
+import javax.management.ObjectName;
+
+import org.json.simple.JSONObject;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.config.api.ValidationException;
+import org.opendaylight.controller.config.api.ValidationException.ExceptionMessageWithStackTrace;
+import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
+import org.opendaylight.controller.config.manager.impl.factoriesresolver.HardcodedModuleFactoriesResolver;
+import org.opendaylight.controller.config.manager.testingservices.parallelapsp.TestingParallelAPSPConfigMXBean;
+import org.opendaylight.controller.config.manager.testingservices.parallelapsp.TestingParallelAPSPImpl;
+import org.opendaylight.controller.config.manager.testingservices.parallelapsp.TestingParallelAPSPModuleFactory;
+import org.opendaylight.controller.config.manager.testingservices.threadpool.TestingFixedThreadPool;
+import org.opendaylight.controller.config.manager.testingservices.threadpool.TestingFixedThreadPoolConfigMXBean;
+import org.opendaylight.controller.config.manager.testingservices.threadpool.TestingFixedThreadPoolModuleFactory;
+import org.opendaylight.controller.config.util.ConfigTransactionJMXClient;
+import org.opendaylight.controller.config.util.jolokia.ConfigTransactionJolokiaClient;
+
+public class DependentWiringTest extends AbstractParallelAPSPTest {
+    private final String fixed1 = "fixed1";
+    private final String apsp1 = "apsp-parallel";
+
+    @Before
+    public void setUp() {
+        super.initConfigTransactionManagerImpl(new HardcodedModuleFactoriesResolver(
+                new TestingFixedThreadPoolModuleFactory(),
+                new TestingParallelAPSPModuleFactory()));
+    }
+
+    @After
+    public void tearDown() {
+        TestingFixedThreadPool.cleanUp();
+    }
+
+    @Override
+    String getThreadPoolImplementationName() {
+        return TestingFixedThreadPoolModuleFactory.NAME;
+    }
+
+    @Test
+    public void testDependencies() throws Exception {
+        ObjectName apspON;
+        {
+            ConfigTransactionJMXClient transaction = configRegistryClient
+                    .createTransaction();
+            // create fixed1
+            ObjectName threadPoolTransactionON = createFixed1(transaction,
+                    TestingParallelAPSPImpl.MINIMAL_NUMBER_OF_THREADS);
+            // create apsp-parallel
+            ObjectName apspNameTransactionON = createParallelAPSP(transaction,
+                    threadPoolTransactionON);
+            TestingParallelAPSPConfigMXBean parallelAPSPConfigProxy = transaction
+                    .newMXBeanProxy(apspNameTransactionON, TestingParallelAPSPConfigMXBean.class);
+            parallelAPSPConfigProxy.setSomeParam("");// trigger validation
+                                                     // failure
+            try {
+                transaction.validateConfig();
+                fail();
+            } catch (ValidationException e) {
+                for (Map.Entry<String, Map<String, ExceptionMessageWithStackTrace>> exception : e
+                        .getFailedValidations().entrySet()) {
+                    for (Map.Entry<String, ExceptionMessageWithStackTrace> entry : exception
+                            .getValue().entrySet()) {
+                        assertThat(
+                                entry.getValue().getMessage(),
+                                containsString("Parameter 'SomeParam' is blank"));
+                    }
+                }
+            }
+
+            // try committing (validation fails)
+            try {
+                transaction.commit();
+                fail();
+            } catch (ValidationException e) {
+                for (Map.Entry<String, Map<String, ExceptionMessageWithStackTrace>> exception : e
+                        .getFailedValidations().entrySet()) {
+                    for (Map.Entry<String, ExceptionMessageWithStackTrace> entry : exception
+                            .getValue().entrySet()) {
+                        String err = entry.getValue().getMessage();
+                        assertTrue("Unexpected error message: " + err,
+                                err.contains("Parameter 'SomeParam' is blank"));
+                    }
+                }
+            }
+
+            parallelAPSPConfigProxy.setSomeParam("abc");// fix validation
+                                                        // failure
+            transaction.commit();
+            apspON = ObjectNameUtil
+                    .withoutTransactionName(apspNameTransactionON);
+        }
+
+        // test reported apsp number of threads
+        TestingParallelAPSPConfigMXBean parallelAPSPRuntimeProxy = configRegistryClient
+                .newMBeanProxy(apspON, TestingParallelAPSPConfigMXBean.class);
+        assertEquals(
+                (Integer) TestingParallelAPSPImpl.MINIMAL_NUMBER_OF_THREADS,
+                parallelAPSPRuntimeProxy.getMaxNumberOfThreads());
+
+        // next transaction - recreate new thread pool
+        int newNumberOfThreads = TestingParallelAPSPImpl.MINIMAL_NUMBER_OF_THREADS * 2;
+        {
+            // start new transaction
+            ConfigTransactionJMXClient transaction = configRegistryClient
+                    .createTransaction();
+            ObjectName threadPoolNames_newTx = transaction.lookupConfigBean(
+                    getThreadPoolImplementationName(), fixed1);
+            TestingFixedThreadPoolConfigMXBean fixedConfigTransactionProxy = transaction
+                    .newMXBeanProxy(threadPoolNames_newTx, TestingFixedThreadPoolConfigMXBean.class);
+            fixedConfigTransactionProxy.setThreadCount(newNumberOfThreads);
+
+            transaction.commit();
+        }
+        // new reference should be copied to apsp-parallel
+        assertEquals((Integer) newNumberOfThreads,
+                parallelAPSPRuntimeProxy.getMaxNumberOfThreads());
+
+    }
+
+    @Test
+    public void testUsingJolokia() throws Exception {
+
+        ConfigTransactionJolokiaClient transactionClient = configRegistryJolokiaClient
+                .createTransaction();
+        // fixed1
+        ObjectName fixed1ON = transactionClient.createModule(
+                getThreadPoolImplementationName(), fixed1);
+        transactionClient.setAttribute(fixed1ON, "ThreadCount",
+                TestingParallelAPSPImpl.MINIMAL_NUMBER_OF_THREADS);
+
+        // apsp-parallel with syntetic attrib
+        String threadPoolString = "ThreadPool";
+        ObjectName apsp1ON = transactionClient.createModule(
+                TestingParallelAPSPModuleFactory.NAME, apsp1);
+        transactionClient.setAttribute(apsp1ON, threadPoolString, fixed1ON);
+        // check
+        assertEquals(ObjectNameUtil.withoutTransactionName(fixed1ON),
+                transactionClient.getAttributeON(apsp1ON, threadPoolString));
+        transactionClient.setAttribute(apsp1ON, "SomeParam", "ahoj");
+
+        // commit
+        transactionClient.commit();
+        // check thread pool
+        assertEquals(1, TestingFixedThreadPool.allExecutors.size());
+        // check platform MBeanServer
+        ObjectName apspReadOnlyON = ObjectNameUtil
+                .withoutTransactionName(apsp1ON);
+        JSONObject threadPoolONJson = (JSONObject) configRegistryJolokiaClient
+                .getAttribute(apspReadOnlyON, threadPoolString);
+        ObjectName fixed1ReadOnlyON = ObjectNameUtil
+                .withoutTransactionName(fixed1ON);
+        assertEquals(fixed1ReadOnlyON, ObjectNameUtil.createON(threadPoolONJson
+                .get("objectName").toString()));
+        assertEquals(fixed1ReadOnlyON,
+                configRegistryJolokiaClient.getAttributeON(apspReadOnlyON,
+                        threadPoolString));
+
+    }
+}
diff --git a/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/parallelapsp/test/MockedDependenciesTest.java b/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/parallelapsp/test/MockedDependenciesTest.java
new file mode 100644 (file)
index 0000000..c27e01f
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * 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.manager.testingservices.parallelapsp.test;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.concurrent.Executor;
+
+import javax.management.ObjectName;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.config.api.DynamicMBeanWithInstance;
+import org.opendaylight.controller.config.api.ModuleIdentifier;
+import org.opendaylight.controller.config.manager.impl.ClassBasedModuleFactory;
+import org.opendaylight.controller.config.manager.impl.factoriesresolver.HardcodedModuleFactoriesResolver;
+import org.opendaylight.controller.config.manager.testingservices.parallelapsp.TestingParallelAPSPImpl;
+import org.opendaylight.controller.config.manager.testingservices.parallelapsp.TestingParallelAPSPModuleFactory;
+import org.opendaylight.controller.config.manager.testingservices.seviceinterface.TestingThreadPoolServiceInterface;
+import org.opendaylight.controller.config.manager.testingservices.threadpool.TestingThreadPoolConfigMXBean;
+import org.opendaylight.controller.config.manager.testingservices.threadpool.TestingThreadPoolIfc;
+import org.opendaylight.controller.config.spi.Module;
+import org.opendaylight.controller.config.util.ConfigTransactionJMXClient;
+
+public class MockedDependenciesTest extends AbstractParallelAPSPTest {
+    private final String threadPoolImplementationName = "mockedthreadpool";
+
+    @Before
+    public void setUp() {
+
+        ClassBasedModuleFactory mockedThreadPoolConfigFactory = new ClassBasedModuleFactory(
+                threadPoolImplementationName, MockedThreadPoolModule.class);
+
+        super.initConfigTransactionManagerImpl(new HardcodedModuleFactoriesResolver(
+                new TestingParallelAPSPModuleFactory(),
+                mockedThreadPoolConfigFactory));
+    }
+
+    public static interface MockedTestingThreadPoolConfigMXBean extends
+            TestingThreadPoolConfigMXBean {
+        public void setThreadCount(int threadCount);
+    }
+
+    public static class MockedThreadPoolModule implements Module,
+            MockedTestingThreadPoolConfigMXBean,
+            TestingThreadPoolServiceInterface {
+        int threadCount;
+
+        public MockedThreadPoolModule() {
+        }
+
+        public MockedThreadPoolModule(
+                DynamicMBeanWithInstance dynamicMBeanWithInstance) {
+            // no reconfiguration / reuse is supported
+        }
+
+        @Override
+        public ModuleIdentifier getName() {
+            return new ModuleIdentifier("a", "b");
+        }
+
+        @Override
+        public int getThreadCount() {
+            return threadCount;
+        }
+
+        @Override
+        public void setThreadCount(int threadCount) {
+            this.threadCount = threadCount;
+        }
+
+        @Override
+        public void validate() {
+
+        }
+
+        @Override
+        public Closeable getInstance() {
+            return new MockedThreadPool(threadCount);
+        }
+    }
+
+    public static class MockedThreadPool implements TestingThreadPoolIfc,
+            Closeable {
+        private final int threadCount;
+
+        public MockedThreadPool(int threadCount) {
+            this.threadCount = threadCount;
+        }
+
+        @Override
+        public Executor getExecutor() {
+            return null;
+        }
+
+        @Override
+        public int getMaxNumberOfThreads() {
+            return threadCount;
+        }
+
+        @Override
+        public void close() throws IOException {
+
+        }
+    }
+
+    @Override
+    String getThreadPoolImplementationName() {
+        return threadPoolImplementationName;
+    }
+
+    @Test
+    public void testDependencies() throws Exception {
+        ConfigTransactionJMXClient transaction = configRegistryClient
+                .createTransaction();
+        // create fixed1
+        ObjectName threadPoolTransactionON = createFixed1(transaction,
+                TestingParallelAPSPImpl.MINIMAL_NUMBER_OF_THREADS);
+        // create apsp-parallel
+        createParallelAPSP(transaction, threadPoolTransactionON);
+
+        transaction.commit();
+    }
+
+}
diff --git a/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/scheduledthreadpool/TestingScheduledThreadPoolConfigBeanMXBean.java b/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/scheduledthreadpool/TestingScheduledThreadPoolConfigBeanMXBean.java
new file mode 100644 (file)
index 0000000..df86232
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * 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.manager.testingservices.scheduledthreadpool;
+
+import org.opendaylight.controller.config.manager.testingservices.threadpool.TestingThreadPoolConfigMXBean;
+
+public interface TestingScheduledThreadPoolConfigBeanMXBean extends
+        TestingThreadPoolConfigMXBean {
+
+    public boolean isRecreate();
+
+    public void setRecreate(boolean recreate);
+}
diff --git a/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/scheduledthreadpool/TestingScheduledThreadPoolIfc.java b/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/scheduledthreadpool/TestingScheduledThreadPoolIfc.java
new file mode 100644 (file)
index 0000000..3437eca
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * 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.manager.testingservices.scheduledthreadpool;
+
+import java.util.concurrent.ScheduledExecutorService;
+
+public interface TestingScheduledThreadPoolIfc {
+
+    ScheduledExecutorService getScheduledExecutor();
+
+}
diff --git a/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/scheduledthreadpool/TestingScheduledThreadPoolImpl.java b/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/scheduledthreadpool/TestingScheduledThreadPoolImpl.java
new file mode 100644 (file)
index 0000000..fb97b64
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * 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.manager.testingservices.scheduledthreadpool;
+
+import java.io.Closeable;
+import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+
+import org.opendaylight.controller.config.api.runtime.HierarchicalRuntimeBeanRegistration;
+import org.opendaylight.controller.config.api.runtime.RootRuntimeBeanRegistrator;
+import org.opendaylight.controller.config.manager.testingservices.scheduledthreadpool.runtimebeans
+        .TestingScheduledRuntimeBean;
+import org.opendaylight.controller.config.manager.testingservices.threadpool.TestingThreadPoolIfc;
+
+import com.google.common.collect.Lists;
+
+public class TestingScheduledThreadPoolImpl implements TestingThreadPoolIfc,
+        TestingScheduledThreadPoolIfc, Closeable {
+    private static volatile int numberOfCloseMethodCalls = 0;
+    private final ScheduledThreadPoolExecutor executor;
+    private final RootRuntimeBeanRegistrator runtimeBeanRegistrator;
+
+    public static final List<ScheduledThreadPoolExecutor> allExecutors = Lists
+            .newLinkedList();
+
+    public TestingScheduledThreadPoolImpl(
+            RootRuntimeBeanRegistrator runtimeBeanRegistrator, int corePoolSize) {
+        this.runtimeBeanRegistrator = runtimeBeanRegistrator;
+        executor = new ScheduledThreadPoolExecutor(corePoolSize);
+        allExecutors.add(executor);
+        HierarchicalRuntimeBeanRegistration hierarchicalRuntimeBeanRegistration = runtimeBeanRegistrator
+                .registerRoot(new TestingScheduledRuntimeBean());
+        hierarchicalRuntimeBeanRegistration.register("a", "b",
+                new TestingScheduledRuntimeBean());
+    }
+
+    @Override
+    public void close() {
+        numberOfCloseMethodCalls++;
+        runtimeBeanRegistrator.close();
+        executor.shutdown();
+    }
+
+    @Override
+    public ScheduledExecutorService getScheduledExecutor() {
+        return executor;
+    }
+
+    @Override
+    public Executor getExecutor() {
+        return executor;
+    }
+
+    @Override
+    public int getMaxNumberOfThreads() {
+        return executor.getCorePoolSize();
+    }
+
+    public static void cleanUp() {
+        for (ScheduledThreadPoolExecutor executor : allExecutors) {
+            executor.shutdown();
+        }
+        allExecutors.clear();
+        numberOfCloseMethodCalls = 0;
+    }
+
+    public static int getNumberOfCloseMethodCalls() {
+        return numberOfCloseMethodCalls;
+    }
+
+}
diff --git a/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/scheduledthreadpool/TestingScheduledThreadPoolModule.java b/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/scheduledthreadpool/TestingScheduledThreadPoolModule.java
new file mode 100644 (file)
index 0000000..4716d58
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * 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.manager.testingservices.scheduledthreadpool;
+
+import static com.google.common.base.Preconditions.checkState;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import java.io.Closeable;
+
+import javax.annotation.Nullable;
+
+import org.opendaylight.controller.config.api.ModuleIdentifier;
+import org.opendaylight.controller.config.api.RuntimeBeanRegistratorAwareModule;
+import org.opendaylight.controller.config.api.runtime.RootRuntimeBeanRegistrator;
+import org.opendaylight.controller.config.manager.testingservices.seviceinterface.TestingScheduledThreadPoolServiceInterface;
+import org.opendaylight.controller.config.spi.Module;
+
+/**
+ * This class has two exported interfaces and two runtime beans. Recreation is
+ * triggered by setting Recreate attribute to true.
+ */
+public class TestingScheduledThreadPoolModule implements Module,
+        TestingScheduledThreadPoolConfigBeanMXBean,
+        RuntimeBeanRegistratorAwareModule,
+        TestingScheduledThreadPoolServiceInterface {
+
+    private final ModuleIdentifier name;
+    @Nullable
+    private final AutoCloseable oldCloseable;
+    @Nullable
+    private final TestingScheduledThreadPoolImpl oldInstance;
+
+    private final int threadCount = 10;
+    private TestingScheduledThreadPoolImpl instance;
+    private RootRuntimeBeanRegistrator runtimeBeanRegistrator;
+    private boolean recreate;
+
+    public TestingScheduledThreadPoolModule(ModuleIdentifier name,
+            @Nullable AutoCloseable oldCloseable,
+            @Nullable TestingScheduledThreadPoolImpl oldInstance) {
+        this.name = name;
+        this.oldCloseable = oldCloseable;
+        this.oldInstance = oldInstance;
+    }
+
+    @Override
+    public ModuleIdentifier getName() {
+        return name;
+    }
+
+    @Override
+    public void setRuntimeBeanRegistrator(
+            RootRuntimeBeanRegistrator runtimeBeanRegistrator) {
+        this.runtimeBeanRegistrator = runtimeBeanRegistrator;
+    }
+
+    @Override
+    public void validate() {
+        assertNull(runtimeBeanRegistrator);
+        // check thread count
+        checkState(threadCount > 0,
+                "Parameter 'ThreadCount' must be greater than 0");
+    }
+
+    @Override
+    public int getThreadCount() {
+        return threadCount;
+    }
+
+    @Override
+    public Closeable getInstance() {
+        assertNotNull(runtimeBeanRegistrator);
+        if (instance == null) {
+            if (oldInstance != null && recreate == false) {
+                // reuse old instance
+                instance = oldInstance;
+            }
+            if (instance == null) {
+                if (oldCloseable != null) {
+                    try {
+                        oldCloseable.close();
+                    } catch (Exception e) {
+                        throw new RuntimeException(e);
+                    }
+                }
+                // close old threadpool and esp. unregister runtime beans
+                instance = new TestingScheduledThreadPoolImpl(
+                        runtimeBeanRegistrator, threadCount);
+            }
+        }
+        return instance;
+    }
+
+    // getters and setters
+    @Override
+    public boolean isRecreate() {
+        return recreate;
+    }
+
+    @Override
+    public void setRecreate(boolean recreate) {
+        this.recreate = recreate;
+    }
+
+}
diff --git a/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/scheduledthreadpool/TestingScheduledThreadPoolModuleFactory.java b/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/scheduledthreadpool/TestingScheduledThreadPoolModuleFactory.java
new file mode 100644 (file)
index 0000000..8244bc1
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * 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.manager.testingservices.scheduledthreadpool;
+
+import java.util.Arrays;
+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.manager.testingservices.seviceinterface
+        .TestingScheduledThreadPoolServiceInterface;
+import org.opendaylight.controller.config.manager.testingservices.seviceinterface.TestingThreadPoolServiceInterface;
+import org.opendaylight.controller.config.spi.Module;
+import org.opendaylight.controller.config.spi.ModuleFactory;
+
+public class TestingScheduledThreadPoolModuleFactory implements ModuleFactory {
+    public static final String NAME = "scheduled";
+
+    private static List<Class<? extends TestingThreadPoolServiceInterface>> ifc = Arrays
+            .asList(TestingScheduledThreadPoolServiceInterface.class, TestingThreadPoolServiceInterface.class);
+
+    @Override
+    public boolean isModuleImplementingServiceInterface(
+            Class<? extends AbstractServiceInterface> serviceInterface) {
+        return ifc.contains(serviceInterface);
+    }
+
+    @Override
+    public String getImplementationName() {
+        return NAME;
+    }
+
+    @Override
+    public Module createModule(String instanceName,
+            DependencyResolver dependencyResolver) {
+        return new TestingScheduledThreadPoolModule(new ModuleIdentifier(NAME,
+                instanceName), null, null);
+    }
+
+    @Override
+    public Module createModule(String instanceName,
+            DependencyResolver dependencyResolver, DynamicMBeanWithInstance old)
+            throws Exception {
+        TestingScheduledThreadPoolImpl oldInstance;
+        try {
+            oldInstance = (TestingScheduledThreadPoolImpl) old.getInstance();
+        } catch (ClassCastException e) {// happens after OSGi update
+            oldInstance = null;
+        }
+
+        TestingScheduledThreadPoolModule configBean = new TestingScheduledThreadPoolModule(
+                new ModuleIdentifier(NAME, instanceName), old.getInstance(),
+                oldInstance);
+        // copy attributes
+        configBean.setRecreate((Boolean) old.getAttribute("Recreate"));
+        return configBean;
+    }
+
+}
diff --git a/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/scheduledthreadpool/package-info.java b/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/scheduledthreadpool/package-info.java
new file mode 100644 (file)
index 0000000..998627e
--- /dev/null
@@ -0,0 +1,12 @@
+/*
+ * 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
+ */
+/**
+ * Tests config bean that exports two independent interfaces - {@link org.opendaylight.controller.config.manager.testingservices.scheduledthreadpool.TestingScheduledThreadPoolIfc} and
+ * {@link org.opendaylight.controller.config.manager.testingservices.threadpool.TestingThreadPoolIfc}.
+ */
+package org.opendaylight.controller.config.manager.testingservices.scheduledthreadpool;
diff --git a/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/scheduledthreadpool/runtimebeans/TestingScheduledRuntimeBean.java b/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/scheduledthreadpool/runtimebeans/TestingScheduledRuntimeBean.java
new file mode 100644 (file)
index 0000000..c16e95d
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * 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.manager.testingservices.scheduledthreadpool.runtimebeans;
+
+public class TestingScheduledRuntimeBean implements
+        TestingScheduledRuntimeMXBean {
+
+    public TestingScheduledRuntimeBean() {
+    }
+
+    @Override
+    public int getActualNumberOfThreads() {
+        return 0;
+    }
+
+}
diff --git a/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/scheduledthreadpool/runtimebeans/TestingScheduledRuntimeMXBean.java b/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/scheduledthreadpool/runtimebeans/TestingScheduledRuntimeMXBean.java
new file mode 100644 (file)
index 0000000..31902df
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * 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.manager.testingservices.scheduledthreadpool.runtimebeans;
+
+import org.opendaylight.controller.config.api.runtime.RuntimeBean;
+
+public interface TestingScheduledRuntimeMXBean extends RuntimeBean {
+
+    int getActualNumberOfThreads();
+
+}
diff --git a/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/scheduledthreadpool/test/AbstractScheduledTest.java b/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/scheduledthreadpool/test/AbstractScheduledTest.java
new file mode 100644 (file)
index 0000000..28fea45
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * 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.manager.testingservices.scheduledthreadpool.test;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.After;
+import org.junit.Before;
+import org.opendaylight.controller.config.manager.impl.AbstractConfigTest;
+import org.opendaylight.controller.config.manager.impl.factoriesresolver.HardcodedModuleFactoriesResolver;
+import org.opendaylight.controller.config.manager.testingservices.parallelapsp.TestingParallelAPSPModuleFactory;
+import org.opendaylight.controller.config.manager.testingservices.scheduledthreadpool.TestingScheduledThreadPoolImpl;
+import org.opendaylight.controller.config.manager.testingservices.scheduledthreadpool
+        .TestingScheduledThreadPoolModuleFactory;
+import org.opendaylight.controller.config.manager.testingservices.threadpool.TestingFixedThreadPoolModuleFactory;
+
+public abstract class AbstractScheduledTest extends AbstractConfigTest {
+    protected static final String scheduled1 = "scheduled1";
+
+    @Before
+    public final void setUp() {
+        assertEquals(0,
+                TestingScheduledThreadPoolImpl.getNumberOfCloseMethodCalls());
+        super.initConfigTransactionManagerImpl(new HardcodedModuleFactoriesResolver(
+                new TestingScheduledThreadPoolModuleFactory(),
+                new TestingFixedThreadPoolModuleFactory(),
+                new TestingParallelAPSPModuleFactory()));
+    }
+
+    @After
+    public final void cleanUp() throws Exception {
+        destroyAllConfigBeans();
+        TestingScheduledThreadPoolImpl.cleanUp();
+        assertEquals(0,
+                TestingScheduledThreadPoolImpl.getNumberOfCloseMethodCalls());
+    }
+}
diff --git a/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/scheduledthreadpool/test/RuntimeBeanTest.java b/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/scheduledthreadpool/test/RuntimeBeanTest.java
new file mode 100644 (file)
index 0000000..d2a0507
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+ * 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.manager.testingservices.scheduledthreadpool.test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import java.util.Collections;
+import java.util.List;
+
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.InstanceNotFoundException;
+import javax.management.ObjectName;
+
+import org.junit.Test;
+import org.opendaylight.controller.config.api.ConflictingVersionException;
+import org.opendaylight.controller.config.api.ValidationException;
+import org.opendaylight.controller.config.api.jmx.CommitStatus;
+import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
+import org.opendaylight.controller.config.manager.testingservices.scheduledthreadpool
+        .TestingScheduledThreadPoolConfigBeanMXBean;
+import org.opendaylight.controller.config.manager.testingservices.scheduledthreadpool
+        .TestingScheduledThreadPoolModuleFactory;
+import org.opendaylight.controller.config.util.ConfigTransactionJMXClient;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+
+/**
+ * TestingScheduledThreadPool exports 2 interfaces: <br>
+ * {@link org.opendaylight.controller.config.manager.testingservices.scheduledthreadpool.TestingScheduledThreadPoolModuleFactory#NAME}
+ * ,<br>
+ * {@link org.opendaylight.controller.config.manager.testingservices.threadpool.TestingFixedThreadPoolModuleFactory#NAME}
+ * <br>
+ * <br>
+ * It also exports 2 runtime beans, one default and one with additional
+ * properties of {'a':'b'}.
+ */
+public class RuntimeBeanTest extends AbstractScheduledTest {
+
+    ObjectName ifc1runtimeON1 = ObjectNameUtil.createRuntimeBeanName(
+            TestingScheduledThreadPoolModuleFactory.NAME, scheduled1,
+            Maps.<String, String> newHashMap());
+    // additional runtime bean
+    ObjectName ifc1runtimeON2 = ObjectNameUtil.createRuntimeBeanName(
+            TestingScheduledThreadPoolModuleFactory.NAME, scheduled1,
+            ImmutableMap.of("a", "b"));
+
+    List<ObjectName> allObjectNames = Lists.newArrayList(ifc1runtimeON1,
+            ifc1runtimeON2);
+
+    private ObjectName createScheduled() throws InstanceAlreadyExistsException,
+            ConflictingVersionException, ValidationException {
+        ConfigTransactionJMXClient transaction = configRegistryClient
+                .createTransaction();
+
+        // create using TestingThreadPoolIfc:
+        ObjectName createdConfigBean = transaction.createModule(
+                TestingScheduledThreadPoolModuleFactory.NAME, scheduled1);
+        // commit
+        transaction.commit();
+        return createdConfigBean;
+    }
+
+    @Test
+    public void testCreateScheduled() throws Exception {
+        createScheduled();
+        checkRuntimeBeans();
+    }
+
+    private void checkRuntimeBeans() throws Exception {
+        // check runtime bean - on 2 places
+        for (ObjectName on : allObjectNames)
+            checkRuntimeBean(on);
+    }
+
+    private void checkRuntimeBean(ObjectName on) throws Exception {
+        assertEquals(0,
+                platformMBeanServer.getAttribute(on, "ActualNumberOfThreads"));
+    }
+
+    private void checkRuntimeBeanDoesNotExist(ObjectName on) throws Exception {
+        try {
+            checkRuntimeBean(on);
+            fail();
+        } catch (InstanceNotFoundException e) {
+
+        }
+    }
+
+    @Test
+    public void testLookup() throws Exception {
+        createScheduled();
+        assertEquals(Sets.newHashSet(ifc1runtimeON1, ifc1runtimeON2),
+                configRegistryClient.lookupRuntimeBeans());
+    }
+
+    @Test
+    public void testReuse() throws Exception {
+        ObjectName createdConfigBean = createScheduled();
+        // empty transaction
+        CommitStatus commitInfo = configRegistryClient.createTransaction()
+                .commit();
+
+        // check that it was reused
+        ObjectName readableConfigBean = ObjectNameUtil
+                .withoutTransactionName(createdConfigBean);
+        List<ObjectName> newInstances = Collections.<ObjectName> emptyList();
+        List<ObjectName> reusedInstances = Lists
+                .newArrayList(readableConfigBean);
+        List<ObjectName> recreatedInstaces = Collections
+                .<ObjectName> emptyList();
+        assertEquals(new CommitStatus(newInstances, reusedInstances,
+                recreatedInstaces), commitInfo);
+        checkRuntimeBeans();
+    }
+
+    @Test
+    public void testRecreate() throws Exception {
+        ObjectName createdConfigBean = createScheduled();
+        // empty transaction
+        ConfigTransactionJMXClient configTransaction = configRegistryClient
+                .createTransaction();
+        ObjectName scheduledWritableON = configTransaction.lookupConfigBean(
+                TestingScheduledThreadPoolModuleFactory.NAME, scheduled1);
+        TestingScheduledThreadPoolConfigBeanMXBean scheduledWritableProxy = configTransaction
+                .newMXBeanProxy(scheduledWritableON, TestingScheduledThreadPoolConfigBeanMXBean.class);
+        scheduledWritableProxy.setRecreate(true);
+        CommitStatus commitInfo = configTransaction.commit();
+        // check that it was recreated
+        ObjectName readableConfigBean = ObjectNameUtil
+                .withoutTransactionName(createdConfigBean);
+        List<ObjectName> newInstances = Collections.<ObjectName> emptyList();
+        List<ObjectName> reusedInstances = Collections.<ObjectName> emptyList();
+        List<ObjectName> recreatedInstaces = Lists
+                .newArrayList(readableConfigBean);
+        assertEquals(new CommitStatus(newInstances, reusedInstances,
+                recreatedInstaces), commitInfo);
+        checkRuntimeBeans();
+    }
+
+    @Test
+    public void testDestroy_shouldUnregisterRuntimeBeans() throws Exception {
+        ObjectName createdConfigBean = createScheduled();
+        ConfigTransactionJMXClient configTransaction = configRegistryClient
+                .createTransaction();
+        configTransaction.destroyModule(ObjectNameUtil
+                .createTransactionModuleON(configTransaction.getTransactionName(), createdConfigBean));
+        configTransaction.commit();
+        for (ObjectName on : allObjectNames)
+            checkRuntimeBeanDoesNotExist(on);
+    }
+
+}
diff --git a/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/scheduledthreadpool/test/TwoInterfacesExportTest.java b/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/scheduledthreadpool/test/TwoInterfacesExportTest.java
new file mode 100644 (file)
index 0000000..ca038f1
--- /dev/null
@@ -0,0 +1,205 @@
+/*
+ * 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.manager.testingservices.scheduledthreadpool.test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.junit.matchers.JUnitMatchers.containsString;
+
+import javax.annotation.Nullable;
+import javax.management.DynamicMBean;
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.InstanceNotFoundException;
+import javax.management.ObjectName;
+
+import org.junit.Test;
+import org.opendaylight.controller.config.api.ValidationException;
+import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
+import org.opendaylight.controller.config.manager.testingservices.parallelapsp.TestingParallelAPSPConfigMXBean;
+import org.opendaylight.controller.config.manager.testingservices.parallelapsp.TestingParallelAPSPModuleFactory;
+import org.opendaylight.controller.config.manager.testingservices.scheduledthreadpool.TestingScheduledThreadPoolImpl;
+import org.opendaylight.controller.config.manager.testingservices.scheduledthreadpool
+        .TestingScheduledThreadPoolModuleFactory;
+import org.opendaylight.controller.config.util.ConfigTransactionJMXClient;
+
+public class TwoInterfacesExportTest extends AbstractScheduledTest {
+
+    private void assertExists(String moduleName, String instanceName)
+            throws Exception {
+        assertExists(null, moduleName, instanceName);
+    }
+
+    private void assertExists(@Nullable ConfigTransactionJMXClient transaction,
+            String moduleName, String instanceName)
+            throws InstanceNotFoundException {
+        if (transaction != null) {
+            transaction.lookupConfigBean(moduleName, instanceName);
+            // make a dummy call
+            configRegistryClient.newMBeanProxy(
+                    ObjectNameUtil.createTransactionModuleON(
+                            transaction.getTransactionName(), moduleName,
+                            instanceName), DynamicMBean.class).getMBeanInfo();
+        } else {
+            configRegistryClient.lookupConfigBean(moduleName, instanceName);
+            // make a dummy call
+            configRegistryClient.newMBeanProxy(
+                    ObjectNameUtil.createReadOnlyModuleON(moduleName,
+                            instanceName), DynamicMBean.class).getMBeanInfo();
+        }
+    }
+
+    private void assertNotExists(String moduleName, String instanceName) {
+        assertNotExists(null, moduleName, instanceName);
+    }
+
+    private void assertNotExists(
+            @Nullable ConfigTransactionJMXClient transaction,
+            String moduleName, String instanceName) {
+
+        if (transaction != null) {
+            try {
+                transaction.lookupConfigBean(moduleName, instanceName);
+                fail();
+            } catch (InstanceNotFoundException e) {
+
+            }
+        } else {
+            try {
+                configRegistryClient.lookupConfigBean(moduleName, instanceName);
+                fail();
+            } catch (InstanceNotFoundException e) {
+
+            }
+        }
+    }
+
+    @Test
+    public void twoInterfaceNamesAfterCreatingConfigBean() throws Exception {
+        ConfigTransactionJMXClient transaction = configRegistryClient
+                .createTransaction();
+
+        // create using TestingThreadPoolIfc:
+        ObjectName scheduled1name = transaction.createModule(
+                TestingScheduledThreadPoolModuleFactory.NAME, scheduled1);
+
+        ObjectName retrievedName = transaction.lookupConfigBean(
+                TestingScheduledThreadPoolModuleFactory.NAME, scheduled1);
+        assertEquals(scheduled1name, retrievedName);
+
+        // getExistingConfigBean should resolve moduleName
+        String moduleName = TestingScheduledThreadPoolModuleFactory.NAME;
+        retrievedName = transaction.lookupConfigBean(moduleName, scheduled1);
+        ObjectName expected = ObjectNameUtil.createTransactionModuleON(
+                transaction.getTransactionName(), moduleName, scheduled1);
+        assertEquals(expected, retrievedName);
+
+        // commit
+        transaction.commit();
+        assertEquals(1, TestingScheduledThreadPoolImpl.allExecutors.size());
+        assertFalse(TestingScheduledThreadPoolImpl.allExecutors.get(0)
+                .isTerminated());
+        assertEquals(0,
+                TestingScheduledThreadPoolImpl.getNumberOfCloseMethodCalls());
+
+        assertExists(moduleName, scheduled1);
+
+        // destroy using ThreadPool ifc
+        transaction = configRegistryClient.createTransaction();
+        transaction.destroyModule(ObjectNameUtil.createTransactionModuleON(
+                transaction.getTransactionName(), moduleName, scheduled1));
+        transaction.commit();
+        assertEquals(1, TestingScheduledThreadPoolImpl.allExecutors.size());
+        assertTrue(TestingScheduledThreadPoolImpl.allExecutors.get(0)
+                .isTerminated());
+        assertEquals(1,
+                TestingScheduledThreadPoolImpl.getNumberOfCloseMethodCalls());
+
+        // should not be in platform:
+
+        assertNotExists(moduleName, scheduled1);
+
+        transaction = configRegistryClient.createTransaction();
+        // should not be in transaction
+        assertNotExists(transaction, moduleName, scheduled1);
+    }
+
+    @Test
+    public void tryToRegisterThreadPoolWithSameName()
+            throws InstanceAlreadyExistsException {
+        ConfigTransactionJMXClient transaction = configRegistryClient
+                .createTransaction();
+
+        transaction.createModule(TestingScheduledThreadPoolModuleFactory.NAME,
+                scheduled1);
+        try {
+            transaction.createModule(
+                    TestingScheduledThreadPoolModuleFactory.NAME, scheduled1);
+            fail();
+        } catch (InstanceAlreadyExistsException e) {
+            assertThat(
+                    e.getMessage(),
+                    containsString("There is an instance registered with name ModuleIdentifier{factoryName='scheduled', instanceName='scheduled1'}"));
+        }
+    }
+
+    // --
+    @Test
+    public void testRegisteringAllIfcNames() throws Exception {
+        ConfigTransactionJMXClient transaction = configRegistryClient
+                .createTransaction();
+        transaction.createModule(TestingScheduledThreadPoolModuleFactory.NAME,
+                scheduled1);
+        transaction.commit();
+        assertExists(TestingScheduledThreadPoolModuleFactory.NAME, scheduled1);
+        // another transaction
+        transaction = configRegistryClient.createTransaction();
+        assertExists(transaction, TestingScheduledThreadPoolModuleFactory.NAME,
+                scheduled1);
+    }
+
+    @Test
+    public void testWithAPSP_useScheduledNames()
+            throws InstanceAlreadyExistsException, ValidationException {
+        ConfigTransactionJMXClient transaction = configRegistryClient
+                .createTransaction();
+        ObjectName scheduledName = transaction.createModule(
+                TestingScheduledThreadPoolModuleFactory.NAME, scheduled1);
+
+        ObjectName apspName = transaction.createModule(
+                TestingParallelAPSPModuleFactory.NAME, "apsp1");
+        TestingParallelAPSPConfigMXBean apspProxy = transaction.newMBeanProxy(
+                apspName, TestingParallelAPSPConfigMXBean.class);
+        apspProxy.setThreadPool(scheduledName);
+        apspProxy.setSomeParam("someParam");
+        transaction.validateConfig();
+
+    }
+
+    @Test
+    public void testWithAPSP_useIfcNameMismatch() throws Exception {
+        ConfigTransactionJMXClient transaction = configRegistryClient
+                .createTransaction();
+        transaction.createModule(TestingScheduledThreadPoolModuleFactory.NAME,
+                scheduled1);
+
+        ObjectName apspName = transaction.createModule(
+                TestingParallelAPSPModuleFactory.NAME, "apsp1");
+        TestingParallelAPSPConfigMXBean apspProxy = transaction.newMBeanProxy(
+                apspName, TestingParallelAPSPConfigMXBean.class);
+        apspProxy.setThreadPool(ObjectNameUtil.createReadOnlyModuleON(
+                TestingScheduledThreadPoolModuleFactory.NAME, scheduled1));
+        apspProxy.setSomeParam("someParam");
+        transaction.validateConfig();
+        transaction.commit();
+
+    }
+
+}
diff --git a/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/seviceinterface/ModifiableThreadPoolServiceInterface.java b/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/seviceinterface/ModifiableThreadPoolServiceInterface.java
new file mode 100644 (file)
index 0000000..ab1b6bd
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * 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.manager.testingservices.seviceinterface;
+
+import org.opendaylight.controller.config.api.annotations.ServiceInterfaceAnnotation;
+import org.opendaylight.controller.config.manager.testingservices.threadpool.TestingModifiableThreadPoolIfc;
+
+@ServiceInterfaceAnnotation(value = "modifiable-threadpool", osgiRegistrationType = TestingModifiableThreadPoolIfc.class)
+public interface ModifiableThreadPoolServiceInterface extends
+        TestingThreadPoolServiceInterface {
+}
diff --git a/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/seviceinterface/TestingScheduledThreadPoolServiceInterface.java b/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/seviceinterface/TestingScheduledThreadPoolServiceInterface.java
new file mode 100644 (file)
index 0000000..e4e388c
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * 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.manager.testingservices.seviceinterface;
+
+import org.opendaylight.controller.config.api.annotations.ServiceInterfaceAnnotation;
+import org.opendaylight.controller.config.manager.testingservices.scheduledthreadpool.TestingScheduledThreadPoolIfc;
+
+@ServiceInterfaceAnnotation(value = "threadpool-scheduled", osgiRegistrationType = TestingScheduledThreadPoolIfc.class)
+public interface TestingScheduledThreadPoolServiceInterface extends
+        TestingThreadPoolServiceInterface {
+}
diff --git a/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/seviceinterface/TestingThreadPoolServiceInterface.java b/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/seviceinterface/TestingThreadPoolServiceInterface.java
new file mode 100644 (file)
index 0000000..fd5ab17
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * 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.manager.testingservices.seviceinterface;
+
+import org.opendaylight.controller.config.api.annotations.AbstractServiceInterface;
+import org.opendaylight.controller.config.api.annotations.ServiceInterfaceAnnotation;
+import org.opendaylight.controller.config.manager.testingservices.threadpool.TestingThreadPoolIfc;
+
+@ServiceInterfaceAnnotation(value = "testing-threadpool", osgiRegistrationType = TestingThreadPoolIfc.class)
+public interface TestingThreadPoolServiceInterface extends
+        AbstractServiceInterface {
+}
diff --git a/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/threadpool/TestingFixedThreadPool.java b/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/threadpool/TestingFixedThreadPool.java
new file mode 100644 (file)
index 0000000..d5434d2
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * 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.manager.testingservices.threadpool;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadPoolExecutor;
+
+import com.google.common.collect.Lists;
+
+public class TestingFixedThreadPool implements TestingThreadPoolIfc, Closeable,
+        TestingModifiableThreadPoolIfc {
+    private final ThreadPoolExecutor executorService;
+    private final String uniqueName;
+
+    public static void cleanUp() {
+        for (ExecutorService executorService : allExecutors) {
+            executorService.shutdown();
+        }
+        allExecutors.clear();
+    }
+
+    // for verification purposes:
+    public static final List<ThreadPoolExecutor> allExecutors = Collections
+            .synchronizedList(Lists.<ThreadPoolExecutor>newLinkedList());
+
+    public TestingFixedThreadPool(int threadCount, String uniqueName) {
+        checkNotNull(uniqueName);
+        this.uniqueName = uniqueName;
+        executorService = (ThreadPoolExecutor) Executors
+                .newFixedThreadPool(threadCount);
+        allExecutors.add(executorService);
+    }
+
+    @Override
+    public Executor getExecutor() {
+        return executorService;
+    }
+
+    @Override
+    public void close() throws IOException {
+        executorService.shutdown();
+
+    }
+
+    @Override
+    public int getMaxNumberOfThreads() {
+        return executorService.getMaximumPoolSize();
+    }
+
+    public String getUniqueName() {
+        return uniqueName;
+    }
+
+    @Override
+    public void setMaximumNumberOfThreads(int activeCount) {
+        checkArgument(activeCount > 0);
+        executorService.setMaximumPoolSize(activeCount);
+    }
+
+}
diff --git a/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/threadpool/TestingFixedThreadPoolConfigMXBean.java b/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/threadpool/TestingFixedThreadPoolConfigMXBean.java
new file mode 100644 (file)
index 0000000..115eb83
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * 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.manager.testingservices.threadpool;
+
+public interface TestingFixedThreadPoolConfigMXBean extends
+        TestingThreadPoolConfigMXBean {
+
+    public void setThreadCount(int threadCount);
+
+    public boolean isTriggerNewInstanceCreation();
+
+    public void setTriggerNewInstanceCreation(boolean boolTest);
+
+}
diff --git a/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/threadpool/TestingFixedThreadPoolModule.java b/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/threadpool/TestingFixedThreadPoolModule.java
new file mode 100644 (file)
index 0000000..ff33b16
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * 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.manager.testingservices.threadpool;
+
+import static com.google.common.base.Preconditions.checkState;
+
+import java.io.Closeable;
+
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.NotThreadSafe;
+
+import org.opendaylight.controller.config.api.ModuleIdentifier;
+import org.opendaylight.controller.config.manager.testingservices.seviceinterface.ModifiableThreadPoolServiceInterface;
+import org.opendaylight.controller.config.spi.Module;
+
+@NotThreadSafe
+public class TestingFixedThreadPoolModule implements
+        TestingFixedThreadPoolConfigMXBean, Module,
+        TestingThreadPoolConfigMXBean, ModifiableThreadPoolServiceInterface {
+    private final AutoCloseable oldCloseable;
+    private final TestingFixedThreadPool oldInstance;
+    private final ModuleIdentifier name;
+    private TestingFixedThreadPool instance;
+    private int threadCount = 0;
+    private boolean triggerNewInstanceCreation;
+
+    TestingFixedThreadPoolModule(ModuleIdentifier name,
+            @Nullable AutoCloseable oldCloseable,
+            @Nullable TestingFixedThreadPool oldInstance) {
+        this.name = name;
+        this.oldCloseable = oldCloseable;
+        this.oldInstance = oldInstance;
+    }
+
+    @Override
+    public ModuleIdentifier getName() {
+        return name;
+    }
+
+    // attributes
+    @Override
+    public void setThreadCount(int threadCount) {
+        this.threadCount = threadCount;
+    }
+
+    @Override
+    public int getThreadCount() {
+        return threadCount;
+    }
+
+    @Override
+    public boolean isTriggerNewInstanceCreation() {
+        return triggerNewInstanceCreation;
+    }
+
+    @Override
+    public void setTriggerNewInstanceCreation(boolean triggerNewInstanceCreation) {
+        this.triggerNewInstanceCreation = triggerNewInstanceCreation;
+    }
+
+    // operations
+
+    private boolean isReusable() {
+        return oldInstance != null;
+    }
+
+    @Override
+    public void validate() {
+        checkState(threadCount > 0,
+                "Parameter 'threadCount' must be greater than 0");
+    }
+
+    @Override
+    public Closeable getInstance() {
+        if (instance == null) {
+            if (isReusable() && triggerNewInstanceCreation == false) { // simulate
+                                                                       // big
+                                                                       // change
+                                                                       // using
+                                                                       // triggerNewInstanceCreation
+                oldInstance.setMaximumNumberOfThreads(threadCount);
+                instance = oldInstance;
+            } else {
+                if (oldCloseable != null) {
+                    try {
+                        oldCloseable.close();
+                    } catch (Exception e) {
+                        throw new RuntimeException(e);
+                    }
+                }
+                instance = new TestingFixedThreadPool(threadCount,
+                        name.toString());
+            }
+        }
+        return instance;
+    }
+
+}
diff --git a/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/threadpool/TestingFixedThreadPoolModuleFactory.java b/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/threadpool/TestingFixedThreadPoolModuleFactory.java
new file mode 100644 (file)
index 0000000..ba60b39
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * 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.manager.testingservices.threadpool;
+
+import java.util.Arrays;
+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.manager.testingservices.seviceinterface.ModifiableThreadPoolServiceInterface;
+import org.opendaylight.controller.config.manager.testingservices.seviceinterface.TestingThreadPoolServiceInterface;
+import org.opendaylight.controller.config.spi.Module;
+import org.opendaylight.controller.config.spi.ModuleFactory;
+
+public class TestingFixedThreadPoolModuleFactory implements ModuleFactory {
+    public static final String NAME = "fixed";
+    private static List<Class<? extends TestingThreadPoolServiceInterface>> ifc = Arrays
+            .asList(ModifiableThreadPoolServiceInterface.class, TestingThreadPoolServiceInterface.class);
+
+    @Override
+    public String getImplementationName() {
+        return NAME;
+    }
+
+    @Override
+    public TestingFixedThreadPoolModule createModule(String instanceName,
+            DependencyResolver dependencyResolver) {
+        return new TestingFixedThreadPoolModule(new ModuleIdentifier(NAME,
+                instanceName), null, null);
+    }
+
+    @Override
+    public Module createModule(String instanceName,
+            DependencyResolver dependencyResolver, DynamicMBeanWithInstance old)
+            throws Exception {
+        int threadCount = (Integer) old.getAttribute("ThreadCount");
+        // is the instance compatible?
+        TestingFixedThreadPool oldInstance;
+        try {
+            // reconfigure existing instance
+            oldInstance = (TestingFixedThreadPool) old.getInstance();
+        } catch (ClassCastException e) {
+            // old instance will be closed, new needs to be created
+            oldInstance = null;
+        }
+        TestingFixedThreadPoolModule result = new TestingFixedThreadPoolModule(
+                new ModuleIdentifier(NAME, instanceName), old.getInstance(),
+                oldInstance);
+        result.setThreadCount(threadCount);
+        return result;
+    }
+
+    @Override
+    public boolean isModuleImplementingServiceInterface(
+            Class<? extends AbstractServiceInterface> serviceInterface) {
+        return ifc.contains(serviceInterface);
+    }
+}
diff --git a/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/threadpool/TestingModifiableThreadPoolIfc.java b/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/threadpool/TestingModifiableThreadPoolIfc.java
new file mode 100644 (file)
index 0000000..b51c6e3
--- /dev/null
@@ -0,0 +1,12 @@
+/*
+ * 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.manager.testingservices.threadpool;
+
+public interface TestingModifiableThreadPoolIfc extends TestingThreadPoolIfc {
+    void setMaximumNumberOfThreads(int activeCount);
+}
diff --git a/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/threadpool/TestingThreadPoolConfigMXBean.java b/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/threadpool/TestingThreadPoolConfigMXBean.java
new file mode 100644 (file)
index 0000000..792a666
--- /dev/null
@@ -0,0 +1,13 @@
+/*
+ * 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.manager.testingservices.threadpool;
+
+public interface TestingThreadPoolConfigMXBean {
+
+    public int getThreadCount();
+}
diff --git a/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/threadpool/TestingThreadPoolIfc.java b/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/threadpool/TestingThreadPoolIfc.java
new file mode 100644 (file)
index 0000000..2e99aa0
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * 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.manager.testingservices.threadpool;
+
+import java.util.concurrent.Executor;
+
+public interface TestingThreadPoolIfc {
+
+    Executor getExecutor();
+
+    int getMaxNumberOfThreads();
+
+}
diff --git a/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/threadpool/package-info.java b/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/threadpool/package-info.java
new file mode 100644 (file)
index 0000000..bd75a70
--- /dev/null
@@ -0,0 +1,13 @@
+/*
+ * 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
+ */
+/**
+ * Represents simple service that has getters and setters, but no dependencies.
+ * Creates fixed thread pool wrapped in {@link org.opendaylight.controller.config.manager.testingservices.threadpool.TestingThreadPoolIfc}.
+ * Supports changing the number of threads on 'live' executor.
+ */
+package org.opendaylight.controller.config.manager.testingservices.threadpool;
diff --git a/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/threadpool/test/SimpleConfigurationTest.java b/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/threadpool/test/SimpleConfigurationTest.java
new file mode 100644 (file)
index 0000000..6372cb3
--- /dev/null
@@ -0,0 +1,461 @@
+/*
+ * 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.manager.testingservices.threadpool.test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.junit.matchers.JUnitMatchers.containsString;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ThreadPoolExecutor;
+
+import javax.management.DynamicMBean;
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.InstanceNotFoundException;
+import javax.management.MBeanException;
+import javax.management.ObjectName;
+import javax.management.ReflectionException;
+import javax.management.RuntimeMBeanException;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.config.api.ConflictingVersionException;
+import org.opendaylight.controller.config.api.ValidationException;
+import org.opendaylight.controller.config.api.ValidationException.ExceptionMessageWithStackTrace;
+import org.opendaylight.controller.config.api.jmx.CommitStatus;
+import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
+import org.opendaylight.controller.config.manager.impl.AbstractConfigWithJolokiaTest;
+import org.opendaylight.controller.config.manager.impl.factoriesresolver.HardcodedModuleFactoriesResolver;
+import org.opendaylight.controller.config.manager.testingservices.threadpool.TestingFixedThreadPool;
+import org.opendaylight.controller.config.manager.testingservices.threadpool.TestingFixedThreadPoolConfigMXBean;
+import org.opendaylight.controller.config.manager.testingservices.threadpool.TestingFixedThreadPoolModuleFactory;
+import org.opendaylight.controller.config.manager.testingservices.threadpool.TestingThreadPoolIfc;
+import org.opendaylight.controller.config.util.ConfigTransactionClient;
+import org.opendaylight.controller.config.util.ConfigTransactionJMXClient;
+import org.opendaylight.controller.config.util.jolokia.ConfigTransactionJolokiaClient;
+
+/**
+ * Tests basic functionality of configuration registry:
+ * <ol>
+ * <li>Creation of instance</li>
+ * <li>Destruction of instance</li>
+ * <li>Reconfiguration of live object</li>
+ * <li>Reconfiguration that triggers new object creation</li>
+ * <li>Replacement of running instance with different one with same name</li>
+ * </ol>
+ * Only one bean is being configured - {@link TestingThreadPoolIfc} which has no
+ * dependencies.
+ */
+public class SimpleConfigurationTest extends AbstractConfigWithJolokiaTest {
+    private final int numberOfThreads = 5;
+    private final int numberOfThreads2 = 10;
+    private static final String fixed1 = "fixed1";
+    private static final List<ObjectName> emptyONs = Collections
+            .<ObjectName> emptyList();
+    private static final ObjectName platformFixed1ON = ObjectNameUtil
+            .createReadOnlyModuleON(TestingFixedThreadPoolModuleFactory.NAME, fixed1);
+    private static final List<ObjectName> fixed1List = Arrays
+            .asList(platformFixed1ON);
+
+    @Before
+    public void setUp() {
+        super.initConfigTransactionManagerImpl(new HardcodedModuleFactoriesResolver(
+                new TestingFixedThreadPoolModuleFactory()));
+    }
+
+    @After
+    public void tearDown() {
+        TestingFixedThreadPool.cleanUp();
+    }
+
+    private ObjectName firstCommit() throws Exception {
+        ConfigTransactionJMXClient transaction = configRegistryClient
+                .createTransaction();
+
+        ObjectName fixed1names = createFixedThreadPool(transaction);
+
+        // commit
+        assertEquals(1, configRegistryClient.getOpenConfigs().size());
+        CommitStatus commitStatus = transaction.commit();
+        assertEquals(0, configRegistryClient.getOpenConfigs().size());
+        CommitStatus expected = new CommitStatus(Arrays.asList(ObjectNameUtil
+                .withoutTransactionName(fixed1names)), emptyONs, emptyONs);
+        assertEquals(expected, commitStatus);
+
+        assertEquals(1, TestingFixedThreadPool.allExecutors.size());
+        assertFalse(TestingFixedThreadPool.allExecutors.get(0).isShutdown());
+        return fixed1names;
+    }
+
+    private ObjectName createFixedThreadPool(
+            ConfigTransactionJMXClient transaction)
+            throws InstanceAlreadyExistsException, InstanceNotFoundException {
+        transaction.assertVersion(0, 1);
+
+        ObjectName fixed1names = transaction.createModule(
+                TestingFixedThreadPoolModuleFactory.NAME, fixed1);
+        TestingFixedThreadPoolConfigMXBean fixedConfigProxy = transaction
+                .newMXBeanProxy(fixed1names, TestingFixedThreadPoolConfigMXBean.class);
+        fixedConfigProxy.setThreadCount(numberOfThreads);
+
+        ObjectName retrievedNames = transaction.lookupConfigBean(
+                TestingFixedThreadPoolModuleFactory.NAME, fixed1);
+        assertEquals(fixed1names, retrievedNames);
+        return fixed1names;
+    }
+
+    @Test
+    public void testCreateAndDestroyBeanInSameTransaction() throws Exception {
+        ConfigTransactionJMXClient transaction = configRegistryClient
+                .createTransaction();
+        ObjectName fixed1names = createFixedThreadPool(transaction);
+        transaction.destroyModule(fixed1names);
+        CommitStatus commitStatus = transaction.commit();
+        assertStatus(commitStatus, 0, 0, 0);
+
+    }
+
+    @Test
+    public void testValidationUsingJMXClient() throws Exception {
+        ConfigTransactionClient transaction = configRegistryClient
+                .createTransaction();
+        testValidation(transaction);
+    }
+
+    @Test
+    public void testValidationUsingJolokiaClient() throws Exception {
+        ConfigTransactionClient transaction = configRegistryJolokiaClient
+                .createTransaction();
+        testValidation(transaction);
+    }
+
+    private void testValidation(ConfigTransactionClient transaction)
+            throws InstanceAlreadyExistsException, ReflectionException,
+            InstanceNotFoundException, MBeanException {
+        ObjectName fixed1names = transaction.createModule(
+                TestingFixedThreadPoolModuleFactory.NAME, fixed1);
+        // call validate on config bean
+        try {
+            platformMBeanServer.invoke(fixed1names, "validate", new Object[0],
+                    new String[0]);
+            fail();
+        } catch (RuntimeMBeanException e) {
+            RuntimeException targetException = e.getTargetException();
+            assertNotNull(targetException);
+            assertEquals(ValidationException.class, targetException.getClass());
+        }
+
+        // validate config bean
+        try {
+            transaction.validateBean(fixed1names);
+            fail();
+        } catch (ValidationException e) {
+            for (Map.Entry<String, Map<String, ExceptionMessageWithStackTrace>> exception : e
+                    .getFailedValidations().entrySet()) {
+                for (Map.Entry<String, ExceptionMessageWithStackTrace> entry : exception
+                        .getValue().entrySet()) {
+                    assertEquals(
+                            "Parameter 'threadCount' must be greater than 0",
+                            entry.getValue().getMessage());
+                }
+            }
+        }
+        // validate transaction
+        try {
+            transaction.validateConfig();
+            fail();
+        } catch (ValidationException e) {
+            for (Map.Entry<String, Map<String, ExceptionMessageWithStackTrace>> exception : e
+                    .getFailedValidations().entrySet()) {
+                for (Map.Entry<String, ExceptionMessageWithStackTrace> entry : exception
+                        .getValue().entrySet()) {
+                    assertEquals(
+                            "Parameter 'threadCount' must be greater than 0",
+                            entry.getValue().getMessage());
+                }
+            }
+        }
+        try {
+            transaction.commit();
+        } catch (ValidationException e) {
+            for (Map.Entry<String, Map<String, ExceptionMessageWithStackTrace>> exception : e
+                    .getFailedValidations().entrySet()) {
+                for (Map.Entry<String, ExceptionMessageWithStackTrace> entry : exception
+                        .getValue().entrySet()) {
+                    assertEquals(
+                            "Parameter 'threadCount' must be greater than 0",
+                            entry.getValue().getMessage());
+                }
+            }
+        }
+    }
+
+    @Test
+    public void test_createThreadPool_changeNumberOfThreads() throws Exception {
+        firstCommit();
+        ConfigTransactionJMXClient transaction = configRegistryClient
+                .createTransaction();
+        TestingFixedThreadPoolConfigMXBean fixedConfigProxy = startReconfiguringFixed1ThreadPool(transaction);
+        assertEquals(numberOfThreads, fixedConfigProxy.getThreadCount());
+        fixedConfigProxy.setThreadCount(numberOfThreads2);
+        CommitStatus commitStatus = transaction.commit();
+        checkThreadPools(1, numberOfThreads2);
+        CommitStatus expected = new CommitStatus(emptyONs, fixed1List, emptyONs);
+        assertEquals(expected, commitStatus);
+    }
+
+    @Test
+    public void test_createFixedThreadPool_destroyIt() throws Exception {
+        // 1, start transaction, create new fixed thread pool
+        ObjectName fixed1name = firstCommit();
+
+        // 2, check that configuration was copied to platform
+        DynamicMBean dynamicMBean = configRegistryClient.newMBeanProxy(
+                ObjectNameUtil.withoutTransactionName(fixed1name),
+                DynamicMBean.class);
+        dynamicMBean.getMBeanInfo();
+        assertEquals(numberOfThreads, dynamicMBean.getAttribute("ThreadCount"));
+
+        // 3, shutdown fixed1 in new transaction
+        assertFalse(TestingFixedThreadPool.allExecutors.get(0).isShutdown());
+        ConfigTransactionJMXClient transaction = configRegistryClient
+                .createTransaction();
+
+        // check versions
+        transaction.assertVersion(1, 2);
+
+        // test that it was copied to new transaction
+        ObjectName retrievedName = transaction.lookupConfigBean(
+                TestingFixedThreadPoolModuleFactory.NAME, fixed1);
+        assertNotNull(retrievedName);
+
+        // check that number of threads was copied from dynamic
+
+        TestingFixedThreadPoolConfigMXBean fixedConfigProxy = transaction
+                .newMXBeanProxy(retrievedName, TestingFixedThreadPoolConfigMXBean.class);
+        assertEquals(numberOfThreads, fixedConfigProxy.getThreadCount());
+
+        // destroy
+        transaction.destroyModule(ObjectNameUtil.createTransactionModuleON(
+                transaction.getTransactionName(),
+                TestingFixedThreadPoolModuleFactory.NAME, fixed1));
+        transaction.commit();
+
+        // 4, check
+        assertEquals(2, configRegistryClient.getVersion());
+        assertEquals(1, TestingFixedThreadPool.allExecutors.size());
+        assertTrue(TestingFixedThreadPool.allExecutors.get(0).isShutdown());
+
+        // dynamic config should be removed from platform
+        try {
+            dynamicMBean.getMBeanInfo();
+            fail();
+        } catch (Exception e) {
+            assertTrue(e.getCause() instanceof InstanceNotFoundException);
+        }
+    }
+
+    @Test
+    public void testReplaceFixed1() throws Exception {
+        // 1, start transaction, create new fixed thread pool
+        firstCommit();
+        // destroy and recreate with different # of threads
+        ConfigTransactionJMXClient transaction = configRegistryClient
+                .createTransaction();
+
+        transaction.destroyModule(ObjectNameUtil.createTransactionModuleON(
+                transaction.getTransactionName(),
+                TestingFixedThreadPoolModuleFactory.NAME, fixed1));
+
+        ObjectName fixed1name = transaction.createModule(
+                TestingFixedThreadPoolModuleFactory.NAME, fixed1);
+        TestingFixedThreadPoolConfigMXBean fixedConfigProxy = transaction
+                .newMXBeanProxy(fixed1name, TestingFixedThreadPoolConfigMXBean.class);
+        fixedConfigProxy.setThreadCount(numberOfThreads2);
+        // commit
+        transaction.commit();
+        // check that first threadpool is closed
+        checkThreadPools(2, numberOfThreads2);
+    }
+
+    private void checkThreadPools(int expectedTotalNumberOfExecutors,
+            int expectedNumberOfThreadsInLastExecutor) {
+        assertEquals(expectedTotalNumberOfExecutors,
+                TestingFixedThreadPool.allExecutors.size());
+        for (int i = 0; i < expectedTotalNumberOfExecutors - 1; i++) {
+            assertTrue(TestingFixedThreadPool.allExecutors.get(i).isShutdown());
+        }
+        ThreadPoolExecutor lastExecutor = TestingFixedThreadPool.allExecutors
+                .get(expectedTotalNumberOfExecutors - 1);
+        assertFalse(lastExecutor.isShutdown());
+        assertEquals(expectedNumberOfThreadsInLastExecutor,
+                lastExecutor.getMaximumPoolSize());
+    }
+
+    @Test
+    public void testTriggerRecreatingInstance() throws Exception {
+        // 1, start transaction, create new fixed thread pool
+        firstCommit();
+        // switch boolean to create new instance
+        ConfigTransactionJMXClient transaction = configRegistryClient
+                .createTransaction();
+        TestingFixedThreadPoolConfigMXBean fixedConfigProxy = startReconfiguringFixed1ThreadPool(transaction);
+
+        fixedConfigProxy.setTriggerNewInstanceCreation(true);
+        // commit
+        CommitStatus commitStatus = transaction.commit();
+        // check that new threadpool is created and old one is closed
+        checkThreadPools(2, numberOfThreads);
+        CommitStatus expected = new CommitStatus(emptyONs, emptyONs, fixed1List);
+        assertEquals(expected, commitStatus);
+    }
+
+    // return MBeanProxy for 'fixed1' and current transaction
+    private TestingFixedThreadPoolConfigMXBean startReconfiguringFixed1ThreadPool(
+            ConfigTransactionJMXClient transaction)
+            throws InstanceNotFoundException {
+        ObjectName fixed1name = transaction.lookupConfigBean(
+                TestingFixedThreadPoolModuleFactory.NAME, fixed1);
+
+        TestingFixedThreadPoolConfigMXBean fixedConfigProxy = transaction
+                .newMXBeanProxy(fixed1name, TestingFixedThreadPoolConfigMXBean.class);
+        return fixedConfigProxy;
+    }
+
+    @Test
+    public void testAbort() {
+        ConfigTransactionJMXClient transaction = configRegistryClient
+                .createTransaction();
+        assertEquals(1, configRegistryClient.getOpenConfigs().size());
+
+        transaction.abortConfig();
+        try {
+            transaction.createModule(TestingFixedThreadPoolModuleFactory.NAME,
+                    fixed1);
+            fail();
+        } catch (Exception e) {
+            assertTrue(e.getCause() instanceof InstanceNotFoundException);
+        }
+        try {
+            transaction.validateConfig();
+            fail();
+        } catch (Exception e) {
+            assertTrue(e.getCause() instanceof InstanceNotFoundException);
+        }
+        assertEquals(0, configRegistryClient.getOpenConfigs().size());
+    }
+
+    @Test
+    public void testOptimisticLock_ConfigTransactionClient() throws Exception {
+        ConfigTransactionJMXClient transaction1 = configRegistryClient
+                .createTransaction();
+        ConfigTransactionJMXClient transaction2 = configRegistryClient
+                .createTransaction();
+        transaction2.assertVersion(0, 2);
+        transaction2.commit();
+        try {
+            transaction1.commit();
+            fail();
+        } catch (ConflictingVersionException e) {
+            assertEquals(
+                    "Optimistic lock failed. Expected parent version 2, was 0",
+                    e.getMessage());
+        }
+    }
+
+    @Test
+    public void testOptimisticLock_ConfigRegistry() throws Exception {
+        ConfigTransactionJMXClient transaction1 = configRegistryClient
+                .createTransaction();
+        ConfigTransactionJMXClient transaction2 = configRegistryClient
+                .createTransaction();
+        transaction2.assertVersion(0, 2);
+        transaction2.commit();
+        try {
+            configRegistryClient.commitConfig(transaction1.getObjectName());
+            fail();
+        } catch (ConflictingVersionException e) {
+            assertEquals(
+                    "Optimistic lock failed. Expected parent version 2, was 0",
+                    e.getMessage());
+        }
+    }
+
+    @Test
+    public void testOptimisticLock_ConfigTransactionJolokiaClient()
+            throws Exception {
+        ConfigTransactionJolokiaClient transaction1 = configRegistryJolokiaClient
+                .createTransaction();
+        ConfigTransactionJolokiaClient transaction2 = configRegistryJolokiaClient
+                .createTransaction();
+        transaction2.assertVersion(0, 2);
+        transaction2.commit();
+        try {
+            transaction1.commit();
+            fail();
+        } catch (ConflictingVersionException e) {
+            assertEquals(
+                    "Optimistic lock failed. Expected parent version 2, was 0",
+                    e.getMessage());
+        }
+    }
+
+    @Test
+    public void testOptimisticLock_ConfigRegistryJolokiaClient()
+            throws Exception {
+        ConfigTransactionJolokiaClient transaction1 = configRegistryJolokiaClient
+                .createTransaction();
+        ConfigTransactionJolokiaClient transaction2 = configRegistryJolokiaClient
+                .createTransaction();
+        transaction2.assertVersion(0, 2);
+        transaction2.commit();
+        try {
+            configRegistryJolokiaClient.commitConfig(transaction1
+                    .getObjectName());
+            fail();
+        } catch (ConflictingVersionException e) {
+            assertEquals(
+                    "Optimistic lock failed. Expected parent version 2, was 0",
+                    e.getMessage());
+        }
+    }
+
+    @Test
+    public void testUsingJolokia() throws Exception {
+        ConfigTransactionJolokiaClient transactionClient = configRegistryJolokiaClient
+                .createTransaction();
+
+        ObjectName name = transactionClient.createModule(
+                TestingFixedThreadPoolModuleFactory.NAME, fixed1);
+
+        try {
+            transactionClient.validateConfig();
+            fail();
+        } catch (ValidationException e) {
+            assertThat(
+                    e.getMessage(),
+                    containsString("Parameter 'threadCount' must be greater than 0"));
+        }
+
+        transactionClient.setAttribute(name, "ThreadCount", numberOfThreads);
+        // commit
+        CommitStatus commitStatus = transactionClient.commit();
+        CommitStatus expected = new CommitStatus(Arrays.asList(ObjectNameUtil
+                .withoutTransactionName(name)), emptyONs, emptyONs);
+        assertEquals(expected, commitStatus);
+    }
+
+}
diff --git a/opendaylight/config/config-util/.gitignore b/opendaylight/config/config-util/.gitignore
new file mode 100644 (file)
index 0000000..fc1d35e
--- /dev/null
@@ -0,0 +1,3 @@
+target
+.classpath
+.settings
diff --git a/opendaylight/config/config-util/pom.xml b/opendaylight/config/config-util/pom.xml
new file mode 100644 (file)
index 0000000..4fd6ccd
--- /dev/null
@@ -0,0 +1,59 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <artifactId>config-subsystem</artifactId>
+        <groupId>org.opendaylight</groupId>
+        <version>0.2.1-SNAPSHOT</version>
+        <relativePath>..</relativePath>
+    </parent>
+    <artifactId>config-util</artifactId>
+    <name>${project.artifactId}</name>
+    <packaging>jar</packaging>
+
+    <dependencies>
+        <!-- compile dependencies -->
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>config-api</artifactId>
+            <version>0.2.1-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>org.jolokia</groupId>
+            <artifactId>jolokia-client-java</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.jolokia</groupId>
+            <artifactId>jolokia-jvm</artifactId>
+            <classifier>agent</classifier>
+        </dependency>
+
+        <!-- test dependencies -->
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <forkCount>1</forkCount>
+                    <reuseForks>false</reuseForks>
+                    <perCoreThreadCount>false</perCoreThreadCount>
+                    <threadCount>1</threadCount>
+                </configuration>
+            </plugin>
+            <!-- test jar -->
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jar-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/opendaylight/config/config-util/src/main/java/org/opendaylight/controller/config/util/AttributeEntry.java b/opendaylight/config/config-util/src/main/java/org/opendaylight/controller/config/util/AttributeEntry.java
new file mode 100644 (file)
index 0000000..c3299ac
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * 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.util;
+
+public class AttributeEntry {
+    private final String key;
+    private final String description;
+    private final Object value;
+    private final String type;
+    private final boolean rw;
+
+    public AttributeEntry(String key, String description, Object value,
+            String type, boolean rw) {
+        this.key = key;
+        this.description = description;
+        this.value = value;
+        this.type = type;
+        this.rw = rw;
+    }
+
+    public String getKey() {
+        return key;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public Object getValue() {
+        return value;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public boolean isRw() {
+        return rw;
+    }
+
+}
diff --git a/opendaylight/config/config-util/src/main/java/org/opendaylight/controller/config/util/ConfigRegistryClient.java b/opendaylight/config/config-util/src/main/java/org/opendaylight/controller/config/util/ConfigRegistryClient.java
new file mode 100644 (file)
index 0000000..e907502
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * 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.util;
+
+import javax.management.ObjectName;
+
+import org.opendaylight.controller.config.api.jmx.ConfigRegistryMXBean;
+
+public interface ConfigRegistryClient extends ConfigRegistryMXBean {
+
+    ConfigTransactionClient createTransaction();
+
+    ConfigTransactionClient getConfigTransactionClient(String transactionName);
+
+    ConfigTransactionClient getConfigTransactionClient(ObjectName objectName);
+
+    long getVersion();
+
+    Object invokeMethod(ObjectName on, String name, Object[] params,
+            String[] signature);
+
+    Object getAttributeCurrentValue(ObjectName on, String attributeName);
+
+}
diff --git a/opendaylight/config/config-util/src/main/java/org/opendaylight/controller/config/util/ConfigRegistryJMXClient.java b/opendaylight/config/config-util/src/main/java/org/opendaylight/controller/config/util/ConfigRegistryJMXClient.java
new file mode 100644 (file)
index 0000000..3a1efae
--- /dev/null
@@ -0,0 +1,170 @@
+/*
+ * 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.util;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+
+import javax.management.AttributeNotFoundException;
+import javax.management.InstanceNotFoundException;
+import javax.management.JMException;
+import javax.management.JMX;
+import javax.management.MBeanException;
+import javax.management.MBeanServer;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.ReflectionException;
+
+import org.opendaylight.controller.config.api.ConflictingVersionException;
+import org.opendaylight.controller.config.api.ValidationException;
+import org.opendaylight.controller.config.api.jmx.CommitStatus;
+import org.opendaylight.controller.config.api.jmx.ConfigRegistryMXBean;
+import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
+
+public class ConfigRegistryJMXClient implements ConfigRegistryClient {
+    private final ConfigRegistryMXBean configRegistryProxy;
+    private final ObjectName configRegistryON;
+    private final MBeanServer configMBeanServer;
+
+    public ConfigRegistryJMXClient(MBeanServer configMBeanServer) {
+        this.configMBeanServer = configMBeanServer;
+        configRegistryON = OBJECT_NAME;
+        Set<ObjectInstance> searchResult = configMBeanServer.queryMBeans(
+                configRegistryON, null);
+        if (!(searchResult.size() == 1)) {
+            throw new IllegalStateException("Config registry not found");
+        }
+        configRegistryProxy = JMX.newMXBeanProxy(configMBeanServer, configRegistryON, ConfigRegistryMXBean.class,
+                false);
+    }
+
+    @Override
+    public ConfigTransactionJMXClient createTransaction() {
+        ObjectName configTransactionControllerON = beginConfig();
+        return getConfigTransactionClient(configTransactionControllerON);
+    }
+
+    @Override
+    public ConfigTransactionJMXClient getConfigTransactionClient(
+            String transactionName) {
+        ObjectName objectName = ObjectNameUtil
+                .createTransactionControllerON(transactionName);
+        return getConfigTransactionClient(objectName);
+    }
+
+    @Override
+    public ConfigTransactionJMXClient getConfigTransactionClient(
+            ObjectName objectName) {
+        return new ConfigTransactionJMXClient(configRegistryProxy, objectName,
+                configMBeanServer);
+    }
+
+    public <T> T newMBeanProxy(ObjectName on, Class<T> clazz) {
+        return JMX.newMBeanProxy(configMBeanServer, on, clazz);
+    }
+
+    public <T> T newMXBeanProxy(ObjectName on, Class<T> clazz) {
+        return JMX.newMXBeanProxy(configMBeanServer, on, clazz);
+    }
+
+    @Override
+    public ObjectName beginConfig() {
+        return configRegistryProxy.beginConfig();
+    }
+
+    @Override
+    public CommitStatus commitConfig(ObjectName transactionControllerON)
+            throws ConflictingVersionException, ValidationException {
+        return configRegistryProxy.commitConfig(transactionControllerON);
+    }
+
+    @Override
+    public List<ObjectName> getOpenConfigs() {
+        return configRegistryProxy.getOpenConfigs();
+    }
+
+    @Override
+    public long getVersion() {
+        try {
+            return (Long) configMBeanServer.getAttribute(configRegistryON,
+                    "Version");
+        } catch (JMException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public Set<String> getAvailableModuleNames() {
+        return configRegistryProxy.getAvailableModuleNames();
+    }
+
+    @Override
+    public boolean isHealthy() {
+        return configRegistryProxy.isHealthy();
+    }
+
+    @Override
+    public Set<ObjectName> lookupConfigBeans() {
+        return configRegistryProxy.lookupConfigBeans();
+    }
+
+    @Override
+    public Set<ObjectName> lookupConfigBeans(String moduleName) {
+        return configRegistryProxy.lookupConfigBeans(moduleName);
+    }
+
+    @Override
+    public Set<ObjectName> lookupConfigBeans(String moduleName,
+            String instanceName) {
+        return configRegistryProxy.lookupConfigBeans(moduleName, instanceName);
+    }
+
+    @Override
+    public ObjectName lookupConfigBean(String moduleName, String instanceName)
+            throws InstanceNotFoundException {
+        return configRegistryProxy.lookupConfigBean(moduleName, instanceName);
+    }
+
+    @Override
+    public Set<ObjectName> lookupRuntimeBeans() {
+        return configRegistryProxy.lookupRuntimeBeans();
+    }
+
+    @Override
+    public Set<ObjectName> lookupRuntimeBeans(String ifcName,
+            String instanceName) {
+        return configRegistryProxy.lookupRuntimeBeans(ifcName, instanceName);
+    }
+
+    @Override
+    public Object invokeMethod(ObjectName on, String name, Object[] params,
+            String[] signature) {
+        try {
+            return configMBeanServer.invoke(on, name, params, signature);
+        } catch (InstanceNotFoundException | ReflectionException
+                | MBeanException e) {
+            throw new RuntimeException("Unable to invoke operation " + name
+                    + " on " + on + " with attributes "
+                    + Arrays.toString(params) + " and signature "
+                    + Arrays.toString(signature), e);
+        }
+    }
+
+    @Override
+    public Object getAttributeCurrentValue(ObjectName on, String attributeName) {
+        try {
+            return configMBeanServer.getAttribute(on, attributeName);
+        } catch (AttributeNotFoundException | InstanceNotFoundException
+                | MBeanException | ReflectionException e) {
+            throw new RuntimeException("Unable to get attribute "
+                    + attributeName + " for " + on, e);
+        }
+    }
+
+}
diff --git a/opendaylight/config/config-util/src/main/java/org/opendaylight/controller/config/util/ConfigTransactionClient.java b/opendaylight/config/config-util/src/main/java/org/opendaylight/controller/config/util/ConfigTransactionClient.java
new file mode 100644 (file)
index 0000000..75b1809
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * 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.util;
+
+import javax.management.Attribute;
+import javax.management.InstanceNotFoundException;
+import javax.management.ObjectName;
+
+import org.opendaylight.controller.config.api.ConflictingVersionException;
+import org.opendaylight.controller.config.api.ValidationException;
+import org.opendaylight.controller.config.api.jmx.CommitStatus;
+import org.opendaylight.controller.config.api.jmx.ConfigTransactionControllerMXBean;
+
+public interface ConfigTransactionClient extends
+        ConfigTransactionControllerMXBean {
+
+    CommitStatus commit() throws ConflictingVersionException,
+            ValidationException;
+
+    void assertVersion(int expectedParentVersion, int expectedCurrentVersion);
+
+    long getParentVersion();
+
+    long getVersion();
+
+    ObjectName getObjectName();
+
+    void validateBean(ObjectName configBeanON) throws ValidationException;
+
+    void destroyConfigBean(String moduleName, String instanceName)
+            throws InstanceNotFoundException;
+
+    void setAttribute(ObjectName on, String jmxName, Attribute attribute);
+}
diff --git a/opendaylight/config/config-util/src/main/java/org/opendaylight/controller/config/util/ConfigTransactionJMXClient.java b/opendaylight/config/config-util/src/main/java/org/opendaylight/controller/config/util/ConfigTransactionJMXClient.java
new file mode 100644 (file)
index 0000000..548c0e9
--- /dev/null
@@ -0,0 +1,185 @@
+/*
+ * 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.util;
+
+import java.util.Set;
+
+import javax.management.Attribute;
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.InstanceNotFoundException;
+import javax.management.JMException;
+import javax.management.JMX;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import javax.management.RuntimeMBeanException;
+
+import org.opendaylight.controller.config.api.ConflictingVersionException;
+import org.opendaylight.controller.config.api.ValidationException;
+import org.opendaylight.controller.config.api.jmx.CommitStatus;
+import org.opendaylight.controller.config.api.jmx.ConfigRegistryMXBean;
+import org.opendaylight.controller.config.api.jmx.ConfigTransactionControllerMXBean;
+import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
+
+public class ConfigTransactionJMXClient implements ConfigTransactionClient {
+    private final ConfigRegistryMXBean configTransactionManagerProxy;
+    private final ObjectName configTransactionControllerON;
+    private final ConfigTransactionControllerMXBean configControllerProxy;
+    private final MBeanServer configMBeanServer;
+
+    public ConfigTransactionJMXClient(
+            ConfigRegistryMXBean configTransactionManagerProxy,
+            ObjectName configTransactionControllerON,
+            MBeanServer configMBeanServer) {
+        this.configMBeanServer = configMBeanServer;
+        this.configTransactionManagerProxy = configTransactionManagerProxy;
+        this.configTransactionControllerON = configTransactionControllerON;
+        this.configControllerProxy = JMX.newMXBeanProxy(configMBeanServer,
+                configTransactionControllerON,
+                ConfigTransactionControllerMXBean.class);
+    }
+
+    public <T> T newMXBeanProxy(ObjectName on, Class<T> clazz) {
+        return JMX.newMXBeanProxy(configMBeanServer, on, clazz);
+    }
+
+    public <T> T newMBeanProxy(ObjectName on, Class<T> clazz) {
+        return JMX.newMBeanProxy(configMBeanServer, on, clazz);
+    }
+
+    @Override
+    public CommitStatus commit() throws ConflictingVersionException,
+            ValidationException {
+        return configTransactionManagerProxy
+                .commitConfig(configTransactionControllerON);
+    }
+
+    @Override
+    public void assertVersion(int expectedParentVersion,
+            int expectedCurrentVersion) {
+        if (expectedParentVersion != getParentVersion()) {
+            throw new IllegalStateException();
+        }
+        if (expectedCurrentVersion != getVersion()) {
+            throw new IllegalStateException();
+        }
+    }
+
+    // proxy around ConfigManagerMXBean
+    @Override
+    public ObjectName createModule(String moduleName, String instanceName)
+            throws InstanceAlreadyExistsException {
+        return configControllerProxy.createModule(moduleName, instanceName);
+    }
+
+    @Override
+    public void destroyModule(ObjectName objectName)
+            throws InstanceNotFoundException {
+        configControllerProxy.destroyModule(objectName);
+    }
+
+    @Override
+    public void destroyConfigBean(String moduleName, String instanceName)
+            throws InstanceNotFoundException {
+        destroyModule(ObjectNameUtil.createTransactionModuleON(
+                getTransactionName(), moduleName, instanceName));
+    }
+
+    @Override
+    public void abortConfig() {
+        configControllerProxy.abortConfig();
+    }
+
+    @Override
+    public void validateConfig() throws ValidationException {
+        configControllerProxy.validateConfig();
+    }
+
+    @Override
+    public long getParentVersion() {
+        try {
+            return (Long) configMBeanServer.getAttribute(
+                    configTransactionControllerON, "ParentVersion");
+        } catch (JMException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public long getVersion() {
+        try {
+            return (Long) configMBeanServer.getAttribute(
+                    configTransactionControllerON, "Version");
+        } catch (JMException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public String getTransactionName() {
+        return configControllerProxy.getTransactionName();
+    }
+
+    @Override
+    public Set<String> getAvailableModuleNames() {
+        return configControllerProxy.getAvailableModuleNames();
+    }
+
+    @Override
+    public ObjectName getObjectName() {
+        return configTransactionControllerON;
+    }
+
+    @Override
+    public Set<ObjectName> lookupConfigBeans() {
+        return configControllerProxy.lookupConfigBeans();
+    }
+
+    @Override
+    public Set<ObjectName> lookupConfigBeans(String moduleName) {
+        return configControllerProxy.lookupConfigBeans(moduleName);
+    }
+
+    @Override
+    public ObjectName lookupConfigBean(String moduleName, String instanceName)
+            throws InstanceNotFoundException {
+        return configControllerProxy.lookupConfigBean(moduleName, instanceName);
+    }
+
+    @Override
+    public Set<ObjectName> lookupConfigBeans(String moduleName,
+            String instanceName) {
+        return configControllerProxy
+                .lookupConfigBeans(moduleName, instanceName);
+    }
+
+    @Override
+    public void validateBean(ObjectName configBeanON)
+            throws ValidationException {
+        try {
+            configMBeanServer.invoke(configBeanON, "validate", null, null);
+        } catch (JMException e) {
+            throw new RuntimeException(e);
+        } catch (RuntimeMBeanException e) {
+            throw e.getTargetException();
+        }
+    }
+
+    @Override
+    public void setAttribute(ObjectName on, String attrName, Attribute attribute) {
+        if (ObjectNameUtil.getTransactionName(on) == null)
+            throw new IllegalArgumentException("Not in transaction instance "
+                    + on + ", no transaction name present");
+
+        try {
+            configMBeanServer.setAttribute(on, attribute);
+        } catch (JMException e) {
+            throw new IllegalStateException("Unable to set attribute "
+                    + attrName + " for " + on, e);
+        }
+    }
+}
diff --git a/opendaylight/config/config-util/src/main/java/org/opendaylight/controller/config/util/jolokia/ConfigRegistryJolokiaClient.java b/opendaylight/config/config-util/src/main/java/org/opendaylight/controller/config/util/jolokia/ConfigRegistryJolokiaClient.java
new file mode 100644 (file)
index 0000000..f29f0e0
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ * 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.util.jolokia;
+
+import java.util.List;
+import java.util.Set;
+
+import javax.management.ObjectName;
+
+import org.jolokia.client.request.J4pExecRequest;
+import org.jolokia.client.request.J4pReadRequest;
+import org.jolokia.client.request.J4pResponse;
+import org.json.simple.JSONArray;
+import org.json.simple.JSONObject;
+import org.opendaylight.controller.config.api.ConflictingVersionException;
+import org.opendaylight.controller.config.api.ValidationException;
+import org.opendaylight.controller.config.api.jmx.CommitStatus;
+import org.opendaylight.controller.config.api.jmx.ConfigRegistryMXBean;
+import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
+import org.opendaylight.controller.config.util.ConfigRegistryClient;
+
+public class ConfigRegistryJolokiaClient extends ListableJolokiaClient
+        implements ConfigRegistryClient {
+
+    public ConfigRegistryJolokiaClient(String url) {
+        super(url, ConfigRegistryMXBean.OBJECT_NAME);
+    }
+
+    @Override
+    public ConfigTransactionJolokiaClient createTransaction() {
+        // create transaction
+        J4pExecRequest execReq = new J4pExecRequest(objectName, "beginConfig");
+        J4pResponse<J4pExecRequest> resp = execute(execReq);
+        ObjectName transactionControllerON = extractObjectName(resp);
+        return getConfigTransactionClient(transactionControllerON);
+    }
+
+    @Override
+    public ConfigTransactionJolokiaClient getConfigTransactionClient(
+            String transactionName) {
+        ObjectName objectName = ObjectNameUtil
+                .createTransactionControllerON(transactionName);
+        return getConfigTransactionClient(objectName);
+    }
+
+    @Override
+    public ConfigTransactionJolokiaClient getConfigTransactionClient(
+            ObjectName objectName) {
+        return new ConfigTransactionJolokiaClient(url, objectName, this);
+    }
+
+    @Override
+    public CommitStatus commitConfig(ObjectName transactionControllerON)
+            throws ConflictingVersionException, ValidationException {
+        J4pExecRequest execReq = new J4pExecRequest(objectName, "commitConfig",
+                transactionControllerON);
+        JSONObject jsonObject;
+        jsonObject = execute(execReq).getValue();
+        JSONArray newInstancesArray = (JSONArray) jsonObject
+                .get("newInstances");
+        List<ObjectName> newInstances = jsonArrayToObjectNames(newInstancesArray);
+        JSONArray reusedInstancesArray = (JSONArray) jsonObject
+                .get("reusedInstances");
+        List<ObjectName> reusedInstances = jsonArrayToObjectNames(reusedInstancesArray);
+        JSONArray recreatedInstancesArray = (JSONArray) jsonObject
+                .get("recreatedInstances");
+        List<ObjectName> recreatedInstances = jsonArrayToObjectNames(recreatedInstancesArray);
+        return new CommitStatus(newInstances, reusedInstances,
+                recreatedInstances);
+    }
+
+    public Object getAttribute(ObjectName configBeanTransactionON, String key) {
+        J4pReadRequest req = new J4pReadRequest(configBeanTransactionON, key);
+        return execute(req).getValue();
+    }
+
+    public ObjectName getAttributeON(ObjectName configBeanTransactionON,
+            String key) {
+        JSONObject jsonAttrib = (JSONObject) getAttribute(
+                configBeanTransactionON, key);
+        return extractObjectName(jsonAttrib);
+    }
+
+    // proxy around ConfigTransactionManagerMXBean
+
+    @Override
+    public ObjectName beginConfig() {
+        ConfigTransactionJolokiaClient result = createTransaction();
+        return result.getTransactionON();
+    }
+
+    @Override
+    public List<ObjectName> getOpenConfigs() {
+        J4pReadRequest req = new J4pReadRequest(objectName, "OpenConfigs");
+        JSONArray jsonArray = execute(req).getValue();
+        return jsonArrayToObjectNames(jsonArray);
+    }
+
+    @Override
+    public long getVersion() {
+        J4pReadRequest req = new J4pReadRequest(objectName, "Version");
+        return (Long) execute(req).getValue();
+    }
+
+    @Override
+    public boolean isHealthy() {
+        J4pReadRequest req = new J4pReadRequest(objectName, "Healthy");
+        return (Boolean) execute(req).getValue();
+    }
+
+    @Override
+    public Set<ObjectName> lookupRuntimeBeans() {
+        return lookupSomething("lookupRuntimeBeans()", new Object[0]);
+    }
+
+    @Override
+    public Set<ObjectName> lookupRuntimeBeans(String moduleName,
+            String instanceName) {
+        return lookupSomething(
+                "lookupRuntimeBeans(java.lang.String,java.lang.String)",
+                new Object[] { moduleName, instanceName });
+    }
+
+    @Override
+    public Object invokeMethod(ObjectName on, String name, Object[] params,
+            String[] signature) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Object getAttributeCurrentValue(ObjectName on, String attributeName) {
+        throw new UnsupportedOperationException();
+    }
+}
diff --git a/opendaylight/config/config-util/src/main/java/org/opendaylight/controller/config/util/jolokia/ConfigTransactionJolokiaClient.java b/opendaylight/config/config-util/src/main/java/org/opendaylight/controller/config/util/jolokia/ConfigTransactionJolokiaClient.java
new file mode 100644 (file)
index 0000000..f4824cd
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ * 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.util.jolokia;
+
+import java.util.Map;
+
+import javax.management.Attribute;
+import javax.management.AttributeNotFoundException;
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.InstanceNotFoundException;
+import javax.management.ObjectName;
+
+import org.jolokia.client.request.J4pExecRequest;
+import org.jolokia.client.request.J4pReadRequest;
+import org.jolokia.client.request.J4pWriteRequest;
+import org.opendaylight.controller.config.api.ConflictingVersionException;
+import org.opendaylight.controller.config.api.ValidationException;
+import org.opendaylight.controller.config.api.jmx.CommitStatus;
+import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
+import org.opendaylight.controller.config.util.AttributeEntry;
+import org.opendaylight.controller.config.util.ConfigTransactionClient;
+
+public class ConfigTransactionJolokiaClient extends ListableJolokiaClient
+        implements ConfigTransactionClient {
+
+    private final ConfigRegistryJolokiaClient configRegistryJolokiaClient;
+
+    public ConfigTransactionJolokiaClient(String url,
+            ObjectName transactionControllerON,
+            ConfigRegistryJolokiaClient configRegistryJolokiaClient) {
+        super(url, transactionControllerON);
+        this.configRegistryJolokiaClient = configRegistryJolokiaClient;
+    }
+
+    public ObjectName getTransactionON() {
+        return objectName;
+    }
+
+    @Override
+    public CommitStatus commit() throws ConflictingVersionException,
+            ValidationException {
+        return configRegistryJolokiaClient.commitConfig(objectName);
+    }
+
+    @Override
+    public ObjectName createModule(String moduleName, String instanceName)
+            throws InstanceAlreadyExistsException {
+        J4pExecRequest execReq = new J4pExecRequest(objectName, "createModule",
+                moduleName, instanceName);
+        try {
+            return extractObjectName(execute(execReq));
+        } catch (RuntimeException e) {
+            if (e.getMessage() != null
+                    && e.getMessage().startsWith(
+                            InstanceAlreadyExistsException.class.getName()))
+                throw new InstanceAlreadyExistsException();
+            throw e;
+        }
+    }
+
+    @Override
+    public void destroyModule(ObjectName configBeanON) {
+        J4pExecRequest execReq = new J4pExecRequest(objectName,
+                "destroyModule(javax.management.ObjectName)", configBeanON);
+        execute(execReq);
+    }
+
+    @Override
+    public void destroyConfigBean(String moduleName, String instanceName)
+            throws InstanceNotFoundException {
+        destroyModule(ObjectNameUtil.createTransactionModuleON(
+                getTransactionName(), moduleName, instanceName));
+    }
+
+    @Override
+    public void abortConfig() {
+        J4pExecRequest execReq = new J4pExecRequest(objectName, "abortConfig");
+        execute(execReq);
+    }
+
+    @Override
+    public void validateConfig() throws ValidationException {
+        J4pExecRequest execReq = new J4pExecRequest(objectName,
+                "validateConfig");
+        execute(execReq);
+    }
+
+    @Override
+    public long getParentVersion() {
+        J4pReadRequest req = new J4pReadRequest(objectName, "ParentVersion");
+        return (Long) execute(req).getValue();
+    }
+
+    @Override
+    public long getVersion() {
+        J4pReadRequest req = new J4pReadRequest(objectName, "Version");
+        return (Long) execute(req).getValue();
+    }
+
+    public void setAttribute(ObjectName configBeanTransactionON, String key,
+            Object value) {
+        J4pWriteRequest req = new J4pWriteRequest(configBeanTransactionON, key,
+                value);
+        try {
+            execute(req);
+        } catch (RuntimeException e) {
+            if (e.getMessage() != null
+                    && e.getMessage().startsWith(
+                            AttributeNotFoundException.class.getName())) {
+                // try to fix wrong case
+                Map<String, AttributeEntry> allAttributes = getAttributes(configBeanTransactionON);
+                for (AttributeEntry attrib : allAttributes.values()) {
+                    if (attrib.getKey().equalsIgnoreCase(key)) {
+                        req = new J4pWriteRequest(configBeanTransactionON,
+                                attrib.getKey(), value);
+                        execute(req);
+                        return;
+                    }
+                }
+            }
+            throw e;
+        }
+    }
+
+    public Object getAttribute(ObjectName objectName, String key) {
+        return configRegistryJolokiaClient.getAttribute(objectName, key);
+    }
+
+    public ObjectName getAttributeON(ObjectName objectName, String key) {
+        return configRegistryJolokiaClient.getAttributeON(objectName, key);
+    }
+
+    @Override
+    public String getTransactionName() {
+        return ObjectNameUtil.getTransactionName(objectName);
+    }
+
+    @Override
+    public void validateBean(ObjectName rwON) throws ValidationException {
+        J4pExecRequest req = new J4pExecRequest(rwON, "validate", new Object[0]);
+        execute(req);
+    }
+
+    @Override
+    public void assertVersion(int expectedParentVersion,
+            int expectedCurrentVersion) {
+        if (expectedParentVersion != getParentVersion()) {
+            throw new IllegalStateException();
+        }
+        if (expectedCurrentVersion != getVersion()) {
+            throw new IllegalStateException();
+        }
+    }
+
+    @Override
+    public void setAttribute(ObjectName on, String jmxName, Attribute attribute) {
+        throw new UnsupportedOperationException();
+    }
+
+}
diff --git a/opendaylight/config/config-util/src/main/java/org/opendaylight/controller/config/util/jolokia/ListableJolokiaClient.java b/opendaylight/config/config-util/src/main/java/org/opendaylight/controller/config/util/jolokia/ListableJolokiaClient.java
new file mode 100644 (file)
index 0000000..e998beb
--- /dev/null
@@ -0,0 +1,268 @@
+/*
+ * 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.util.jolokia;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+import javax.management.InstanceNotFoundException;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+
+import org.jolokia.client.J4pClient;
+import org.jolokia.client.exception.J4pException;
+import org.jolokia.client.exception.J4pRemoteException;
+import org.jolokia.client.request.J4pExecRequest;
+import org.jolokia.client.request.J4pListRequest;
+import org.jolokia.client.request.J4pQueryParameter;
+import org.jolokia.client.request.J4pReadRequest;
+import org.jolokia.client.request.J4pRequest;
+import org.jolokia.client.request.J4pResponse;
+import org.json.simple.JSONArray;
+import org.json.simple.JSONObject;
+import org.opendaylight.controller.config.api.ConflictingVersionException;
+import org.opendaylight.controller.config.api.ValidationException;
+import org.opendaylight.controller.config.api.ValidationException.ExceptionMessageWithStackTrace;
+import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
+import org.opendaylight.controller.config.util.AttributeEntry;
+
+abstract class ListableJolokiaClient {
+    protected final J4pClient j4pClient;
+    protected final String url;
+    protected final ObjectName objectName;
+
+    public ListableJolokiaClient(String url, ObjectName objectName) {
+        if (url == null) {
+            throw new NullPointerException("Parameter 'url' is null");
+        }
+        if (!url.endsWith("/")) {
+            throw new IllegalArgumentException(
+                    "Parameter 'url' must end with '/'");
+        }
+
+        this.url = url;
+        this.j4pClient = new J4pClient(url);
+        this.objectName = objectName;
+    }
+
+    public ObjectName getObjectName() {
+        return objectName;
+    }
+
+    protected <R extends J4pResponse<T>, T extends J4pRequest> R execute(
+            T pRequest) {
+        try {
+            Map<J4pQueryParameter, String> pProcessingOptions = new HashMap<J4pQueryParameter, String>();
+            pProcessingOptions
+                    .put(J4pQueryParameter.INCLUDE_STACKTRACE, "true");
+            pProcessingOptions.put(J4pQueryParameter.SERIALIZE_EXCEPTION,
+                    "true");
+            return j4pClient.execute(pRequest, "POST", pProcessingOptions);
+        } catch (J4pRemoteException e) {
+            tryToConvertException(e.getRemoteStackTrace(), e.getErrorValue());
+            throw new RuntimeException(e.getRemoteStackTrace(), e);
+        } catch (J4pException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    protected void tryToConvertException(String remoteStackTrace,
+            JSONObject errorValue) {
+        String conflictPrefix = ConflictingVersionException.class.getName()
+                + ": ";
+        if (remoteStackTrace.startsWith(conflictPrefix)) {
+            remoteStackTrace = remoteStackTrace.substring(conflictPrefix
+                    .length());
+            Pattern p = Pattern.compile("\r?\n");
+            remoteStackTrace = Arrays.asList(p.split(remoteStackTrace))
+                    .iterator().next();
+            throw new ConflictingVersionException(remoteStackTrace);
+        }
+        String validationExceptionPrefix = ValidationException.class.getName();
+        if (remoteStackTrace.startsWith(validationExceptionPrefix)) {
+            throw createValidationExceptionFromJSONObject(errorValue);
+        }
+    }
+
+    static ValidationException createValidationExceptionFromJSONObject(
+            JSONObject errorValue) {
+        String fValsKey = "failedValidations";
+        JSONObject failedVals = (JSONObject) errorValue.get(fValsKey);
+
+        checkArgument(
+                !failedVals.isEmpty(),
+                fValsKey + " was not present in received JSON: "
+                        + errorValue.toJSONString());
+        Map<String, Map<String, ExceptionMessageWithStackTrace>> failedValsMap = new HashMap<String, Map<String, ExceptionMessageWithStackTrace>>();
+
+        for (Object key : failedVals.keySet()) {
+            checkArgument(key instanceof String, "Unexpected key " + key
+                    + ", expected instance of String");
+            Map<String, ExceptionMessageWithStackTrace> innerMap = new HashMap<String, ValidationException.ExceptionMessageWithStackTrace>();
+            for (Object innerKey : ((JSONObject) failedVals.get(key)).keySet()) {
+                checkArgument(innerKey instanceof String, "Unexpected key "
+                        + innerKey + ", expected instance of String");
+                JSONObject exWithStackTraceVal = (JSONObject) (((JSONObject) failedVals
+                        .get(key)).get(innerKey));
+                Object mess = exWithStackTraceVal.get("message");
+                Object stack = exWithStackTraceVal.get("trace");
+                checkArgument(mess != null && stack != null,
+                        "\"Message\" and \"trace\" elements expected in received json: "
+                                + errorValue.toJSONString());
+                innerMap.put(innerKey.toString(),
+                        new ExceptionMessageWithStackTrace((String) mess,
+                                (String) stack));
+            }
+            failedValsMap.put((String) key, innerMap);
+        }
+        return new ValidationException(failedValsMap);
+    }
+
+    private static void checkArgument(boolean b, String string) {
+        if (b == false)
+            throw new IllegalArgumentException(string);
+    }
+
+    public String getUrl() {
+        return url;
+    }
+
+    public Map<String, AttributeEntry> getAttributes(ObjectName on) {
+        J4pListRequest req = new J4pListRequest(on);
+        J4pResponse<J4pListRequest> response = execute(req);
+        JSONObject listJSONResponse = response.getValue();
+        JSONObject attributes = (JSONObject) listJSONResponse.get("attr");
+        Map<String, JSONObject> listMap = new HashMap<String, JSONObject>();
+        for (Object entryObject : attributes.entrySet()) {
+            Entry<String, Object> entry = (Entry<String, Object>) entryObject;
+            JSONObject entryVal = (JSONObject) entry.getValue();
+
+            // read value
+            listMap.put(entry.getKey(), entryVal);
+        }
+        J4pReadRequest j4pReadRequest = new J4pReadRequest(on, listMap.keySet()
+                .toArray(new String[0]));
+        J4pResponse<J4pReadRequest> readResponse = execute(j4pReadRequest);
+        Object readResponseValue = readResponse.getValue();
+        // readResponseValue can be String if there is just one attribute or
+        // JSONObject
+        Map<String, Object> attribsToValues = new HashMap<String, Object>();
+        if (readResponseValue instanceof JSONObject) {
+            JSONObject readJSONResponse = (JSONObject) readResponseValue;
+            for (Object entryObject : readJSONResponse.entrySet()) {
+                Entry<String, Object> entry = (Entry<String, Object>) entryObject;
+                String key = entry.getKey();
+                Object value = entry.getValue();
+                attribsToValues.put(key, value);
+            }
+        }
+
+        Map<String, AttributeEntry> resultMap = new HashMap<String, AttributeEntry>();
+        for (Entry<String, JSONObject> entry : listMap.entrySet()) {
+            String key = entry.getKey();
+            Object value = attribsToValues.size() > 0 ? attribsToValues
+                    .get(key) : readResponseValue;
+            JSONObject listJSON = entry.getValue();
+            String description = (String) listJSON.get("desc");
+            String type = (String) listJSON.get("type");
+            boolean rw = (Boolean) listJSON.get("rw");
+            AttributeEntry attributeEntry = new AttributeEntry(key,
+                    description, value, type, rw);
+            resultMap.put(key, attributeEntry);
+        }
+
+        return resultMap;
+    }
+
+    public String getConfigBeanDescripton(ObjectName on) {
+        J4pListRequest req = new J4pListRequest(on);
+        J4pResponse<J4pListRequest> response = execute(req);
+        JSONObject jsonDesc = response.getValue();
+        Object description = jsonDesc.get("desc");
+        return description == null ? null : description.toString();
+    }
+
+    protected List<ObjectName> jsonArrayToObjectNames(JSONArray jsonArray) {
+        List<ObjectName> result = new ArrayList<>(jsonArray.size());
+        for (Object entry : jsonArray) {
+            JSONObject jsonObject = (JSONObject) entry;
+            String objectNameString = (String) jsonObject.get("objectName");
+            try {
+                result.add(new ObjectName(objectNameString));
+            } catch (MalformedObjectNameException e) {
+                throw new IllegalStateException("Cannot convert "
+                        + objectNameString + " to ObjectName", e);
+            }
+        }
+        return result;
+    }
+
+    protected ObjectName extractObjectName(J4pResponse<J4pExecRequest> resp) {
+        JSONObject jsonResponse = resp.getValue();
+        return extractObjectName(jsonResponse);
+    }
+
+    protected ObjectName extractObjectName(JSONObject jsonResponse) {
+        String result = jsonResponse.get("objectName").toString();
+        return ObjectNameUtil.createON(result);
+    }
+
+    protected Set<ObjectName> lookupSomething(String signature,
+            Object[] parameters) {
+        J4pExecRequest req = new J4pExecRequest(objectName, signature,
+                parameters);
+        JSONArray jsonArray = execute(req).getValue();
+        return new HashSet<>(jsonArrayToObjectNames(jsonArray));
+    }
+
+    public Set<ObjectName> lookupConfigBeans() {
+        return lookupSomething("lookupConfigBeans()", new Object[0]);
+    }
+
+    public Set<ObjectName> lookupConfigBeans(String ifcName) {
+        return lookupSomething("lookupConfigBeans(java.lang.String)",
+                new Object[] { ifcName });
+    }
+
+    public Set<ObjectName> lookupConfigBeans(String ifcName, String instanceName) {
+        return lookupSomething(
+                "lookupConfigBeans(java.lang.String,java.lang.String)",
+                new Object[] { ifcName, instanceName });
+    }
+
+    public ObjectName lookupConfigBean(String ifcName, String instanceName)
+            throws InstanceNotFoundException {
+        J4pExecRequest req = new J4pExecRequest(objectName,
+                "lookupConfigBean(java.lang.String,java.lang.String)",
+                new Object[] { ifcName, instanceName });
+        try {
+            J4pResponse<J4pExecRequest> resp = execute(req);
+            return extractObjectName(resp);
+        } catch (RuntimeException e) {
+            if (e.getMessage() != null
+                    && e.getMessage().startsWith(
+                            InstanceNotFoundException.class.getName()))
+                throw new InstanceNotFoundException();
+            throw e;
+        }
+    }
+
+    public Set<String> getAvailableModuleNames() {
+        J4pReadRequest req = new J4pReadRequest(objectName,
+                "AvailableModuleNames");
+        List<String> value = execute(req).getValue();
+        return new HashSet<>(value);
+    }
+}
diff --git a/opendaylight/config/config-util/src/test/java/org/opendaylight/controller/config/util/ConfigRegistryClientsTest.java b/opendaylight/config/config-util/src/test/java/org/opendaylight/controller/config/util/ConfigRegistryClientsTest.java
new file mode 100644 (file)
index 0000000..85397d2
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * 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.util;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+import static org.junit.matchers.JUnitMatchers.containsString;
+
+import java.lang.management.ManagementFactory;
+import java.util.Set;
+
+import javax.management.InstanceNotFoundException;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.config.api.ConfigRegistry;
+import org.opendaylight.controller.config.api.ValidationException;
+import org.opendaylight.controller.config.util.jolokia.ConfigRegistryJolokiaClient;
+
+public class ConfigRegistryClientsTest {
+
+    private String jolokiaURL;
+
+    private TestingConfigRegistry testingRegistry;
+    private ObjectName testingRegistryON;
+    private final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
+    private ConfigRegistryClient jmxRegistryClient, jolokiaRegistryClient;
+
+    @Before
+    public void setUp() throws Exception {
+        jolokiaURL = JolokiaHelper.startTestingJolokia();
+        testingRegistry = new TestingConfigRegistry();
+        testingRegistryON = ConfigRegistry.OBJECT_NAME;
+        mbs.registerMBean(testingRegistry, testingRegistryON);
+        jmxRegistryClient = new ConfigRegistryJMXClient(
+                ManagementFactory.getPlatformMBeanServer());
+        jolokiaRegistryClient = new ConfigRegistryJolokiaClient(jolokiaURL);
+    }
+
+    @After
+    public void cleanUp() throws Exception {
+        JolokiaHelper.stopJolokia();
+        if (testingRegistryON != null) {
+            mbs.unregisterMBean(testingRegistryON);
+        }
+    }
+
+    @Test
+    public void testLookupRuntimeBeans() throws Exception {
+        Set<ObjectName> jmxLookup = lookupRuntimeBeans(jmxRegistryClient);
+        Set<ObjectName> jolokiaLookup = lookupRuntimeBeans(jolokiaRegistryClient);
+        assertEquals(jmxLookup, jolokiaLookup);
+    }
+
+    private Set<ObjectName> lookupRuntimeBeans(ConfigRegistryClient client)
+            throws Exception {
+        Set<ObjectName> beans = client.lookupRuntimeBeans();
+        for (ObjectName on : beans) {
+            assertEquals("RuntimeBean", on.getKeyProperty("type"));
+        }
+        assertEquals(3, beans.size());
+        return beans;
+    }
+
+    @Test
+    public void testLookupRuntimeBeansWithIfcNameAndInstanceName()
+            throws InstanceNotFoundException {
+        Set<ObjectName> jmxLookup = clientLookupRuntimeBeansWithModuleAndInstance(
+                jmxRegistryClient, TestingConfigRegistry.moduleName1,
+                TestingConfigRegistry.instName1);
+        assertEquals(1, jmxLookup.size());
+        Set<ObjectName> jolokiaLookup = clientLookupRuntimeBeansWithModuleAndInstance(
+                jolokiaRegistryClient, TestingConfigRegistry.moduleName1,
+                TestingConfigRegistry.instName1);
+        assertEquals(jmxLookup, jolokiaLookup);
+
+        jmxLookup = clientLookupRuntimeBeansWithModuleAndInstance(
+                jmxRegistryClient, TestingConfigRegistry.moduleName2,
+                TestingConfigRegistry.instName2);
+        assertEquals(1, jmxLookup.size());
+        jolokiaLookup = clientLookupRuntimeBeansWithModuleAndInstance(
+                jolokiaRegistryClient, TestingConfigRegistry.moduleName2,
+                TestingConfigRegistry.instName2);
+        assertEquals(jmxLookup, jolokiaLookup);
+
+        jmxLookup = clientLookupRuntimeBeansWithModuleAndInstance(
+                jmxRegistryClient, TestingConfigRegistry.moduleName1,
+                TestingConfigRegistry.instName2);
+        assertEquals(0, jmxLookup.size());
+        jolokiaLookup = clientLookupRuntimeBeansWithModuleAndInstance(
+                jolokiaRegistryClient, TestingConfigRegistry.moduleName1,
+                TestingConfigRegistry.instName2);
+        assertEquals(jmxLookup, jolokiaLookup);
+    }
+
+    private Set<ObjectName> clientLookupRuntimeBeansWithModuleAndInstance(
+            ConfigRegistryClient client, String moduleName, String instanceName) {
+        Set<ObjectName> beans = client.lookupRuntimeBeans(moduleName, instanceName);
+        if (beans.size() > 0) {
+            assertEquals("RuntimeBean",
+                    beans.iterator().next().getKeyProperty("type"));
+        }
+        return beans;
+    }
+
+    @Test
+    public void testValidationExceptionDeserialization() {
+        try {
+            jolokiaRegistryClient.commitConfig(null);
+            fail();
+        } catch (ValidationException e) {
+            String moduleName = "moduleName", instanceName = "instanceName";
+            assertThat(e.getFailedValidations().containsKey(moduleName),
+                    is(true));
+            assertThat(e.getFailedValidations().size(), is(1));
+            assertThat(e.getFailedValidations().get(moduleName).size(), is(1));
+            assertThat(
+                    e.getFailedValidations().get(moduleName)
+                            .containsKey(instanceName), is(true));
+            assertThat(
+                    e.getFailedValidations().get(moduleName).get(instanceName)
+                            .getMessage(), is("message"));
+            assertThat(
+                    e.getFailedValidations().get(moduleName).get(instanceName)
+                            .getTrace(),
+                    containsString("org.opendaylight.controller.config.util.TestingConfigRegistry.commitConfig"));
+        }
+    }
+
+}
diff --git a/opendaylight/config/config-util/src/test/java/org/opendaylight/controller/config/util/ConfigTransactionClientsTest.java b/opendaylight/config/config-util/src/test/java/org/opendaylight/controller/config/util/ConfigTransactionClientsTest.java
new file mode 100644 (file)
index 0000000..a6df1e9
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * 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.util;
+
+import static org.junit.Assert.assertEquals;
+
+import java.lang.management.ManagementFactory;
+import java.util.Set;
+
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.config.api.jmx.ConfigTransactionControllerMXBean;
+import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
+import org.opendaylight.controller.config.util.jolokia.ConfigTransactionJolokiaClient;
+
+public class ConfigTransactionClientsTest {
+
+    private final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
+    private String jolokiaURL;
+    private ConfigTransactionControllerMXBean transactionController;
+    private ObjectName transactionControllerON;
+    private ConfigTransactionClient jmxTransactionClient,
+            jolokiaTransactionClient;
+
+    @Before
+    public void setUp() throws Exception {
+        jolokiaURL = JolokiaHelper.startTestingJolokia();
+        transactionController = new TestingConfigTransactionController();
+        transactionControllerON = new ObjectName(ObjectNameUtil.ON_DOMAIN + ":"
+                + ObjectNameUtil.TYPE_KEY + "=TransactionController");
+        mbs.registerMBean(transactionController, transactionControllerON);
+        jmxTransactionClient = new ConfigTransactionJMXClient(null,
+                transactionControllerON,
+                ManagementFactory.getPlatformMBeanServer());
+        jolokiaTransactionClient = new ConfigTransactionJolokiaClient(
+                jolokiaURL, transactionControllerON, null);
+    }
+
+    @After
+    public void cleanUp() throws Exception {
+        JolokiaHelper.stopJolokia();
+        if (transactionControllerON != null) {
+            mbs.unregisterMBean(transactionControllerON);
+        }
+    }
+
+    @Test
+    public void testLookupConfigBeans() throws Exception {
+        Set<ObjectName> jmxLookup = testClientLookupConfigBeans(jmxTransactionClient);
+        Set<ObjectName> jolokiaLookup = testClientLookupConfigBeans(jolokiaTransactionClient);
+        assertEquals(jmxLookup, jolokiaLookup);
+    }
+
+    private Set<ObjectName> testClientLookupConfigBeans(
+            ConfigTransactionClient client) {
+        Set<ObjectName> beans = client.lookupConfigBeans();
+        for (ObjectName on : beans) {
+            assertEquals("Module", on.getKeyProperty("type"));
+        }
+        assertEquals(3, beans.size());
+        return beans;
+    }
+
+}
diff --git a/opendaylight/config/config-util/src/test/java/org/opendaylight/controller/config/util/JolokiaHelper.java b/opendaylight/config/config-util/src/test/java/org/opendaylight/controller/config/util/JolokiaHelper.java
new file mode 100644 (file)
index 0000000..322f3da
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * 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.util;
+
+import java.io.IOException;
+
+import org.jolokia.jvmagent.JolokiaServer;
+import org.jolokia.jvmagent.JvmAgentConfig;
+
+public class JolokiaHelper {
+    private static JolokiaServer jolokiaServer;
+
+    /**
+     * Bind to port 17777. By convention, ports above 10000 are used for testing
+     * and < 10000 for production
+     *
+     * @return url that can be passed to new J4pClient(url)
+     */
+    public static String startTestingJolokia() {
+        return startJolokia("localhost", 17777);
+    }
+
+    /**
+     * @return url that can be passed to new J4pClient(url)
+     * @throws IOException
+     */
+    public static String startJolokia(String host, int port) {
+        String agentArgs = "host=" + host + ",port=" + port;
+        JvmAgentConfig config = new JvmAgentConfig(agentArgs);
+        Exception lastException = null;
+        for (int i = 0; i < 10; i++) {
+            try {
+                jolokiaServer = new JolokiaServer(config, false);
+                jolokiaServer.start();
+                return "http://" + host + ":" + port + "/jolokia/";
+            } catch (Exception e) {
+                lastException = e;
+                try {
+                    Thread.sleep(100);
+                } catch (InterruptedException ie) {
+                    ie.printStackTrace();
+                }
+            }
+        }
+        throw new RuntimeException(lastException);
+    }
+
+    public static void stopJolokia() {
+        jolokiaServer.stop();
+    }
+}
diff --git a/opendaylight/config/config-util/src/test/java/org/opendaylight/controller/config/util/LookupTest.java b/opendaylight/config/config-util/src/test/java/org/opendaylight/controller/config/util/LookupTest.java
new file mode 100644 (file)
index 0000000..0749204
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * 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.util;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import java.lang.management.ManagementFactory;
+import java.lang.reflect.Method;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import javax.management.InstanceNotFoundException;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.config.api.ConfigRegistry;
+import org.opendaylight.controller.config.api.LookupRegistry;
+import org.opendaylight.controller.config.api.jmx.ConfigTransactionControllerMXBean;
+import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
+import org.opendaylight.controller.config.util.jolokia.ConfigRegistryJolokiaClient;
+import org.opendaylight.controller.config.util.jolokia.ConfigTransactionJolokiaClient;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Sets;
+
+public class LookupTest {
+
+    private String jolokiaURL;
+    private TestingConfigRegistry testingRegistry;
+    private ObjectName testingRegistryON;
+    private final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
+    private ConfigRegistryClient jmxRegistryClient, jolokiaRegistryClient;
+    private ConfigTransactionControllerMXBean testingTransactionController;
+    private ObjectName testingTransactionControllerON;
+    private ConfigTransactionClient jmxTransactionClient,
+            jolokiaTransactionClient;
+
+    Map<LookupRegistry, ? extends Set<? extends LookupRegistry>> lookupProvidersToClients;
+
+    @Before
+    public void setUp() throws Exception {
+        jolokiaURL = JolokiaHelper.startTestingJolokia();
+        testingRegistry = new TestingConfigRegistry();
+        testingRegistryON = ConfigRegistry.OBJECT_NAME;
+        mbs.registerMBean(testingRegistry, testingRegistryON);
+        jmxRegistryClient = new ConfigRegistryJMXClient(
+                ManagementFactory.getPlatformMBeanServer());
+        jolokiaRegistryClient = new ConfigRegistryJolokiaClient(jolokiaURL);
+
+        testingTransactionController = new TestingConfigTransactionController();
+        testingTransactionControllerON = new ObjectName(
+                ObjectNameUtil.ON_DOMAIN + ":" + ObjectNameUtil.TYPE_KEY
+                        + "=TransactionController");
+        mbs.registerMBean(testingTransactionController,
+                testingTransactionControllerON);
+
+        jmxTransactionClient = new ConfigTransactionJMXClient(null,
+                testingTransactionControllerON,
+                ManagementFactory.getPlatformMBeanServer());
+        jolokiaTransactionClient = new ConfigTransactionJolokiaClient(
+                jolokiaURL, testingTransactionControllerON, null);
+        lookupProvidersToClients = ImmutableMap
+                .of(testingRegistry, Sets.newHashSet(jmxRegistryClient, jolokiaRegistryClient),
+                        testingTransactionController, Sets.newHashSet(jmxTransactionClient, jolokiaTransactionClient));
+    }
+
+    @After
+    public void cleanUp() throws Exception {
+        JolokiaHelper.stopJolokia();
+        mbs.unregisterMBean(testingRegistryON);
+        mbs.unregisterMBean(testingTransactionControllerON);
+    }
+
+    @Test
+    public void testLookupConfigBeans() throws Exception {
+        Method method = LookupRegistry.class.getMethod("lookupConfigBeans");
+        Object[] args = new Object[0];
+        test(method, args);
+    }
+
+    @Test
+    public void testLookupConfigBeans1() throws Exception {
+        Method method = LookupRegistry.class.getMethod("lookupConfigBeans",
+                String.class);
+        Object[] args = new Object[] { TestingConfigRegistry.moduleName1 };
+        test(method, args);
+    }
+
+    @Test
+    public void testLookupConfigBeans2() throws Exception {
+        Method method = LookupRegistry.class.getMethod("lookupConfigBeans",
+                String.class, String.class);
+        Object[] args = new Object[] { TestingConfigRegistry.moduleName1,
+                TestingConfigRegistry.instName1 };
+        test(method, args);
+    }
+
+    @Test
+    public void testLookupConfigBean() throws Exception {
+        Method method = LookupRegistry.class.getMethod("lookupConfigBean",
+                String.class, String.class);
+        Object[] args = new Object[] { TestingConfigRegistry.moduleName1,
+                TestingConfigRegistry.instName1 };
+        test(method, args);
+    }
+
+    private void test(Method method, Object[] args) throws Exception {
+        for (Entry<LookupRegistry, ? extends Set<? extends LookupRegistry>> entry : lookupProvidersToClients
+                .entrySet()) {
+            Object expected = method.invoke(entry.getKey(), args);
+            for (LookupRegistry client : entry.getValue()) {
+                Object actual = method.invoke(client, args);
+                assertEquals("Error while comparing " + entry.getKey()
+                        + " with client " + client, expected, actual);
+            }
+        }
+    }
+
+    @Test
+    public void testException() {
+        for (Entry<LookupRegistry, ? extends Set<? extends LookupRegistry>> entry : lookupProvidersToClients
+                .entrySet()) {
+            for (LookupRegistry client : entry.getValue()) {
+                try {
+                    client.lookupConfigBean(
+                            InstanceNotFoundException.class.getSimpleName(), "");
+                    fail(client.toString());
+                } catch (InstanceNotFoundException e) {
+
+                }
+            }
+        }
+    }
+}
diff --git a/opendaylight/config/config-util/src/test/java/org/opendaylight/controller/config/util/TestingBeanImpl.java b/opendaylight/config/config-util/src/test/java/org/opendaylight/controller/config/util/TestingBeanImpl.java
new file mode 100644 (file)
index 0000000..1d0b9d6
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * 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.util;
+
+public class TestingBeanImpl implements TestingBeanMXBean {
+
+    @Override
+    public int getStat() {
+        return 0;
+    }
+
+    @Override
+    public void setStat() {
+
+    }
+}
diff --git a/opendaylight/config/config-util/src/test/java/org/opendaylight/controller/config/util/TestingBeanMXBean.java b/opendaylight/config/config-util/src/test/java/org/opendaylight/controller/config/util/TestingBeanMXBean.java
new file mode 100644 (file)
index 0000000..5d8bad3
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * 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.util;
+
+public interface TestingBeanMXBean {
+
+    int getStat();
+
+    void setStat();
+
+}
diff --git a/opendaylight/config/config-util/src/test/java/org/opendaylight/controller/config/util/TestingConfigRegistry.java b/opendaylight/config/config-util/src/test/java/org/opendaylight/controller/config/util/TestingConfigRegistry.java
new file mode 100644 (file)
index 0000000..d4ae42d
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+ * 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.util;
+
+import java.util.List;
+import java.util.Set;
+
+import javax.management.InstanceNotFoundException;
+import javax.management.ObjectName;
+
+import org.opendaylight.controller.config.api.ConflictingVersionException;
+import org.opendaylight.controller.config.api.ModuleIdentifier;
+import org.opendaylight.controller.config.api.ValidationException;
+import org.opendaylight.controller.config.api.jmx.CommitStatus;
+import org.opendaylight.controller.config.api.jmx.ConfigRegistryMXBean;
+import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
+
+import com.google.common.collect.Sets;
+
+public class TestingConfigRegistry implements ConfigRegistryMXBean {
+
+    static final ObjectName conf1, conf2, conf3, run1, run2, run3;
+
+    public static final String moduleName1 = "moduleA";
+    public static final String moduleName2 = "moduleB";
+    public static final String instName1 = "instA";
+    public static final String instName2 = "instB";
+
+    static {
+        conf1 = ObjectNameUtil.createON(ObjectNameUtil.ON_DOMAIN
+                + ":type=Module," + ObjectNameUtil.MODULE_FACTORY_NAME_KEY
+                + "=" + moduleName1);
+        conf2 = ObjectNameUtil.createON(ObjectNameUtil.ON_DOMAIN
+                + ":type=Module," + ObjectNameUtil.MODULE_FACTORY_NAME_KEY
+                + "=" + moduleName1 + "," + ObjectNameUtil.INSTANCE_NAME_KEY
+                + "=" + instName1);
+        conf3 = ObjectNameUtil.createON(ObjectNameUtil.ON_DOMAIN
+                + ":type=Module," + ObjectNameUtil.MODULE_FACTORY_NAME_KEY
+                + "=" + moduleName2 + "," + ObjectNameUtil.INSTANCE_NAME_KEY
+                + "=" + instName2);
+        run1 = ObjectNameUtil.createON(ObjectNameUtil.ON_DOMAIN
+                + ":type=RuntimeBean," + ObjectNameUtil.MODULE_FACTORY_NAME_KEY
+                + "=" + moduleName1);
+        run2 = ObjectNameUtil.createON(ObjectNameUtil.ON_DOMAIN
+                + ":type=RuntimeBean," + ObjectNameUtil.MODULE_FACTORY_NAME_KEY
+                + "=" + moduleName1 + "," + ObjectNameUtil.INSTANCE_NAME_KEY
+                + "=" + instName1);
+        run3 = ObjectNameUtil.createON(ObjectNameUtil.ON_DOMAIN
+                + ":type=RuntimeBean," + ObjectNameUtil.MODULE_FACTORY_NAME_KEY
+                + "=" + moduleName2 + "," + ObjectNameUtil.INSTANCE_NAME_KEY
+                + "=" + instName2);
+    }
+
+    @Override
+    public ObjectName beginConfig() {
+        return null;
+    }
+
+    @Override
+    public CommitStatus commitConfig(ObjectName transactonControllerON)
+            throws ConflictingVersionException, ValidationException {
+        if (transactonControllerON == null) {
+            Exception e = new RuntimeException("message");
+            throw ValidationException.createForSingleException(
+                    new ModuleIdentifier("moduleName", "instanceName"), e);
+        }
+        return null;
+    }
+
+    @Override
+    public List<ObjectName> getOpenConfigs() {
+        return null;
+    }
+
+    @Override
+    public boolean isHealthy() {
+        return false;
+    }
+
+    @Override
+    public Set<String> getAvailableModuleNames() {
+        return null;
+    }
+
+    @Override
+    public Set<ObjectName> lookupConfigBeans() {
+        return Sets.newHashSet(conf1, conf2, conf3);
+    }
+
+    @Override
+    public Set<ObjectName> lookupConfigBeans(String moduleName) {
+        if (moduleName.equals(moduleName1)) {
+            return Sets.newHashSet(conf1, conf2);
+        } else if (moduleName.equals(moduleName2)) {
+            return Sets.newHashSet(conf3);
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    public Set<ObjectName> lookupConfigBeans(String moduleName,
+            String instanceName) {
+        if (moduleName.equals(moduleName1) && instanceName.equals(instName1)) {
+            return Sets.newHashSet(conf2);
+        } else if (moduleName.equals(moduleName2)
+                && instanceName.equals(instName2)) {
+            return Sets.newHashSet(conf3);
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    public ObjectName lookupConfigBean(String moduleName, String instanceName)
+            throws InstanceNotFoundException {
+        if (moduleName.equals(InstanceNotFoundException.class.getSimpleName())) {
+            throw new InstanceNotFoundException();
+        }
+        return conf3;
+    }
+
+    @Override
+    public Set<ObjectName> lookupRuntimeBeans() {
+        return Sets.<ObjectName> newHashSet(run1, run2, run3);
+    }
+
+    @Override
+    public Set<ObjectName> lookupRuntimeBeans(String moduleName,
+            String instanceName) {
+        if (moduleName.equals(moduleName1) && instanceName.equals(instName1)) {
+            return Sets.<ObjectName> newHashSet(run2);
+        } else if (moduleName.equals(moduleName2)
+                && instanceName.equals(instName2)) {
+            return Sets.<ObjectName> newHashSet(run3);
+        } else {
+            return Sets.<ObjectName> newHashSet();
+        }
+    }
+
+}
diff --git a/opendaylight/config/config-util/src/test/java/org/opendaylight/controller/config/util/TestingConfigTransactionController.java b/opendaylight/config/config-util/src/test/java/org/opendaylight/controller/config/util/TestingConfigTransactionController.java
new file mode 100644 (file)
index 0000000..67e31b0
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * 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.util;
+
+import java.util.Set;
+
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.InstanceNotFoundException;
+import javax.management.ObjectName;
+
+import org.opendaylight.controller.config.api.ValidationException;
+import org.opendaylight.controller.config.api.jmx.ConfigTransactionControllerMXBean;
+import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
+
+import com.google.common.collect.Sets;
+
+public class TestingConfigTransactionController implements
+        ConfigTransactionControllerMXBean {
+
+    private final ObjectName conf1, conf2, conf3;
+
+    public static final String moduleName1 = "moduleA";
+    public static final String moduleName2 = "moduleB";
+    public static final String instName1 = "instA";
+    public static final String instName2 = "instB";
+
+    public TestingConfigTransactionController() throws Exception {
+        conf1 = ObjectNameUtil.createON(ObjectNameUtil.ON_DOMAIN
+                + ":type=Module," + ObjectNameUtil.MODULE_FACTORY_NAME_KEY
+                + "=" + moduleName1);
+        conf2 = ObjectNameUtil.createON(ObjectNameUtil.ON_DOMAIN
+                + ":type=Module," + ObjectNameUtil.MODULE_FACTORY_NAME_KEY
+                + "=" + moduleName1 + "," + ObjectNameUtil.INSTANCE_NAME_KEY
+                + "=" + instName1);
+        conf3 = ObjectNameUtil.createON(ObjectNameUtil.ON_DOMAIN
+                + ":type=Module," + ObjectNameUtil.MODULE_FACTORY_NAME_KEY
+                + "=" + moduleName2 + "," + ObjectNameUtil.INSTANCE_NAME_KEY
+                + "=" + instName2);
+    }
+
+    @Override
+    public ObjectName createModule(String moduleName, String instanceName)
+            throws InstanceAlreadyExistsException {
+        return null;
+    }
+
+    @Override
+    public void destroyModule(ObjectName objectName)
+            throws InstanceNotFoundException {
+    }
+
+    @Override
+    public void abortConfig() {
+    }
+
+    @Override
+    public void validateConfig() throws ValidationException {
+    }
+
+    @Override
+    public String getTransactionName() {
+        return null;
+    }
+
+    @Override
+    public Set<String> getAvailableModuleNames() {
+        return null;
+    }
+
+    @Override
+    public Set<ObjectName> lookupConfigBeans() {
+        return Sets.newHashSet(conf1, conf2, conf3);
+    }
+
+    @Override
+    public Set<ObjectName> lookupConfigBeans(String moduleName) {
+        if (moduleName.equals(moduleName1)) {
+            return Sets.newHashSet(conf1, conf2);
+        } else if (moduleName.equals(moduleName2)) {
+            return Sets.newHashSet(conf3);
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    public ObjectName lookupConfigBean(String moduleName, String instanceName)
+            throws InstanceNotFoundException {
+        if (moduleName.equals(InstanceNotFoundException.class.getSimpleName())) {
+            throw new InstanceNotFoundException();
+        }
+        return conf3;
+    }
+
+    @Override
+    public Set<ObjectName> lookupConfigBeans(String moduleName,
+            String instanceName) {
+        if (moduleName.equals(moduleName1) && instanceName.equals(instName1)) {
+            return Sets.newHashSet(conf2);
+        } else if (moduleName.equals(moduleName2)
+                && instanceName.equals(instName2)) {
+            return Sets.newHashSet(conf3);
+        } else {
+            return null;
+        }
+    }
+}
diff --git a/opendaylight/config/config-util/src/test/java/org/opendaylight/controller/config/util/jolokia/ListableJolokiaClientTest.java b/opendaylight/config/config-util/src/test/java/org/opendaylight/controller/config/util/jolokia/ListableJolokiaClientTest.java
new file mode 100644 (file)
index 0000000..bff8006
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * 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.util.jolokia;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertThat;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.json.simple.JSONObject;
+import org.json.simple.JSONValue;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.config.api.ValidationException;
+import org.opendaylight.controller.config.api.ValidationException.ExceptionMessageWithStackTrace;
+
+public class ListableJolokiaClientTest {
+
+    private Map<String, Map<String, ExceptionMessageWithStackTrace>> failedValidations;
+
+    private ValidationException val;
+
+    private final static String ex = "{\"message\":null,"
+            + "\"failedValidations\":"
+            + "{\"ifc2\":{\"impl1\":{\"message\":\"abc\",\"trace\":\"vvv\"},"
+            + "\"impl2\":{\"message\":\"abc2\",\"trace\":\"vvv2\"}},"
+            + "\"ifc1\":"
+            + "{\"impl1\":{\"message\":\"abc\",\"trace\":\"vvv\"},"
+            + "\"impl2\":{\"message\":\"abc2\",\"trace\":\"vvv2\"}}},"
+            + "\"localizedMessage\":null," + "\"cause\":null}";
+
+    @Before
+    public void setUp() {
+        failedValidations = new HashMap<String, Map<String, ExceptionMessageWithStackTrace>>();
+        Map<String, ExceptionMessageWithStackTrace> map1 = new HashMap<String, ValidationException.ExceptionMessageWithStackTrace>();
+        map1.put("impl1", new ExceptionMessageWithStackTrace("abc", "vvv"));
+        map1.put("impl2", new ExceptionMessageWithStackTrace("abc2", "vvv2"));
+        failedValidations.put("ifc1", map1);
+        failedValidations.put("ifc2", map1);
+        val = new ValidationException(failedValidations);
+    }
+
+    @Test
+    public void testParsing() {
+        JSONObject e = (JSONObject) JSONValue.parse(ex);
+        ValidationException val2 = ListableJolokiaClient
+                .createValidationExceptionFromJSONObject(e);
+        assertThat(val2.getMessage(), is(val.getMessage()));
+        assertThat(val2.getFailedValidations(), is(val.getFailedValidations()));
+    }
+
+}
diff --git a/opendaylight/config/pom.xml b/opendaylight/config/pom.xml
new file mode 100755 (executable)
index 0000000..c9be64a
--- /dev/null
@@ -0,0 +1,297 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.opendaylight.controller</groupId>
+        <artifactId>commons.opendaylight</artifactId>
+        <version>1.4.0-SNAPSHOT</version>
+        <relativePath>../commons/opendaylight</relativePath>
+    </parent>
+
+
+    <groupId>org.opendaylight</groupId>
+    <version>0.2.1-SNAPSHOT</version>
+    <artifactId>config-subsystem</artifactId>
+    <packaging>pom</packaging>
+    <name>${project.artifactId}</name>
+    <prerequisites>
+        <maven>3.0.4</maven>
+    </prerequisites>
+    <modules>
+        <module>config-api</module>
+        <module>config-manager</module>
+        <module>config-util</module>
+        <module>yang-jmx-generator</module>
+        <module>yang-jmx-generator-plugin</module>
+        <module>yang-jmx-generator-it</module>
+        <module>yang-store-api</module>
+        <module>yang-store-impl</module>
+        <module>yang-test</module>
+    </modules>
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <java.version.source>1.7</java.version.source>
+        <java.version.target>1.7</java.version.target>
+        <junit.version>4.10</junit.version>
+        <maven.bundle.version>2.3.7</maven.bundle.version>
+        <osgi.version>5.0.0</osgi.version>
+        <jacoco.version>0.6.2.201302030002</jacoco.version>
+        <slf4j.version>1.7.2</slf4j.version>
+        <jolokia.version>1.1.1</jolokia.version>
+        <opendaylight.yang.version>0.5.7-SNAPSHOT</opendaylight.yang.version>
+        <opendaylight.binding.version>0.5.7-SNAPSHOT</opendaylight.binding.version>
+        <jmxGeneratorPath>${project.build.directory}/generated-sources/config</jmxGeneratorPath>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>ch.qos.logback</groupId>
+            <artifactId>logback-classic</artifactId>
+            <version>${logback.version}</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.slf4j</groupId>
+                <artifactId>slf4j-api</artifactId>
+                <version>${slf4j.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.osgi</groupId>
+                <artifactId>org.osgi.core</artifactId>
+                <version>${osgi.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.google.code.findbugs</groupId>
+                <artifactId>jsr305</artifactId>
+                <version>2.0.1</version>
+            </dependency>
+            <dependency>
+                <groupId>commons-io</groupId>
+                <artifactId>commons-io</artifactId>
+                <version>2.4</version>
+            </dependency>
+            <dependency>
+                <groupId>com.google.guava</groupId>
+                <artifactId>guava</artifactId>
+                <version>14.0.1</version>
+            </dependency>
+            <dependency>
+                <groupId>org.jolokia</groupId>
+                <artifactId>jolokia-core</artifactId>
+                <version>${jolokia.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.jolokia</groupId>
+                <artifactId>jolokia-jvm</artifactId>
+                <version>${jolokia.version}</version>
+                <classifier>agent</classifier>
+            </dependency>
+            <dependency>
+                <groupId>org.jolokia</groupId>
+                <artifactId>jolokia-client-java</artifactId>
+                <version>${jolokia.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>junit</groupId>
+                <artifactId>junit</artifactId>
+                <version>${junit.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.commons</groupId>
+                <artifactId>commons-lang3</artifactId>
+                <version>${commons.lang.version}</version>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>2.5.1</version>
+                <configuration>
+                    <source>${java.version.source}</source>
+                    <target>${java.version.target}</target>
+                    <testSource>${java.version.source}</testSource>
+                    <testTarget>${java.version.target}</testTarget>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.jacoco</groupId>
+                <artifactId>jacoco-maven-plugin</artifactId>
+                <version>${jacoco.version}</version>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>prepare-agent</goal>
+                        </goals>
+                    </execution>
+                    <execution>
+                        <id>report</id>
+                        <phase>prepare-package</phase>
+                        <goals>
+                            <goal>check</goal>
+                            <goal>report</goal>
+                        </goals>
+                        <configuration>
+                            <outputDirectory>${basedir}/target/jacoco</outputDirectory>
+                            <haltOnFailure>false</haltOnFailure>
+                            <check>
+                                <classRatio>80</classRatio>
+                            </check>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>build-helper-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+        <pluginManagement>
+            <plugins>
+                <plugin>
+                    <groupId>org.opendaylight.yangtools</groupId>
+                    <artifactId>yang-maven-plugin</artifactId>
+                    <version>${opendaylight.yang.version}</version>
+                    <executions>
+                        <execution>
+                            <goals>
+                                <goal>generate-sources</goal>
+                            </goals>
+                            <configuration>
+                                <codeGenerators>
+                                    <generator>
+                                        <codeGeneratorClass>
+                                            org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+                                        </codeGeneratorClass>
+                                        <outputBaseDir>${jmxGeneratorPath}</outputBaseDir>
+                                        <additionalConfiguration>
+                                            <namespaceToPackage1>
+                                                urn:opendaylight:params:xml:ns:yang:controller==org.opendaylight.controller.config.yang
+                                            </namespaceToPackage1>
+                                        </additionalConfiguration>
+                                    </generator>
+                                </codeGenerators>
+                                <inspectDependencies>true</inspectDependencies>
+                            </configuration>
+                        </execution>
+                    </executions>
+                    <dependencies>
+                        <dependency>
+                            <groupId>org.opendaylight</groupId>
+                            <artifactId>yang-jmx-generator-plugin</artifactId>
+                            <version>0.2.1-SNAPSHOT</version>
+                        </dependency>
+                    </dependencies>
+                </plugin>
+                <!-- tell eclipse about generated source folders -->
+                <plugin>
+                    <groupId>org.codehaus.mojo</groupId>
+                    <artifactId>build-helper-maven-plugin</artifactId>
+                    <version>1.8</version>
+                    <executions>
+                        <execution>
+                            <id>add-source</id>
+                            <phase>generate-sources</phase>
+                            <goals>
+                                <goal>add-source</goal>
+                            </goals>
+                            <configuration>
+                                <sources>
+                                    <source>${jmxGeneratorPath}</source>
+                                </sources>
+                            </configuration>
+                        </execution>
+                    </executions>
+                </plugin>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-jar-plugin</artifactId>
+                    <version>2.4</version>
+                    <executions>
+                        <execution>
+                            <phase>package</phase>
+                            <goals>
+                                <goal>test-jar</goal>
+                            </goals>
+                        </execution>
+                    </executions>
+                </plugin>
+                <plugin>
+                    <groupId>org.apache.felix</groupId>
+                    <artifactId>maven-bundle-plugin</artifactId>
+                    <version>${maven.bundle.version}</version>
+                    <extensions>true</extensions>
+                    <configuration>
+                        <instructions>
+                            <Bundle-Name>${project.groupId}.${project.artifactId}</Bundle-Name>
+                        </instructions>
+                    </configuration>
+                </plugin>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-surefire-plugin</artifactId>
+                    <version>2.14.1</version>
+                    <configuration>
+                        <redirectTestOutputToFile>true</redirectTestOutputToFile>
+                        <parallel>classes</parallel>
+                        <forkCount>1C</forkCount>
+                        <reuseForks>false</reuseForks>
+                        <perCoreThreadCount>true</perCoreThreadCount>
+                        <threadCount>2</threadCount>
+                    </configuration>
+                </plugin>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-antrun-plugin</artifactId>
+                    <version>1.3</version>
+                </plugin>
+                <plugin>
+                    <groupId>org.codehaus.groovy.maven</groupId>
+                    <artifactId>gmaven-plugin</artifactId>
+                    <version>1.0</version>
+                </plugin>
+            </plugins>
+        </pluginManagement>
+    </build>
+
+    <pluginRepositories>
+        <pluginRepository>
+            <id>nexus.opendaylight.org</id>
+            <url>http://nexus.opendaylight.org/content/repositories/opendaylight.snapshot</url>
+            <releases>
+                <enabled>true</enabled>
+            </releases>
+            <snapshots>
+                <updatePolicy>daily</updatePolicy>
+            </snapshots>
+        </pluginRepository>
+    </pluginRepositories>
+
+    <repositories>
+        <repository>
+            <id>opendaylight-snapshot</id>
+            <url>http://nexus.opendaylight.org/content/repositories/opendaylight.snapshot</url>
+            <snapshots>
+                <updatePolicy>daily</updatePolicy>
+            </snapshots>
+        </repository>
+    </repositories>
+</project>
diff --git a/opendaylight/config/yang-jmx-generator-it/pom.xml b/opendaylight/config/yang-jmx-generator-it/pom.xml
new file mode 100644 (file)
index 0000000..847df6a
--- /dev/null
@@ -0,0 +1,89 @@
+<?xml version="1.0"?>
+<project
+        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
+        xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <artifactId>config-subsystem</artifactId>
+        <groupId>org.opendaylight</groupId>
+        <version>0.2.1-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>yang-jmx-generator-it</artifactId>
+    <name>${project.artifactId}</name>
+
+    <dependencies>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>config-api</artifactId>
+            <version>0.2.1-SNAPSHOT</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>yang-test</artifactId>
+            <version>0.2.1-SNAPSHOT</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>config-manager</artifactId>
+            <version>0.2.1-SNAPSHOT</version>
+            <scope>test</scope>
+            <type>test-jar</type>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>config-manager</artifactId>
+            <version>0.2.1-SNAPSHOT</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>config-util</artifactId>
+            <version>0.2.1-SNAPSHOT</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.bgpcep</groupId>
+            <artifactId>mockito-configuration</artifactId>
+            <version>0.2.0-SNAPSHOT</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <forkCount>1</forkCount>
+                    <reuseForks>false</reuseForks>
+                    <perCoreThreadCount>false</perCoreThreadCount>
+                </configuration>
+                <executions>
+                    <execution>
+                        <id>default-test</id>
+                        <configuration>
+                            <skip>true</skip>
+                        </configuration>
+                    </execution>
+                    <execution>
+                        <id>integration-tests</id>
+                        <phase>integration-test</phase>
+                        <goals>
+                            <goal>test</goal>
+                        </goals>
+                        <configuration>
+                            <includes>
+                                <include>**/org/opendaylight/controller/config/yangjmxgenerator/it/*.java</include>
+                            </includes>
+                            <skip>false</skip>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/opendaylight/config/yang-jmx-generator-it/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/it/ITTest.java b/opendaylight/config/yang-jmx-generator-it/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/it/ITTest.java
new file mode 100644 (file)
index 0000000..598b6b3
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * 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.it;
+
+import static org.junit.Assert.fail;
+
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.ObjectName;
+
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.opendaylight.controller.config.api.ConflictingVersionException;
+import org.opendaylight.controller.config.api.ValidationException;
+import org.opendaylight.controller.config.api.jmx.CommitStatus;
+import org.opendaylight.controller.config.manager.impl.AbstractConfigTest;
+import org.opendaylight.controller.config.manager.impl.factoriesresolver.HardcodedModuleFactoriesResolver;
+import org.opendaylight.controller.config.util.ConfigTransactionJMXClient;
+import org.opendaylight.controller.config.yang.test.impl.DtoA;
+import org.opendaylight.controller.config.yang.test.impl.DtoB;
+import org.opendaylight.controller.config.yang.test.impl.TestImplModuleFactory;
+import org.opendaylight.controller.config.yang.test.impl.TestImplModuleMXBean;
+
+@Ignore
+// ietf beans are not JMX compliant beans:
+// Do not know how to make a
+// org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev2010924.AsNumber
+// from a CompositeData: no method from(CompositeData); no constructor has
+// @ConstructorProperties annotation; does not have a public no-arg constructor;
+// not an interface
+public class ITTest extends AbstractConfigTest {
+
+    private TestImplModuleFactory factory;
+    private final String instanceName = "instance";
+
+    @Before
+    public void setUp() {
+
+        factory = new TestImplModuleFactory();
+        super.initConfigTransactionManagerImpl(new HardcodedModuleFactoriesResolver(
+                factory));
+    }
+
+    @Test
+    public void testCreateBean() throws InstanceAlreadyExistsException,
+            ValidationException, ConflictingVersionException {
+        ConfigTransactionJMXClient transaction = configRegistryClient
+                .createTransaction();
+
+        createModule(transaction, instanceName);
+        transaction.validateConfig();
+        CommitStatus status = transaction.commit();
+
+        assertBeanCount(1, factory.getImplementationName());
+        assertStatus(status, 1, 0, 0);
+    }
+
+    @Test
+    public void testReusingOldInstance() throws InstanceAlreadyExistsException,
+            ConflictingVersionException, ValidationException {
+
+        ConfigTransactionJMXClient transaction = configRegistryClient
+                .createTransaction();
+        createModule(transaction, instanceName);
+
+        transaction.commit();
+
+        assertBeanCount(1, factory.getImplementationName());
+
+        transaction = configRegistryClient.createTransaction();
+        CommitStatus status = transaction.commit();
+
+        assertBeanCount(1, factory.getImplementationName());
+        assertStatus(status, 0, 0, 1);
+
+    }
+
+    @Test
+    public void testInstanceAlreadyExistsException()
+            throws ConflictingVersionException, ValidationException,
+            InstanceAlreadyExistsException {
+        ConfigTransactionJMXClient transaction = configRegistryClient
+                .createTransaction();
+
+        createModule(transaction, instanceName);
+        transaction.commit();
+
+        transaction = configRegistryClient.createTransaction();
+        try {
+            createModule(transaction, instanceName);
+            fail();
+        } catch (InstanceAlreadyExistsException e) {
+
+        }
+    }
+
+    private ObjectName createModule(ConfigTransactionJMXClient transaction,
+            String instanceName) throws InstanceAlreadyExistsException {
+        ObjectName nameCreated = transaction.createModule(
+                factory.getImplementationName(), instanceName);
+        TestImplModuleMXBean mxBean = transaction.newMBeanProxy(nameCreated,
+                TestImplModuleMXBean.class);
+        mxBean.setSimpleInt((long) 45);
+        // mxBean.setAsNumber(new AsNumber((long) 999));
+        mxBean.setDtoA(new DtoA());
+        mxBean.setDtoB(new DtoB());
+        return nameCreated;
+
+    }
+
+}
diff --git a/opendaylight/config/yang-jmx-generator-plugin/pom.xml b/opendaylight/config/yang-jmx-generator-plugin/pom.xml
new file mode 100644 (file)
index 0000000..e13c4e8
--- /dev/null
@@ -0,0 +1,91 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <artifactId>config-subsystem</artifactId>
+        <groupId>org.opendaylight</groupId>
+        <version>0.2.1-SNAPSHOT</version>
+        <relativePath>..</relativePath>
+    </parent>
+    <artifactId>yang-jmx-generator-plugin</artifactId>
+    <dependencies>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight</groupId>
+            <artifactId>yang-jmx-generator</artifactId>
+            <version>0.2.1-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-maven-plugin-spi</artifactId>
+            <version>${opendaylight.yang.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>binding-generator-impl</artifactId>
+            <version>${opendaylight.binding.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jdt</groupId>
+            <artifactId>core</artifactId>
+            <version>3.3.0-v_771</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.freemarker</groupId>
+            <artifactId>freemarker</artifactId>
+            <version>2.3.20</version>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>config-api</artifactId>
+            <version>0.2.1-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>commons-io</groupId>
+            <artifactId>commons-io</artifactId>
+        </dependency>
+        <dependency>
+            <!--FIXME two implementations of slf4j on classpath, logback classic from parent-->
+            <groupId>com.googlecode.slf4j-maven-plugin-log</groupId>
+            <artifactId>slf4j-maven-plugin-log</artifactId>
+            <version>1.0.0</version>
+        </dependency>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.opendaylight</groupId>
+            <artifactId>yang-jmx-generator</artifactId>
+            <version>0.2.1-SNAPSHOT</version>
+            <scope>test</scope>
+            <type>test-jar</type>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse</groupId>
+            <artifactId>jdt</artifactId>
+            <version>3.3.0-v20070607-1300</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.bgpcep</groupId>
+            <artifactId>mockito-configuration</artifactId>
+            <version>0.2.0-SNAPSHOT</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jar-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/CodeWriter.java b/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/CodeWriter.java
new file mode 100644 (file)
index 0000000..13d828a
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * 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;
+
+import java.io.File;
+import java.util.List;
+
+import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry;
+import org.opendaylight.controller.config.yangjmxgenerator.ServiceInterfaceEntry;
+
+public interface CodeWriter {
+
+    File writeSie(ServiceInterfaceEntry sie, File targetBaseDir);
+
+    List<File> writeMbe(ModuleMXBeanEntry mbe, File targetBaseDir,
+            File mainBaseDir, File resourceBaseDir);
+
+}
diff --git a/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/FreeMarkerCodeWriterImpl.java b/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/FreeMarkerCodeWriterImpl.java
new file mode 100644 (file)
index 0000000..2b84ed1
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * 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;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry;
+import org.opendaylight.controller.config.yangjmxgenerator.RuntimeBeanEntry;
+import org.opendaylight.controller.config.yangjmxgenerator.ServiceInterfaceEntry;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.FtlFilePersister;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.FtlTemplate;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.RuntimeRegistratorFtlTemplate;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.TemplateFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.collect.Lists;
+
+final class FreeMarkerCodeWriterImpl implements CodeWriter {
+
+    private static final Logger logger = LoggerFactory
+            .getLogger(FreeMarkerCodeWriterImpl.class);
+
+    private final FtlFilePersister ftlFilePersister = new FtlFilePersister();
+
+    public FreeMarkerCodeWriterImpl() {
+    }
+
+    @Override
+    public File writeSie(ServiceInterfaceEntry sie, File outputBaseDir) {
+        try {
+            Collection<FtlTemplate> values = TemplateFactory.getFtlTemplates(
+                    sie).values();
+            return ftlFilePersister.persist(values, outputBaseDir, true).get(0);
+        } catch (Exception e) {
+            String message = "An error occurred during Service interface generating, sie:"
+                    + sie.getTypeName() + ", " + sie.getFullyQualifiedName();
+            logger.error(message, e);
+            throw new RuntimeException(message, e);
+        }
+    }
+
+    @Override
+    public List<File> writeMbe(ModuleMXBeanEntry mbe, File targetBaseDir,
+            File mainBaseDir, File resourceBaseDir) {
+        try {
+            List<File> generatedFiles = Lists.newArrayList();
+
+            generatedFiles.addAll(ftlFilePersister.persist(TemplateFactory
+                    .getFtlTemplates(mbe).values(), targetBaseDir, true));
+            generatedFiles.addAll(ftlFilePersister.persist(TemplateFactory
+                    .getFtlStubTemplates(mbe).values(), mainBaseDir, false));
+
+            // write runtime bean MXBeans and registrators
+            Collection<RuntimeBeanEntry> runtimeBeans = mbe.getRuntimeBeans();
+            if (runtimeBeans.size() > 0) {
+                List<FtlTemplate> allFtlFiles = new ArrayList<>();
+                { // registrators
+                    Map<String, FtlTemplate> registratorNamesToFtls = RuntimeRegistratorFtlTemplate
+                            .create(RuntimeRegistratorFtlTemplate.findRoot(runtimeBeans));
+
+                    allFtlFiles.addAll(registratorNamesToFtls.values());
+                }
+                { // TOs, MXBean interfaces
+                    for (RuntimeBeanEntry runtimeBeanEntry : runtimeBeans) {
+                        Collection<FtlTemplate> ftlFiles = TemplateFactory
+                                .getTOAndMXInterfaceFtlFiles(runtimeBeanEntry)
+                                .values();
+                        allFtlFiles.addAll(ftlFiles);
+                    }
+                }
+                boolean overwrite = true;
+
+                FtlFilePersister ftlFilePersister = new FtlFilePersister();
+                List<File> persisted = ftlFilePersister.persist(allFtlFiles,
+                        targetBaseDir, overwrite);
+                // FIXME: check for intersection
+                generatedFiles.addAll(persisted);
+            }
+
+            // purge nulls
+            for (Iterator<File> it = generatedFiles.iterator(); it.hasNext();) {
+                if (it.next() == null) {
+                    it.remove();
+                }
+            }
+
+            return generatedFiles;
+
+        } catch (Exception e) {
+            String message = "An error occurred during Module generating, mbe:"
+                    + mbe.getJavaNamePrefix();
+            logger.error(message, e);
+            throw new RuntimeException(message, e);
+        }
+    }
+
+}
diff --git a/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/JMXGenerator.java b/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/JMXGenerator.java
new file mode 100644 (file)
index 0000000..743ffba
--- /dev/null
@@ -0,0 +1,291 @@
+/*
+ * 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;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.maven.plugin.logging.Log;
+import org.apache.maven.project.MavenProject;
+import org.opendaylight.controller.config.spi.ModuleFactory;
+import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry;
+import org.opendaylight.controller.config.yangjmxgenerator.PackageTranslator;
+import org.opendaylight.controller.config.yangjmxgenerator.ServiceInterfaceEntry;
+import org.opendaylight.controller.config.yangjmxgenerator.TypeProviderWrapper;
+import org.opendaylight.yangtools.sal.binding.yang.types.TypeProviderImpl;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang2sources.spi.CodeGenerator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.slf4j.impl.StaticLoggerBinder;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+
+/**
+ * This class interfaces with yang-maven-plugin. Gets parsed yang modules in
+ * {@link SchemaContext}, and parameters form the plugin configuration, and
+ * writes service interfaces and/or modules.
+ */
+public class JMXGenerator implements CodeGenerator {
+
+    static final String NAMESPACE_TO_PACKAGE_DIVIDER = "==";
+    static final String NAMESPACE_TO_PACKAGE_PREFIX = "namespaceToPackage";
+    static final String MODULE_FACTORY_FILE_BOOLEAN = "moduleFactoryFile";
+
+    private PackageTranslator packageTranslator;
+    private final CodeWriter codeWriter;
+    private static final Logger logger = LoggerFactory
+            .getLogger(JMXGenerator.class);
+    private Map<String, String> namespaceToPackageMapping;
+    private File resourceBaseDir;
+    private File projectBaseDir;
+    private boolean generateModuleFactoryFile = true;
+
+    public JMXGenerator() {
+        this.codeWriter = new FreeMarkerCodeWriterImpl();
+    }
+
+    public JMXGenerator(CodeWriter codeWriter) {
+        this.codeWriter = codeWriter;
+    }
+
+    @Override
+    public Collection<File> generateSources(SchemaContext context,
+            File outputBaseDir, Set<Module> yangModulesInCurrentMavenModule) {
+
+        Preconditions.checkArgument(context != null, "Null context received");
+        Preconditions.checkArgument(outputBaseDir != null,
+                "Null outputBaseDir received");
+
+        Preconditions
+                .checkArgument(namespaceToPackageMapping != null && !namespaceToPackageMapping.isEmpty(),
+                        "No namespace to package mapping provided in additionalConfiguration");
+
+        packageTranslator = new PackageTranslator(namespaceToPackageMapping);
+
+        if (!outputBaseDir.exists())
+            outputBaseDir.mkdirs();
+
+        GeneratedFilesTracker generatedFiles = new GeneratedFilesTracker();
+        Map<QName, ServiceInterfaceEntry> qNamesToSIEs = new HashMap<>();
+
+        // create SIE structure qNamesToSIEs
+        for (Module module : context.getModules()) {
+            String packageName = packageTranslator.getPackageName(module);
+            Map<QName, ServiceInterfaceEntry> namesToSIEntries = ServiceInterfaceEntry
+                    .create(module, packageName);
+
+            for (Entry<QName, ServiceInterfaceEntry> sieEntry : namesToSIEntries
+                    .entrySet()) {
+
+                // merge value into qNamesToSIEs
+                if (qNamesToSIEs.containsKey(sieEntry.getKey()) == false) {
+                    qNamesToSIEs.put(sieEntry.getKey(), sieEntry.getValue());
+                } else {
+                    throw new IllegalStateException(
+                            "Cannot add two SIE with same qname "
+                                    + sieEntry.getValue());
+                }
+            }
+            if (yangModulesInCurrentMavenModule.contains(module)) {
+                // write this sie to disk
+                for (ServiceInterfaceEntry sie : namesToSIEntries.values()) {
+                    try {
+                        generatedFiles.addFile(codeWriter.writeSie(sie,
+                                outputBaseDir));
+                    } catch (Exception e) {
+                        throw new RuntimeException(
+                                "Error occurred during SIE source generate phase",
+                                e);
+                    }
+                }
+            }
+        }
+
+        File mainBaseDir = concatFolders(projectBaseDir, "src", "main", "java");
+        Preconditions.checkNotNull(resourceBaseDir,
+                "resource base dir attribute was null");
+
+        StringBuffer fullyQualifiedNamesOfFactories = new StringBuffer();
+        // create MBEs
+        for (Module module : yangModulesInCurrentMavenModule) {
+            String packageName = packageTranslator.getPackageName(module);
+            Map<String /* MB identity local name */, ModuleMXBeanEntry> namesToMBEs = ModuleMXBeanEntry
+                    .create(module, qNamesToSIEs, context, new TypeProviderWrapper(new TypeProviderImpl(context)),
+                            packageName);
+
+            for (Entry<String, ModuleMXBeanEntry> mbeEntry : namesToMBEs
+                    .entrySet()) {
+                ModuleMXBeanEntry mbe = mbeEntry.getValue();
+                try {
+                    List<File> files1 = codeWriter.writeMbe(mbe, outputBaseDir,
+                            mainBaseDir, resourceBaseDir);
+                    generatedFiles.addFile(files1);
+                } catch (Exception e) {
+                    throw new RuntimeException(
+                            "Error occurred during MBE source generate phase",
+                            e);
+                }
+                fullyQualifiedNamesOfFactories.append(mbe
+                        .getFullyQualifiedName(mbe.getStubFactoryName()));
+                fullyQualifiedNamesOfFactories.append("\n");
+            }
+        }
+        // create ModuleFactory file if needed
+        if (fullyQualifiedNamesOfFactories.length() > 0
+                && generateModuleFactoryFile) {
+            File serviceLoaderFile = JMXGenerator.concatFolders(
+                    resourceBaseDir, "META-INF", "services",
+                    ModuleFactory.class.getName());
+            // if this file does not exist, create empty file
+            serviceLoaderFile.getParentFile().mkdirs();
+            try {
+                serviceLoaderFile.createNewFile();
+                FileUtils.write(serviceLoaderFile,
+                        fullyQualifiedNamesOfFactories.toString());
+            } catch (IOException e) {
+                String message = "Cannot write to " + serviceLoaderFile;
+                logger.error(message);
+                throw new RuntimeException(message, e);
+            }
+        }
+        return generatedFiles.getFiles();
+    }
+
+    static File concatFolders(File projectBaseDir, String... folderNames) {
+        StringBuilder b = new StringBuilder();
+        for (String folder : folderNames) {
+            b.append(folder);
+            b.append(File.separator);
+        }
+        return new File(projectBaseDir, b.toString());
+    }
+
+    @Override
+    public void setAdditionalConfig(Map<String, String> additionalCfg) {
+        if (logger != null)
+            logger.debug(getClass().getCanonicalName(),
+                    ": Additional configuration received: ",
+                    additionalCfg.toString());
+        this.namespaceToPackageMapping = extractNamespaceMapping(additionalCfg);
+        this.generateModuleFactoryFile = extractModuleFactoryBoolean(additionalCfg);
+    }
+
+    private boolean extractModuleFactoryBoolean(
+            Map<String, String> additionalCfg) {
+        String bool = additionalCfg.get(MODULE_FACTORY_FILE_BOOLEAN);
+        if (bool == null)
+            return true;
+        if (bool.equals("false"))
+            return false;
+        return true;
+    }
+
+    @Override
+    public void setLog(Log log) {
+        StaticLoggerBinder.getSingleton().setLog(log);
+    }
+
+    private static Map<String, String> extractNamespaceMapping(
+            Map<String, String> additionalCfg) {
+        Map<String, String> namespaceToPackage = Maps.newHashMap();
+        for (String key : additionalCfg.keySet()) {
+            if (key.startsWith(NAMESPACE_TO_PACKAGE_PREFIX)) {
+                String mapping = additionalCfg.get(key);
+                NamespaceMapping mappingResolved = extractNamespaceMapping(mapping);
+                namespaceToPackage.put(mappingResolved.namespace,
+                        mappingResolved.packageName);
+            }
+        }
+        return namespaceToPackage;
+    }
+
+    static Pattern namespaceMappingPattern = Pattern.compile("(.+)"
+            + NAMESPACE_TO_PACKAGE_DIVIDER + "(.+)");
+
+    private static NamespaceMapping extractNamespaceMapping(String mapping) {
+        Matcher matcher = namespaceMappingPattern.matcher(mapping);
+        Preconditions
+                .checkArgument(matcher.matches(), String.format("Namespace to package mapping:%s is in invalid " +
+                        "format, requested format is: %s", mapping, namespaceMappingPattern));
+        return new NamespaceMapping(matcher.group(1), matcher.group(2));
+    }
+
+    private static class NamespaceMapping {
+        public NamespaceMapping(String namespace, String packagename) {
+            this.namespace = namespace;
+            this.packageName = packagename;
+        }
+
+        private final String namespace, packageName;
+    }
+
+    @Override
+    public void setResourceBaseDir(File resourceDir) {
+        this.resourceBaseDir = resourceDir;
+    }
+
+    @Override
+    public void setMavenProject(MavenProject project) {
+        this.projectBaseDir = project.getBasedir();
+
+        if (logger != null)
+            logger.debug(getClass().getCanonicalName(), " project base dir: ",
+                    projectBaseDir);
+    }
+
+    @VisibleForTesting
+    static class GeneratedFilesTracker {
+        private final Set<File> files = Sets.newHashSet();
+
+        void addFile(File file) {
+            if (files.contains(file)) {
+                List<File> undeletedFiles = Lists.newArrayList();
+                for (File presentFile : files) {
+                    if (presentFile.delete() == false) {
+                        undeletedFiles.add(presentFile);
+                    }
+                }
+                if (undeletedFiles.isEmpty() == false) {
+                    logger.error(
+                            "Illegal state occurred: Unable to delete already generated files, undeleted files: {}",
+                            undeletedFiles);
+                }
+                throw new IllegalStateException(
+                        "Name conflict in generated files, file" + file
+                                + " present twice");
+            }
+            files.add(file);
+        }
+
+        void addFile(Collection<File> files) {
+            for (File file : files) {
+                addFile(file);
+            }
+        }
+
+        public Set<File> getFiles() {
+            return files;
+        }
+    }
+}
diff --git a/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/AbstractFactoryTemplate.java b/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/AbstractFactoryTemplate.java
new file mode 100644 (file)
index 0000000..b9245a4
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * 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.ftl;
+
+import java.util.Collections;
+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.spi.Module;
+import org.opendaylight.controller.config.spi.ModuleFactory;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Constructor;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Field;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Header;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.MethodDefinition;
+
+import com.google.common.collect.Lists;
+
+/**
+ *
+ */
+public class AbstractFactoryTemplate extends GeneralClassTemplate {
+
+    private static final List<String> implementedIfcs = Lists
+            .newArrayList(ModuleFactory.class.getCanonicalName());
+
+    private final String globallyUniqueName, moduleInstanceType;
+    private final List<String> providedServices;
+
+    public AbstractFactoryTemplate(Header header, String packageName,
+            String abstractFactoryName, String globallyUniqueName,
+            String moduleInstanceType, List<Field> fields,
+            List<String> providedServices) {
+        super(header, packageName, abstractFactoryName, Collections
+                .<String> emptyList(), implementedIfcs, fields, Collections
+                .<MethodDefinition> emptyList(), true, false, Collections
+                .<Constructor> emptyList());
+        this.globallyUniqueName = globallyUniqueName;
+        this.moduleInstanceType = moduleInstanceType;
+        this.providedServices = providedServices;
+    }
+
+    public String getGloballyUniqueName() {
+        return globallyUniqueName;
+    }
+
+    public String getInstanceType() {
+        return AutoCloseable.class.getCanonicalName();
+    }
+
+    public String getModuleNameType() {
+        return ModuleIdentifier.class.getCanonicalName();
+    }
+
+    public String getModuleInstanceType() {
+        return moduleInstanceType;
+    }
+
+    public String getAbstractServiceInterfaceType() {
+        return AbstractServiceInterface.class.getCanonicalName();
+    }
+
+    public List<String> getProvidedServices() {
+        return providedServices;
+    }
+
+    public String getModuleType() {
+        return Module.class.getCanonicalName();
+    }
+
+    public String getDependencyResolverType() {
+        return DependencyResolver.class.getCanonicalName();
+    }
+
+    public String getDynamicMBeanWithInstanceType() {
+        return DynamicMBeanWithInstance.class.getCanonicalName();
+    }
+
+    @Override
+    public String getFtlTempleteLocation() {
+        return "factory_abs_template.ftl";
+    }
+
+}
diff --git a/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/AbstractFtlTemplate.java b/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/AbstractFtlTemplate.java
new file mode 100644 (file)
index 0000000..7f80299
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * 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.ftl;
+
+import java.io.File;
+import java.util.List;
+
+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.ftl.model.Header;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Method;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.TypeDeclaration;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.util.FullyQualifiedNameHelper;
+
+import com.google.common.collect.Lists;
+
+public abstract class AbstractFtlTemplate implements FtlTemplate {
+    private final String packageName;
+    private final List<Field> fields;
+    private final List<Annotation> annotations;
+    private final List<? extends Method> methods;
+    private String javadoc = null;
+    private final TypeDeclaration typeDeclaration;
+    private final Header header;
+
+    protected AbstractFtlTemplate(Header header, String packageName,
+            List<Field> fields, List<? extends Method> methods,
+            TypeDeclaration typeDeclaration) {
+        this.packageName = packageName;
+        this.fields = fields;
+        this.methods = methods;
+        this.annotations = Lists.newArrayList();
+        this.typeDeclaration = typeDeclaration;
+        this.header = header;
+    }
+
+    @Override
+    public Header getHeader() {
+        return header;
+    }
+
+    @Override
+    public String getFullyQualifiedName() {
+        return FullyQualifiedNameHelper.getFullyQualifiedName(getPackageName(),
+                getTypeDeclaration().getName());
+    }
+
+    @Override
+    public String getPackageName() {
+        return packageName;
+    }
+
+    @Override
+    public TypeDeclaration getTypeDeclaration() {
+        return typeDeclaration;
+    }
+
+    @Override
+    public String getJavadoc() {
+        return javadoc;
+    }
+
+    public void setJavadoc(String javadoc) {
+        this.javadoc = javadoc;
+    }
+
+    @Override
+    public List<Annotation> getAnnotations() {
+        return annotations;
+    }
+
+    @Override
+    public List<Field> getFields() {
+        return fields;
+    }
+
+    @Override
+    public List<? extends Method> getMethods() {
+        return methods;
+    }
+
+    @Override
+    public File getRelativeFile() {
+        return new File(packageName.replace(".", File.separator),
+                getTypeDeclaration().getName() + ".java");
+    }
+
+    @Override
+    public String getFtlTempleteLocation() {
+        return "abstract_ftl_file.ftl";
+    }
+
+    @Override
+    public String toString() {
+        return "AbstractFtlTemplate{" + "typeDeclaration=" + typeDeclaration
+                + ", packageName='" + packageName + '\'' + '}';
+    }
+}
diff --git a/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/AbstractModuleTemplate.java b/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/AbstractModuleTemplate.java
new file mode 100644 (file)
index 0000000..c40bfdf
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * 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.ftl;
+
+import java.util.Collections;
+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.runtime.RootRuntimeBeanRegistrator;
+import org.opendaylight.controller.config.spi.Module;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Constructor;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Field;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Header;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.MethodDefinition;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.ModuleField;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ *
+ */
+public class AbstractModuleTemplate extends GeneralClassTemplate {
+
+    private final List<ModuleField> moduleFields;
+    private final boolean runtime;
+    private final String registratorType;
+
+    public AbstractModuleTemplate(Header header, String packageName,
+            String abstractModuleName, List<String> implementedIfcs,
+            List<ModuleField> moduleFields, List<MethodDefinition> methods,
+            boolean isRuntime, String registratorType) {
+        super(header, packageName, abstractModuleName, Collections
+                .<String> emptyList(), implementedIfcs, Collections
+                .<Field> emptyList(), methods, true, false, Collections
+                .<Constructor> emptyList());
+        this.moduleFields = moduleFields;
+        this.runtime = isRuntime;
+        this.registratorType = registratorType;
+    }
+
+    public List<ModuleField> getModuleFields() {
+        return moduleFields;
+    }
+
+    public String getInstanceType() {
+        return AutoCloseable.class.getCanonicalName();
+    }
+
+    public String getModuleNameType() {
+        return ModuleIdentifier.class.getCanonicalName();
+    }
+
+    public String getAbstractServiceInterfaceType() {
+        return AbstractServiceInterface.class.getCanonicalName();
+    }
+
+    public String getModuleType() {
+        return Module.class.getCanonicalName();
+    }
+
+    public String getRegistratorType() {
+        return registratorType;
+    }
+
+    public boolean isRuntime() {
+        return runtime;
+    }
+
+    public String getDependencyResolverType() {
+        return DependencyResolver.class.getCanonicalName();
+    }
+
+    public String getDynamicMBeanWithInstanceType() {
+        return DynamicMBeanWithInstance.class.getCanonicalName();
+    }
+
+    public String getRootRuntimeRegistratorType() {
+        return RootRuntimeBeanRegistrator.class.getCanonicalName();
+    }
+
+    @Override
+    public String getFtlTempleteLocation() {
+        return "module_abs_template_new.ftl";
+    }
+
+    public String getLoggerType() {
+        return Logger.class.getCanonicalName();
+    }
+
+    public String getLoggerFactoryType() {
+        return LoggerFactory.class.getCanonicalName();
+    }
+
+}
diff --git a/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/FtlFilePersister.java b/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/FtlFilePersister.java
new file mode 100644 (file)
index 0000000..00cc8ca
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * 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.ftl;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.directives.AnnotationsDirective;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.directives.ConstructorsDirective;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.directives.FieldsDirectiveProg;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.directives.HeaderDirective;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.directives.JavadocDirective;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.directives.MethodsDirective;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.directives.ModuleFieldsDirective;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.directives.TypeDeclarationDirective;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.directives.UnimplementedExceptionDirective;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.annotations.VisibleForTesting;
+
+import freemarker.template.Configuration;
+import freemarker.template.Template;
+import freemarker.template.TemplateException;
+
+public class FtlFilePersister {
+    private static final Logger logger = LoggerFactory
+            .getLogger(FtlFilePersister.class);
+
+    private static final Pattern TRAILING_WHITESPACES = Pattern.compile(" +$", Pattern.MULTILINE);
+
+    @VisibleForTesting
+    public Map<FtlTemplate, String> serializeFtls(
+            Collection<? extends FtlTemplate> ftlFiles) {
+        Map<FtlTemplate, String> result = new HashMap<>();
+        for (FtlTemplate ftlFile : ftlFiles) {
+
+            try (Writer writer = new StringWriter()) {
+                Template template = getCfg().getTemplate(
+                        ftlFile.getFtlTempleteLocation());
+                try {
+                    template.process(ftlFile, writer);
+                } catch (TemplateException e) {
+                    throw new IllegalStateException(
+                            "Template error while generating " + ftlFile, e);
+                }
+                String fileContent = writer.toString();
+                // remove trailing spaces
+                fileContent = TRAILING_WHITESPACES.matcher(fileContent).replaceAll("");
+                result.put(ftlFile, fileContent);
+            } catch (IOException e) {
+                throw new IllegalStateException(
+                        "Exception while processing template", e);
+            }
+        }
+
+        return result;
+    }
+
+    public List<File> persist(Collection<? extends FtlTemplate> ftlFiles,
+            File dstFolder, boolean overwrite) throws IOException {
+        Map<FtlTemplate, String> ftlFileStringMap = serializeFtls(ftlFiles);
+        List<File> result = new ArrayList<>();
+        for (Entry<FtlTemplate, String> entry : ftlFileStringMap.entrySet()) {
+            FtlTemplate ftlFile = entry.getKey();
+            File targetFile = new File(dstFolder, ftlFile.getRelativeFile()
+                    .getPath());
+            File pathToFile = targetFile.getParentFile();
+            if (pathToFile.exists() == false) {
+                pathToFile.mkdirs();
+            }
+            if (targetFile.exists() && overwrite == false) {
+                logger.info("Skipping {} since it already exists", targetFile);
+            } else {
+                try (Writer fileWriter = new FileWriter(targetFile)) {
+                    fileWriter.write(entry.getValue());
+                }
+                logger.info("{}: File {} generated successfully",
+                        JMXGenerator.class.getCanonicalName(), targetFile);
+                result.add(targetFile);
+            }
+        }
+        return result;
+    }
+
+    private Configuration getCfg() {
+        Configuration cfg = new Configuration();
+        cfg.setClassForTemplateLoading(getClass(), "/freeMarker/");
+        cfg.setSharedVariable("javadocD", new JavadocDirective());
+        cfg.setSharedVariable("annotationsD", new AnnotationsDirective());
+        cfg.setSharedVariable("typeDeclarationD",
+                new TypeDeclarationDirective());
+        cfg.setSharedVariable("constructorsD", new ConstructorsDirective());
+        cfg.setSharedVariable("fieldsD", new FieldsDirectiveProg());
+        cfg.setSharedVariable("moduleFieldsD", new ModuleFieldsDirective());
+        cfg.setSharedVariable("methodsD", new MethodsDirective());
+        cfg.setSharedVariable("headerD", new HeaderDirective());
+        cfg.setSharedVariable("unimplementedExceptionD",
+                new UnimplementedExceptionDirective());
+        return cfg;
+    }
+
+}
diff --git a/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/FtlTemplate.java b/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/FtlTemplate.java
new file mode 100644 (file)
index 0000000..8172a58
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * 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.ftl;
+
+import java.io.File;
+import java.util.List;
+
+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.ftl.model.Header;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Method;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.TypeDeclaration;
+
+public interface FtlTemplate {
+
+    Header getHeader();
+
+    String getPackageName();
+
+    String getJavadoc();
+
+    public List<Annotation> getAnnotations();
+
+    TypeDeclaration getTypeDeclaration();
+
+    public String getFullyQualifiedName();
+
+    public List<Field> getFields();
+
+    List<? extends Method> getMethods();
+
+    /**
+     * @return relative path to file to be created.
+     */
+    public File getRelativeFile();
+
+    /**
+     *
+     * @return ftl template location
+     */
+    public String getFtlTempleteLocation();
+}
diff --git a/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/GeneralClassTemplate.java b/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/GeneralClassTemplate.java
new file mode 100644 (file)
index 0000000..4df7b2a
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Constructor;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Field;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Header;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.MethodDefinition;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.TypeDeclaration;
+
+public class GeneralClassTemplate extends AbstractFtlTemplate {
+
+    private final List<Constructor> constructors;
+
+    public GeneralClassTemplate(Header header, String packageName, String name,
+            List<String> extendedClasses, List<String> implementedIfcs,
+            List<Field> fields, List<MethodDefinition> methods) {
+        this(header, packageName, name, extendedClasses, implementedIfcs,
+                fields, methods, false, false, Collections
+                        .<Constructor> emptyList());
+    }
+
+    public GeneralClassTemplate(Header header, String packageName, String name,
+            List<String> extendedClasses, List<String> implementedIfcs,
+            List<Field> fields, List<MethodDefinition> methods,
+            boolean isAbstract, boolean isFinal, List<Constructor> constructors) {
+        super(header, packageName, fields, methods, new TypeDeclaration(
+                "class", name, checkCardinality(extendedClasses),
+                implementedIfcs, isAbstract, isFinal));
+        this.constructors = constructors;
+    }
+
+    static List<String> checkCardinality(List<String> extendedClass) {
+        if (extendedClass.size() > 1)
+            throw new IllegalArgumentException(
+                    "Class cannot have more than one super " + "class");
+        return extendedClass;
+    }
+
+    public List<Constructor> getConstructors() {
+        return constructors;
+    }
+
+}
diff --git a/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/GeneralInterfaceTemplate.java b/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/GeneralInterfaceTemplate.java
new file mode 100644 (file)
index 0000000..1539e10
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * 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.ftl;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Field;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Header;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.MethodDeclaration;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.TypeDeclaration;
+
+public class GeneralInterfaceTemplate extends AbstractFtlTemplate {
+
+    public GeneralInterfaceTemplate(Header header, String packageName,
+            String name, List<String> extendedInterfaces,
+            List<MethodDeclaration> methods) {
+        super(header, packageName, Collections.<Field> emptyList(), methods,
+                new TypeDeclaration("interface", name, extendedInterfaces,
+                        Collections.<String> emptyList()));
+    }
+}
diff --git a/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/RuntimeRegistratorFtlTemplate.java b/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/RuntimeRegistratorFtlTemplate.java
new file mode 100644 (file)
index 0000000..d07edae
--- /dev/null
@@ -0,0 +1,296 @@
+/*
+ * 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.ftl;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
+import static java.lang.String.format;
+
+import java.io.Closeable;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.opendaylight.controller.config.api.runtime.HierarchicalRuntimeBeanRegistration;
+import org.opendaylight.controller.config.api.runtime.RootRuntimeBeanRegistrator;
+import org.opendaylight.controller.config.yangjmxgenerator.RuntimeBeanEntry;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Annotation;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Annotation.Parameter;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Field;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.MethodDefinition;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.util.FullyQualifiedNameHelper;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.Lists;
+
+public class RuntimeRegistratorFtlTemplate extends GeneralClassTemplate {
+
+    private RuntimeRegistratorFtlTemplate(RuntimeBeanEntry runtimeBeanEntry,
+            String name, List<Field> fields, List<MethodDefinition> methods) {
+        // TODO header
+        super(null, runtimeBeanEntry.getPackageName(), name, Collections
+                .<String> emptyList(), Arrays.asList(Closeable.class
+                .getCanonicalName()), fields, methods);
+    }
+
+    public static RuntimeBeanEntry findRoot(
+            Collection<RuntimeBeanEntry> runtimeBeanEntries) {
+        RuntimeBeanEntry result = null;
+        for (RuntimeBeanEntry rb : runtimeBeanEntries) {
+            if (rb.isRoot()) {
+                if (result != null) {
+                    throw new IllegalArgumentException(
+                            "More than one root runtime bean found");
+                }
+                result = rb;
+            }
+        }
+        if (result != null) {
+            return result;
+        }
+        throw new IllegalArgumentException("No root runtime bean found");
+    }
+
+    private static String constructConstructorBody(
+            List<Field> constructorParameters) {
+        StringBuffer constructorBody = new StringBuffer();
+        for (Field field : constructorParameters) {
+            constructorBody.append("this.");
+            constructorBody.append(field.getName());
+            constructorBody.append("=");
+            constructorBody.append(field.getName());
+            constructorBody.append(";\n");
+        }
+        return constructorBody.toString();
+    }
+
+    // TODO Move to factory
+    /**
+     * Get registrator and n registration ftls where n is equal to total number
+     * of runtime beans in hierarchy.
+     */
+    public static Map<String, FtlTemplate> create(RuntimeBeanEntry rootRB) {
+        checkArgument(rootRB.isRoot(), "RuntimeBeanEntry must be root");
+        String registratorName = getJavaNameOfRuntimeRegistrator(rootRB);
+        List<MethodDefinition> methods = new ArrayList<>();
+        Field rootRuntimeBeanRegistratorField = new Field(
+                Lists.newArrayList("final"),
+                RootRuntimeBeanRegistrator.class.getName(),
+                "rootRuntimeBeanRegistrator");
+        List<Field> constructorParameters = Lists
+                .newArrayList(rootRuntimeBeanRegistratorField);
+        String constructorBody = constructConstructorBody(constructorParameters);
+        MethodDefinition constructor = MethodDefinition.createConstructor(
+                registratorName, constructorParameters, constructorBody);
+        methods.add(constructor);
+
+        LinkedHashMap<String, RuntimeRegistratorFtlTemplate> RuntimeRegistratorFtlTemplates = createRegistrationHierarchy(
+                rootRB, Collections.<String> emptySet());
+        RuntimeRegistratorFtlTemplate rootFtlFile = RuntimeRegistratorFtlTemplates
+                .values().iterator().next();
+
+        {// add register(rootruntimemxbean)
+            String fullyQualifiedNameOfMXBean = FullyQualifiedNameHelper
+                    .getFullyQualifiedName(rootRB.getPackageName(), rootRB.getJavaNameOfRuntimeMXBean());
+            String childRegistratorFQN = rootFtlFile.getFullyQualifiedName();
+            Field rbParameter = new Field(fullyQualifiedNameOfMXBean, "rb");
+            StringBuffer registerBody = new StringBuffer();
+            registerBody.append(format("%s %s = this.%s.registerRoot(%s);\n",
+                    HierarchicalRuntimeBeanRegistration.class
+                            .getCanonicalName(), hierachchicalRegistration
+                            .getName(), rootRuntimeBeanRegistratorField
+                            .getName(), rbParameter.getName()));
+            registerBody.append(format("return new %s(%s);\n",
+                    rootFtlFile.getFullyQualifiedName(),
+                    hierachchicalRegistration.getName()));
+
+            MethodDefinition registerMethod = new MethodDefinition(
+                    childRegistratorFQN, "register",
+                    Arrays.asList(rbParameter), registerBody.toString());
+            methods.add(registerMethod);
+        }
+
+        MethodDefinition closeRegistrator = createCloseMethodToCloseField(rootRuntimeBeanRegistratorField);
+        methods.add(closeRegistrator);
+
+        // TODO add header
+        GeneralClassTemplate registrator = new GeneralClassTemplate(null,
+                rootRB.getPackageName(), registratorName,
+                Collections.<String> emptyList(), Arrays.asList(Closeable.class
+                        .getCanonicalName()), constructorParameters, methods);
+
+        checkState(RuntimeRegistratorFtlTemplates.containsKey(registrator
+                .getTypeDeclaration().getName()) == false, "Name conflict: "
+                + registrator.getTypeDeclaration().getName());
+        Map<String, FtlTemplate> result = new HashMap<>();
+        result.putAll(RuntimeRegistratorFtlTemplates);
+        result.put(registrator.getTypeDeclaration().getName(), registrator);
+        return result;
+    }
+
+    private static Field hierachchicalRegistration = new Field(
+            Lists.newArrayList("final"),
+            HierarchicalRuntimeBeanRegistration.class.getCanonicalName(),
+            "registration");
+
+    // TODO move to factory + RuntimeBeanEntry
+    /**
+     * Create ftls representing registrations. First registration is represents
+     * parent.
+     *
+     * @return map containing java class name as key, instance representing the
+     *         java file as value.
+     */
+    private static LinkedHashMap<String, RuntimeRegistratorFtlTemplate> createRegistrationHierarchy(
+            RuntimeBeanEntry parent, Set<String> occupiedKeys) {
+        LinkedHashMap<String, RuntimeRegistratorFtlTemplate> unorderedResult = new LinkedHashMap<>();
+        List<MethodDefinition> methods = new ArrayList<>();
+
+        // hierarchy of ON is created as follows:
+        // root RB: <domain>, type=RuntimeBean
+        // 1st RB in hierarchy: <domain>, type=RuntimeBean, <java name of leaf
+        // list>: key or counter
+        // n-th RB in hierarchy has same ON as n-1, with added <java name of
+        // leaf list>: key or counter
+        if (occupiedKeys.contains(parent.getJavaNamePrefix())) {
+            throw new IllegalArgumentException(
+                    "Name conflict in runtime bean hierarchy - java name found more than "
+                            + "once. Consider using java-name extension. Conflicting name: "
+                            + parent.getJavaNamePrefix());
+        }
+        Set<String> currentOccupiedKeys = new HashSet<>(occupiedKeys);
+        currentOccupiedKeys.add(parent.getJavaNamePrefix());
+
+        Field registratorsMapField = new Field(Arrays.asList("final"),
+                TypeHelper.getGenericType(Map.class, String.class,
+                        AtomicInteger.class), "unkeyedMap", "new "
+                        + TypeHelper.getGenericType(HashMap.class,
+                                String.class, AtomicInteger.class) + "()");
+
+        // create register methods for children
+        for (RuntimeBeanEntry child : parent.getChildren()) {
+            checkArgument(parent.getPackageName()
+                    .equals(child.getPackageName()), "Invalid package name");
+
+            // call itself recursively to generate child
+            // registrators/registrations
+            LinkedHashMap<String, RuntimeRegistratorFtlTemplate> childRegistratorMap = createRegistrationHierarchy(
+                    child, currentOccupiedKeys);
+            for (Entry<String, RuntimeRegistratorFtlTemplate> entry : childRegistratorMap
+                    .entrySet()) {
+                if (unorderedResult.containsKey(entry.getKey())) {
+                    throw new IllegalStateException(
+                            "Conflicting name found while generating runtime registration:"
+                                    + entry.getKey());
+                }
+                unorderedResult.put(entry.getKey(), entry.getValue());
+            }
+
+            if (childRegistratorMap.size() > 0) {
+                // first entry is the direct descendant according to the create
+                // contract
+                RuntimeRegistratorFtlTemplate childRegistrator = childRegistratorMap
+                        .values().iterator().next();
+                StringBuffer body = new StringBuffer();
+                String key, value;
+                key = child.getJavaNamePrefix();
+                body.append(format(
+                        "String key = \"%s\"; //TODO: check for conflicts\n",
+                        key));
+
+                if (child.getKeyJavaName().isPresent()) {
+                    value = "bean.get" + child.getKeyJavaName().get() + "()";
+                    value = "String.valueOf(" + value + ")";
+                } else {
+                    body.append("java.util.concurrent.atomic.AtomicInteger counter = unkeyedMap.get(key);\n"
+                            + "if (counter==null){\n"
+                            + "counter = new java.util.concurrent.atomic.AtomicInteger();\n"
+                            + "unkeyedMap.put(key, counter);\n" + "}\n");
+                    value = "String.valueOf(counter.incrementAndGet())";
+                }
+                body.append(format("String value = %s;\n", value));
+                body.append(format("%s r = %s.register(key, value, bean);\n",
+                        HierarchicalRuntimeBeanRegistration.class
+                                .getCanonicalName(), hierachchicalRegistration
+                                .getName()));
+                body.append(format("return new %s(r);",
+                        childRegistrator.getFullyQualifiedName()));
+
+                Field param = new Field(Lists.newArrayList("final"),
+                        child.getJavaNameOfRuntimeMXBean(), "bean");
+                MethodDefinition register = new MethodDefinition(
+                        Arrays.asList("synchronized"),
+                        childRegistrator.getFullyQualifiedName(), "register",
+                        Arrays.asList(param), Collections.<String> emptyList(),
+                        Collections.<Annotation> emptyList(), body.toString());
+                methods.add(register);
+
+            }
+        }
+
+        // create parent registration
+        String createdName = getJavaNameOfRuntimeRegistration(parent.getJavaNamePrefix());
+
+        List<Field> constructorParameters = Arrays
+                .asList(hierachchicalRegistration);
+        String constructorBody = constructConstructorBody(constructorParameters);
+
+        MethodDefinition constructor = MethodDefinition.createConstructor(
+                createdName, constructorParameters, constructorBody);
+
+        MethodDefinition closeRegistrator = createCloseMethodToCloseField(hierachchicalRegistration);
+        methods.add(closeRegistrator);
+        methods.add(constructor);
+        List<Field> privateFields = Lists.newArrayList(registratorsMapField);
+        privateFields.addAll(constructorParameters);
+
+        RuntimeRegistratorFtlTemplate created = new RuntimeRegistratorFtlTemplate(
+                parent, createdName, privateFields, methods);
+
+        LinkedHashMap<String, RuntimeRegistratorFtlTemplate> result = new LinkedHashMap<>();
+        result.put(created.getTypeDeclaration().getName(), created);
+        checkState(unorderedResult.containsKey(created.getTypeDeclaration()
+                .getName()) == false, "Naming conflict: "
+                + created.getTypeDeclaration().getName());
+        result.putAll(unorderedResult);
+        return result;
+    }
+
+    private static MethodDefinition createCloseMethodToCloseField(Field field) {
+        String body = field.getName() + ".close();";
+        // TODO Thrown exception breaks build
+        // return new MethodDefinition(Collections.<String> emptyList(), "void",
+        // "close", Collections.<Field> emptyList(),
+        // Arrays.asList(IOException.class.getCanonicalName()),
+        // Collections.<Annotation> emptyList(), body);
+        List<Annotation> annotations = Lists.newArrayList(new Annotation(
+                "Override", Collections.<Parameter> emptyList()));
+        return new MethodDefinition(Collections.<String> emptyList(), "void",
+                "close", Collections.<Field> emptyList(),
+                Collections.<String> emptyList(), annotations, body);
+    }
+
+    @VisibleForTesting
+    public static String getJavaNameOfRuntimeRegistration(String javaNamePrefix) {
+        return javaNamePrefix + "RuntimeRegistration";
+    }
+
+    public static String getJavaNameOfRuntimeRegistrator(RuntimeBeanEntry rootRB) {
+        checkArgument(rootRB.isRoot(), "RuntimeBeanEntry must be root");
+        return rootRB.getJavaNamePrefix() + "RuntimeRegistrator";
+    }
+}
diff --git a/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/StubFactoryTemplate.java b/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/StubFactoryTemplate.java
new file mode 100644 (file)
index 0000000..be49f93
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * 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.ftl;
+
+import java.util.Collections;
+
+import org.opendaylight.controller.config.api.DynamicMBeanWithInstance;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Field;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Header;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.MethodDefinition;
+
+import com.google.common.collect.Lists;
+
+public class StubFactoryTemplate extends GeneralClassTemplate {
+
+    private final String moduleInstanceType;
+
+    public StubFactoryTemplate(Header header, String packageName, String name,
+            String extendedClass, String moduleInstanceType) {
+        super(header, packageName, name, Lists.newArrayList(extendedClass),
+                Collections.<String> emptyList(), Collections
+                        .<Field> emptyList(), Collections
+                        .<MethodDefinition> emptyList());
+        this.moduleInstanceType = moduleInstanceType;
+    }
+
+    public String getModuleInstanceType() {
+        return moduleInstanceType;
+    }
+
+    public String getDynamicMBeanWithInstanceType() {
+        return DynamicMBeanWithInstance.class.getCanonicalName();
+    }
+}
diff --git a/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/StubModuleTemplate.java b/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/StubModuleTemplate.java
new file mode 100644 (file)
index 0000000..f49bbba
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * 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.ftl;
+
+import java.util.Collections;
+
+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.spi.Module;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Constructor;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Field;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Header;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.MethodDefinition;
+
+import com.google.common.collect.Lists;
+
+/**
+ *
+ */
+public class StubModuleTemplate extends GeneralClassTemplate {
+
+    private final String extendedClass;
+
+    public StubModuleTemplate(Header header, String packageName,
+            String stubModuleName, String extendedClass) {
+        super(header, packageName, stubModuleName, Lists
+                .newArrayList(extendedClass), Collections.<String> emptyList(),
+                Collections.<Field> emptyList(), Collections
+                        .<MethodDefinition> emptyList(), false, true,
+                Collections.<Constructor> emptyList());
+        this.extendedClass = extendedClass;
+    }
+
+    public String getExtendedClass() {
+        return extendedClass;
+    }
+
+    public String getInstanceType() {
+        return AutoCloseable.class.getCanonicalName();
+    }
+
+    public String getModuleNameType() {
+        return ModuleIdentifier.class.getCanonicalName();
+    }
+
+    public String getAbstractServiceInterfaceType() {
+        return AbstractServiceInterface.class.getCanonicalName();
+    }
+
+    public String getModuleType() {
+        return Module.class.getCanonicalName();
+    }
+
+    public String getDependencyResolverType() {
+        return DependencyResolver.class.getCanonicalName();
+    }
+
+    public String getDynamicMBeanWithInstanceType() {
+        return DynamicMBeanWithInstance.class.getCanonicalName();
+    }
+
+    @Override
+    public String getFtlTempleteLocation() {
+        return "module_stub_template.ftl";
+    }
+}
diff --git a/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/TemplateFactory.java b/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/TemplateFactory.java
new file mode 100644 (file)
index 0000000..641fb25
--- /dev/null
@@ -0,0 +1,675 @@
+/*
+ * 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.ftl;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.opendaylight.controller.config.api.RuntimeBeanRegistratorAwareModule;
+import org.opendaylight.controller.config.api.annotations.AbstractServiceInterface;
+import org.opendaylight.controller.config.api.runtime.RuntimeBean;
+import org.opendaylight.controller.config.spi.Module;
+import org.opendaylight.controller.config.yangjmxgenerator.AbstractEntry;
+import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry;
+import org.opendaylight.controller.config.yangjmxgenerator.RuntimeBeanEntry;
+import org.opendaylight.controller.config.yangjmxgenerator.RuntimeBeanEntry.Rpc;
+import org.opendaylight.controller.config.yangjmxgenerator.ServiceInterfaceEntry;
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc;
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.DependencyAttribute;
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.DependencyAttribute.Dependency;
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.JavaAttribute;
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.ListAttribute;
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.TOAttribute;
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.TypedAttribute;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Annotation;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Annotation.Parameter;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Constructor;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Field;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Header;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.MethodDeclaration;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.MethodDefinition;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.ModuleField;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.util.FullyQualifiedNameHelper;
+import org.opendaylight.yangtools.binding.generator.util.BindingGeneratorUtil;
+import org.opendaylight.yangtools.sal.binding.model.api.Type;
+
+import com.google.common.base.Function;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+
+public class TemplateFactory {
+
+    public static Map<String, FtlTemplate> getFtlTemplates(
+            ModuleMXBeanEntry entry) {
+        Map<String, FtlTemplate> result = new HashMap<>();
+
+        result.putAll(TemplateFactory.tOsFromMbe(entry));
+
+        // IFC
+        result.put(entry.getMXBeanInterfaceName() + ".java",
+                TemplateFactory.mXBeanInterfaceTemplateFromMbe(entry));
+
+        // ABS fact
+        result.put(entry.getAbstractFactoryName() + ".java",
+                TemplateFactory.abstractFactoryTemplateFromMbe(entry));
+
+        // ABS module
+        result.put(entry.getAbstractModuleName() + ".java",
+                TemplateFactory.abstractModuleTemplateFromMbe(entry));
+
+        return result;
+    }
+
+    public static Map<String, FtlTemplate> getFtlStubTemplates(
+            ModuleMXBeanEntry entry) {
+        Map<String, FtlTemplate> result = new HashMap<>();
+        // STUB fact
+        result.put(entry.getStubFactoryName() + ".java",
+                TemplateFactory.stubFactoryTemplateFromMbe(entry));
+
+        result.put(entry.getStubModuleName() + ".java",
+                TemplateFactory.stubModuleTemplateFromMbe(entry));
+        return result;
+    }
+
+    public static Map<String, FtlTemplate> getFtlTemplates(
+            ServiceInterfaceEntry entry) {
+
+        Map<String, FtlTemplate> result = new HashMap<>();
+        result.put(entry.getTypeName() + ".java",
+                TemplateFactory.serviceInterfaceFromSie(entry));
+
+        return result;
+    }
+
+    /**
+     * Get map of file name as key, FtlFile instance representing runtime mx
+     * bean as value that should be persisted from this instance.
+     */
+    public static Map<String, FtlTemplate> getTOAndMXInterfaceFtlFiles(
+            RuntimeBeanEntry entry) {
+        Map<String, FtlTemplate> result = new HashMap<>();
+        { // create GeneralInterfaceFtlFile for runtime MXBean. Attributes will
+          // be transformed to getter methods
+            String mxBeanTypeName = entry.getJavaNameOfRuntimeMXBean();
+            List<String> extendedInterfaces = Arrays.asList(RuntimeBean.class
+                    .getCanonicalName());
+            List<MethodDeclaration> methods = new ArrayList<>();
+            // convert attributes to getters
+            for (AttributeIfc attributeIfc : entry.getAttributes()) {
+                String returnType = null;
+                if (attributeIfc instanceof TypedAttribute) {
+                    returnType = ((TypedAttribute) attributeIfc).getType()
+                            .getFullyQualifiedName();
+                } else if (attributeIfc instanceof TOAttribute) {
+                    String fullyQualifiedName = FullyQualifiedNameHelper
+                            .getFullyQualifiedName(entry.getPackageName(),
+                                    attributeIfc.getUpperCaseCammelCase());
+
+                    returnType = fullyQualifiedName;
+                } else if (attributeIfc instanceof ListAttribute) {
+                    AttributeIfc innerAttr = ((ListAttribute) attributeIfc)
+                            .getInnerAttribute();
+
+                    String innerTpe = innerAttr instanceof TypedAttribute ? ((TypedAttribute) innerAttr)
+                            .getType().getFullyQualifiedName()
+                            : FullyQualifiedNameHelper.getFullyQualifiedName(
+                                    entry.getPackageName(),
+                                    attributeIfc.getUpperCaseCammelCase());
+
+                    returnType = "java.util.List<" + innerTpe + ">";
+                } else {
+                    throw new UnsupportedOperationException(
+                            "Attribute not supported: "
+                                    + attributeIfc.getClass());
+                }
+                String getterName = "get"
+                        + attributeIfc.getUpperCaseCammelCase();
+                MethodDeclaration getter = new MethodDeclaration(returnType,
+                        getterName, Collections.<Field> emptyList());
+                methods.add(getter);
+            }
+            // add rpc methods
+            for (Rpc rpc : entry.getRpcs()) {
+                // convert JavaAttribute parameters into fields
+                List<Field> fields = new ArrayList<>();
+                for (JavaAttribute ja : rpc.getParameters()) {
+                    Field field = new Field(Collections.<String> emptyList(),
+                            ja.getType().getFullyQualifiedName(),
+                            ja.getLowerCaseCammelCase());
+                    fields.add(field);
+                }
+                MethodDeclaration operation = new MethodDeclaration(
+                        rpc.getReturnType(), rpc.getName(), fields);
+                methods.add(operation);
+            }
+
+            // FIXME header
+            GeneralInterfaceTemplate runtimeMxBeanIfc = new GeneralInterfaceTemplate(
+                    null, entry.getPackageName(), mxBeanTypeName,
+                    extendedInterfaces, methods);
+
+            result.put(runtimeMxBeanIfc.getTypeDeclaration().getName()
+                    + ".java", runtimeMxBeanIfc);
+        }
+
+        result.putAll(TemplateFactory.tOsFromRbe(entry));
+
+        return result;
+    }
+
+    public static GeneralInterfaceTemplate serviceInterfaceFromSie(
+            ServiceInterfaceEntry sie) {
+
+        List<String> extendedInterfaces = Lists
+                .newArrayList(AbstractServiceInterface.class.getCanonicalName());
+        if (sie.getBase().isPresent()) {
+            extendedInterfaces.add(sie.getBase().get().getFullyQualifiedName());
+        }
+
+        // FIXME header
+        GeneralInterfaceTemplate sieTemplate = new GeneralInterfaceTemplate(
+                getHeaderFromEntry(sie), sie.getPackageName(),
+                sie.getTypeName(), extendedInterfaces,
+                Lists.<MethodDeclaration> newArrayList());
+        sieTemplate.setJavadoc(sie.getNullableDescription());
+
+        if (sie.getNullableDescription() != null)
+            sieTemplate.getAnnotations().add(
+                    Annotation.createDescriptionAnnotation(sie
+                            .getNullableDescription()));
+        sieTemplate.getAnnotations().add(Annotation.createSieAnnotation(sie.getQName(), sie.getExportedOsgiClassName
+                ()));
+
+        return sieTemplate;
+    }
+
+    public static AbstractFactoryTemplate abstractFactoryTemplateFromMbe(
+            ModuleMXBeanEntry mbe) {
+        AbstractFactoryAttributesProcessor attrProcessor = new AbstractFactoryAttributesProcessor();
+        attrProcessor.processAttributes(mbe.getAttributes(),
+                mbe.getPackageName());
+
+        Collection<String> transformed = Collections2.transform(mbe
+                .getProvidedServices().keySet(),
+                new Function<String, String>() {
+
+                    @Override
+                    public String apply(String input) {
+                        return input + ".class";
+                    }
+                });
+
+        return new AbstractFactoryTemplate(getHeaderFromEntry(mbe),
+                mbe.getPackageName(), mbe.getAbstractFactoryName(),
+                mbe.getGloballyUniqueName(), mbe.getFullyQualifiedName(mbe
+                        .getStubModuleName()), attrProcessor.getFields(),
+                Lists.newArrayList(transformed));
+    }
+
+    public static AbstractModuleTemplate abstractModuleTemplateFromMbe(
+            ModuleMXBeanEntry mbe) {
+        AbstractModuleAttributesProcessor attrProcessor = new AbstractModuleAttributesProcessor();
+        attrProcessor.processAttributes(mbe.getAttributes(),
+                mbe.getPackageName());
+
+        List<ModuleField> moduleFields = attrProcessor.getModuleFields();
+        List<String> implementedIfcs = Lists.newArrayList(
+                Module.class.getCanonicalName(),
+                mbe.getFullyQualifiedName(mbe.getMXBeanInterfaceName()));
+
+        for (String implementedService : mbe.getProvidedServices().keySet()) {
+            implementedIfcs.add(implementedService);
+        }
+
+        boolean generateRuntime = false;
+        String registratorFullyQualifiedName = null;
+        if (mbe.getRuntimeBeans() != null
+                && mbe.getRuntimeBeans().isEmpty() == false) {
+            generateRuntime = true;
+            RuntimeBeanEntry rootEntry = RuntimeRegistratorFtlTemplate
+                    .findRoot(mbe.getRuntimeBeans());
+            registratorFullyQualifiedName = rootEntry
+                    .getPackageName()
+                    .concat(".")
+                    .concat(RuntimeRegistratorFtlTemplate.getJavaNameOfRuntimeRegistrator(rootEntry));
+            implementedIfcs.add(RuntimeBeanRegistratorAwareModule.class
+                    .getCanonicalName());
+        }
+
+        AbstractModuleTemplate abstractModuleTemplate = new AbstractModuleTemplate(
+                getHeaderFromEntry(mbe), mbe.getPackageName(),
+                mbe.getAbstractModuleName(), implementedIfcs, moduleFields,
+                attrProcessor.getMethods(), generateRuntime,
+                registratorFullyQualifiedName);
+
+        if (mbe.getNullableDescription() != null)
+            abstractModuleTemplate.getAnnotations().add(
+                    Annotation.createDescriptionAnnotation(mbe
+                            .getNullableDescription()));
+        return abstractModuleTemplate;
+    }
+
+    public static StubFactoryTemplate stubFactoryTemplateFromMbe(
+            ModuleMXBeanEntry mbe) {
+        return new StubFactoryTemplate(getHeaderFromEntry(mbe),
+                mbe.getPackageName(), mbe.getStubFactoryName(),
+                mbe.getFullyQualifiedName(mbe.getAbstractFactoryName()),
+                mbe.getStubModuleName());
+    }
+
+    public static StubModuleTemplate stubModuleTemplateFromMbe(
+            ModuleMXBeanEntry mbe) {
+        return new StubModuleTemplate(getHeaderFromEntry(mbe),
+                mbe.getPackageName(), mbe.getStubModuleName(),
+                mbe.getFullyQualifiedName(mbe.getAbstractModuleName()));
+    }
+
+    public static GeneralInterfaceTemplate mXBeanInterfaceTemplateFromMbe(
+            ModuleMXBeanEntry mbe) {
+        MXBeanInterfaceAttributesProcessor attrProcessor = new MXBeanInterfaceAttributesProcessor();
+        attrProcessor.processAttributes(mbe.getAttributes(),
+                mbe.getPackageName());
+        GeneralInterfaceTemplate ifcTemplate = new GeneralInterfaceTemplate(
+                getHeaderFromEntry(mbe), mbe.getPackageName(),
+                mbe.getMXBeanInterfaceName(), Lists.<String> newArrayList(),
+                attrProcessor.getMethods());
+        ifcTemplate.setJavadoc(mbe.getNullableDescription());
+        return ifcTemplate;
+    }
+
+    public static Map<String, GeneralClassTemplate> tOsFromMbe(
+            ModuleMXBeanEntry mbe) {
+        Map<String, GeneralClassTemplate> retVal = Maps.newHashMap();
+        TOAttributesProcessor processor = new TOAttributesProcessor();
+        processor.processAttributes(mbe.getAttributes(), mbe.getPackageName());
+        for (org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.TemplateFactory.TOAttributesProcessor.TOInternal to : processor
+                .getTOs()) {
+            List<Constructor> constructors = Lists.newArrayList();
+            constructors.add(new Constructor(to.getName(), "super();"));
+
+            Header header = getHeaderFromEntry(mbe);
+            retVal.put(
+                    to.getType(),
+                    new GeneralClassTemplate(header, mbe.getPackageName(), to
+                            .getName(), Collections.<String> emptyList(),
+                            Collections.<String> emptyList(), to.getFields(),
+                            to.getMethods(), false, false, constructors));
+        }
+        return retVal;
+    }
+
+    public static Map<String, GeneralClassTemplate> tOsFromRbe(
+            RuntimeBeanEntry rbe) {
+        Map<String, GeneralClassTemplate> retVal = Maps.newHashMap();
+        TOAttributesProcessor processor = new TOAttributesProcessor();
+        processor.processAttributes(rbe.getYangPropertiesToTypesMap(),
+                rbe.getPackageName());
+        for (org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.TemplateFactory.TOAttributesProcessor.TOInternal to : processor
+                .getTOs()) {
+            List<Constructor> constructors = Lists.newArrayList();
+            constructors.add(new Constructor(to.getName(), "super();"));
+
+            // TODO header
+            retVal.put(
+                    to.getType(),
+                    new GeneralClassTemplate(null, rbe.getPackageName(), to
+                            .getName(), Collections.<String> emptyList(),
+                            Collections.<String> emptyList(), to.getFields(),
+                            to.getMethods(), false, false, constructors));
+        }
+        return retVal;
+    }
+
+    private static Header getHeaderFromEntry(AbstractEntry mbe) {
+        return new Header(mbe.getYangModuleName(), mbe.getYangModuleLocalname());
+    }
+
+    // TODO refactor attribute processors
+
+    private static class TOAttributesProcessor {
+
+        private final List<TOInternal> tos = Lists.newArrayList();
+
+        void processAttributes(Map<String, AttributeIfc> attributes,
+                String packageName) {
+            for (Entry<String, AttributeIfc> attrEntry : attributes.entrySet()) {
+                AttributeIfc attributeIfc = attrEntry.getValue();
+                if (attributeIfc instanceof TOAttribute) {
+                    createTOInternal(packageName, attributeIfc);
+                }
+                if (attributeIfc instanceof ListAttribute) {
+                    AttributeIfc innerAttr = ((ListAttribute) attributeIfc)
+                            .getInnerAttribute();
+                    if (innerAttr instanceof TOAttribute) {
+                        createTOInternal(packageName, innerAttr);
+                    }
+                }
+            }
+        }
+
+        private void createTOInternal(String packageName,
+                AttributeIfc attributeIfc) {
+            String fullyQualifiedName = FullyQualifiedNameHelper
+                    .getFullyQualifiedName(packageName, attributeIfc.getUpperCaseCammelCase());
+
+            String type = fullyQualifiedName;
+            String name = attributeIfc.getUpperCaseCammelCase();
+            Map<String, AttributeIfc> attrs = ((TOAttribute) attributeIfc)
+                    .getCapitalizedPropertiesToTypesMap();
+            // recursive processing
+            processAttributes(attrs, packageName);
+
+            tos.add(new TOInternal(type, name, attrs, packageName));
+        }
+
+        List<TOInternal> getTOs() {
+            return tos;
+        }
+
+        private static class TOInternal {
+            private final String type, name;
+            private List<Field> fields;
+            private List<MethodDefinition> methods;
+
+            public TOInternal(String type, String name,
+                    Map<String, AttributeIfc> attrs, String packageName) {
+                super();
+                this.type = type;
+                this.name = name;
+                processAttrs(attrs, packageName);
+            }
+
+            private void processAttrs(Map<String, AttributeIfc> attrs,
+                    String packageName) {
+                fields = Lists.newArrayList();
+                methods = Lists.newArrayList();
+
+                for (Entry<String, AttributeIfc> attrEntry : attrs.entrySet()) {
+                    String innerName = attrEntry.getKey();
+                    String varName = BindingGeneratorUtil
+                            .parseToValidParamName(attrEntry.getKey());
+
+                    String fullyQualifiedName = null;
+                    if (attrEntry.getValue() instanceof TypedAttribute) {
+                        Type innerType = ((TypedAttribute) attrEntry.getValue())
+                                .getType();
+                        fullyQualifiedName = innerType.getFullyQualifiedName();
+                    } else if (attrEntry.getValue() instanceof ListAttribute) {
+                        AttributeIfc innerAttr = ((ListAttribute) attrEntry
+                                .getValue()).getInnerAttribute();
+
+                        String innerTpe = innerAttr instanceof TypedAttribute ? ((TypedAttribute) innerAttr)
+                                .getType().getFullyQualifiedName()
+                                : FullyQualifiedNameHelper
+                                        .getFullyQualifiedName(packageName, attrEntry.getValue().getUpperCaseCammelCase());
+
+                        fullyQualifiedName = "java.util.List<" + innerTpe + ">";
+                    } else
+                        fullyQualifiedName = FullyQualifiedNameHelper
+                                .getFullyQualifiedName(packageName, attrEntry.getValue().getUpperCaseCammelCase());
+
+                    fields.add(new Field(fullyQualifiedName, varName));
+
+                    String getterName = "get" + innerName;
+                    MethodDefinition getter = new MethodDefinition(
+                            fullyQualifiedName, getterName,
+                            Collections.<Field> emptyList(), "return "
+                                    + varName + ";");
+
+                    String setterName = "set" + innerName;
+                    MethodDefinition setter = new MethodDefinition("void",
+                            setterName, Lists.newArrayList(new Field(
+                                    fullyQualifiedName, varName)), "this."
+                                    + varName + " = " + varName + ";");
+                    methods.add(getter);
+                    methods.add(setter);
+                }
+
+            }
+
+            String getType() {
+                return type;
+            }
+
+            String getName() {
+                return name;
+            }
+
+            List<Field> getFields() {
+                return fields;
+            }
+
+            List<MethodDefinition> getMethods() {
+                return methods;
+            }
+        }
+    }
+
+    private static class MXBeanInterfaceAttributesProcessor {
+        private static final String STRING_FULLY_QUALIFIED_NAME = "java.util.List";
+        private final List<MethodDeclaration> methods = Lists.newArrayList();
+
+        void processAttributes(Map<String, AttributeIfc> attributes,
+                String packageName) {
+            for (Entry<String, AttributeIfc> attrEntry : attributes.entrySet()) {
+                String returnType;
+                AttributeIfc attributeIfc = attrEntry.getValue();
+
+                if (attributeIfc instanceof TypedAttribute) {
+                    returnType = ((TypedAttribute) attributeIfc).getType()
+                            .getFullyQualifiedName();
+                } else if (attributeIfc instanceof TOAttribute) {
+                    String fullyQualifiedName = FullyQualifiedNameHelper
+                            .getFullyQualifiedName(packageName, attributeIfc.getUpperCaseCammelCase());
+
+                    returnType = fullyQualifiedName;
+                } else if (attributeIfc instanceof ListAttribute) {
+                    String fullyQualifiedName = null;
+
+                    AttributeIfc innerAttr = ((ListAttribute) attributeIfc)
+                            .getInnerAttribute();
+                    if (innerAttr instanceof JavaAttribute) {
+                        fullyQualifiedName = ((JavaAttribute) innerAttr)
+                                .getType().getFullyQualifiedName();
+                    } else if (innerAttr instanceof TOAttribute) {
+                        fullyQualifiedName = FullyQualifiedNameHelper
+                                .getFullyQualifiedName(packageName, innerAttr.getUpperCaseCammelCase());
+                    }
+
+                    returnType = STRING_FULLY_QUALIFIED_NAME.concat("<")
+                            .concat(fullyQualifiedName).concat(">");
+                } else {
+                    throw new UnsupportedOperationException(
+                            "Attribute not supported: "
+                                    + attributeIfc.getClass());
+                }
+
+                String getterName = "get"
+                        + attributeIfc.getUpperCaseCammelCase();
+                MethodDeclaration getter = new MethodDeclaration(returnType,
+                        getterName, Collections.<Field> emptyList());
+
+                String varName = BindingGeneratorUtil
+                        .parseToValidParamName(attrEntry.getKey());
+                String setterName = "set"
+                        + attributeIfc.getUpperCaseCammelCase();
+                MethodDeclaration setter = new MethodDeclaration("void",
+                        setterName, Lists.newArrayList(new Field(returnType,
+                                varName)));
+                methods.add(getter);
+                methods.add(setter);
+
+                if (attributeIfc.getNullableDescription() != null) {
+                    setter.setJavadoc(attrEntry.getValue()
+                            .getNullableDescription());
+                }
+            }
+        }
+
+        List<MethodDeclaration> getMethods() {
+            return methods;
+        }
+    }
+
+    private static class AbstractFactoryAttributesProcessor {
+
+        private final List<Field> fields = Lists.newArrayList();
+        private static final String STRING_FULLY_QUALIFIED_NAME = "java.util.List";
+
+        void processAttributes(Map<String, AttributeIfc> attributes,
+                String packageName) {
+            for (Entry<String, AttributeIfc> attrEntry : attributes.entrySet()) {
+                String type;
+                AttributeIfc attributeIfc = attrEntry.getValue();
+
+                if (attributeIfc instanceof TypedAttribute) {
+                    type = ((TypedAttribute) attributeIfc).getType()
+                            .getFullyQualifiedName();
+                } else if (attributeIfc instanceof TOAttribute) {
+                    String fullyQualifiedName = FullyQualifiedNameHelper
+                            .getFullyQualifiedName(packageName, attributeIfc.getUpperCaseCammelCase());
+
+                    type = fullyQualifiedName;
+                } else if (attributeIfc instanceof ListAttribute) {
+                    String fullyQualifiedName = null;
+                    AttributeIfc innerAttr = ((ListAttribute) attributeIfc)
+                            .getInnerAttribute();
+                    if (innerAttr instanceof JavaAttribute) {
+                        fullyQualifiedName = ((JavaAttribute) innerAttr)
+                                .getType().getFullyQualifiedName();
+                    } else if (innerAttr instanceof TOAttribute) {
+                        fullyQualifiedName = FullyQualifiedNameHelper
+                                .getFullyQualifiedName(packageName, innerAttr.getUpperCaseCammelCase());
+                    }
+
+                    type = STRING_FULLY_QUALIFIED_NAME.concat("<")
+                            .concat(fullyQualifiedName).concat(">");
+
+                } else {
+                    throw new UnsupportedOperationException(
+                            "Attribute not supported: "
+                                    + attributeIfc.getClass());
+                }
+
+                fields.add(new Field(type, attributeIfc
+                        .getUpperCaseCammelCase()));
+            }
+        }
+
+        List<Field> getFields() {
+            return fields;
+        }
+    }
+
+    private static class AbstractModuleAttributesProcessor {
+
+        private static final String STRING_FULLY_QUALIFIED_NAME = "java.util.List";
+
+        private final List<ModuleField> moduleFields = Lists.newArrayList();
+        private final List<MethodDefinition> methods = Lists.newArrayList();
+
+        void processAttributes(Map<String, AttributeIfc> attributes,
+                String packageName) {
+            for (Entry<String, AttributeIfc> attrEntry : attributes.entrySet()) {
+                String type;
+                AttributeIfc attributeIfc = attrEntry.getValue();
+
+                if (attributeIfc instanceof TypedAttribute) {
+                    type = ((TypedAttribute) attributeIfc).getType()
+                            .getFullyQualifiedName();
+                } else if (attributeIfc instanceof TOAttribute) {
+                    String fullyQualifiedName = FullyQualifiedNameHelper
+                            .getFullyQualifiedName(packageName, attributeIfc.getUpperCaseCammelCase());
+
+                    type = fullyQualifiedName;
+                } else if (attributeIfc instanceof ListAttribute) {
+                    String fullyQualifiedName = null;
+                    AttributeIfc innerAttr = ((ListAttribute) attributeIfc)
+                            .getInnerAttribute();
+                    if (innerAttr instanceof JavaAttribute) {
+                        fullyQualifiedName = ((JavaAttribute) innerAttr)
+                                .getType().getFullyQualifiedName();
+                    } else if (innerAttr instanceof TOAttribute) {
+                        fullyQualifiedName = FullyQualifiedNameHelper
+                                .getFullyQualifiedName(packageName, innerAttr.getUpperCaseCammelCase());
+                    }
+
+                    type = STRING_FULLY_QUALIFIED_NAME.concat("<")
+                            .concat(fullyQualifiedName).concat(">");
+                } else {
+                    throw new UnsupportedOperationException(
+                            "Attribute not supported: "
+                                    + attributeIfc.getClass());
+                }
+
+                boolean isDependency = false;
+                Dependency dependency = null;
+                Annotation overrideAnnotation = new Annotation("Override",
+                        Collections.<Parameter> emptyList());
+                List<Annotation> annotations = Lists
+                        .newArrayList(overrideAnnotation);
+
+                if (attributeIfc instanceof DependencyAttribute) {
+                    isDependency = true;
+                    dependency = ((DependencyAttribute) attributeIfc)
+                            .getDependency();
+                    annotations.add(Annotation
+                            .createRequireIfcAnnotation(dependency.getSie()));
+                }
+
+                String varName = BindingGeneratorUtil
+                        .parseToValidParamName(attrEntry.getKey());
+                moduleFields.add(new ModuleField(type, varName, attributeIfc
+                        .getUpperCaseCammelCase(), attributeIfc
+                        .getNullableDefault(), isDependency, dependency));
+
+                String getterName = "get"
+                        + attributeIfc.getUpperCaseCammelCase();
+                MethodDefinition getter = new MethodDefinition(type,
+                        getterName, Collections.<Field> emptyList(),
+                        Lists.newArrayList(overrideAnnotation), "return "
+                                + varName + ";");
+
+                String setterName = "set"
+                        + attributeIfc.getUpperCaseCammelCase();
+
+                if (attributeIfc.getNullableDescription() != null) {
+                    annotations.add(Annotation
+                            .createDescriptionAnnotation(attributeIfc.getNullableDescription()));
+                }
+
+                MethodDefinition setter = new MethodDefinition("void",
+                        setterName,
+                        Lists.newArrayList(new Field(type, varName)),
+                        annotations, "this." + varName + " = " + varName + ";");
+                setter.setJavadoc(attributeIfc.getNullableDescription());
+
+                methods.add(getter);
+                methods.add(setter);
+            }
+        }
+
+        List<ModuleField> getModuleFields() {
+            return moduleFields;
+        }
+
+        List<MethodDefinition> getMethods() {
+            return methods;
+        }
+
+    }
+
+}
diff --git a/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/TypeHelper.java b/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/TypeHelper.java
new file mode 100644 (file)
index 0000000..e097516
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * 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.ftl;
+
+class TypeHelper {
+
+    /**
+     * Output string representing java notation of generic class, e.g.
+     * "List<String>" for input parameters List.class, String.class
+     */
+    static String getGenericType(Class<?> type, Class<?>... parameters) {
+        StringBuffer sb = new StringBuffer();
+        sb.append(type.getCanonicalName());
+        if (parameters.length > 0) {
+            sb.append("<");
+            boolean first = true;
+            for (Class<?> parameter : parameters) {
+                if (first) {
+                    first = false;
+                } else {
+                    sb.append(",");
+                }
+                sb.append(parameter.getCanonicalName());
+            }
+            sb.append(">");
+        }
+        return sb.toString();
+    }
+}
diff --git a/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/directives/AnnotationsDirective.java b/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/directives/AnnotationsDirective.java
new file mode 100644 (file)
index 0000000..4a34a1f
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * 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.ftl.directives;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.List;
+import java.util.Map;
+
+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.Annotation.Parameter;
+
+import com.google.common.collect.Lists;
+
+import freemarker.core.Environment;
+import freemarker.template.SimpleSequence;
+import freemarker.template.TemplateDirectiveBody;
+import freemarker.template.TemplateDirectiveModel;
+import freemarker.template.TemplateException;
+import freemarker.template.TemplateModel;
+
+/**
+ * Add annotations to freemarker template.
+ */
+public class AnnotationsDirective implements TemplateDirectiveModel {
+
+    private static final String OBJECT = "object";
+
+    @Override
+    public void execute(Environment env, Map params, TemplateModel[] loopVars,
+            TemplateDirectiveBody body) throws TemplateException, IOException {
+        Object object = params.get(OBJECT);
+        List<Annotation> annotations = Lists.newArrayList();
+
+        if (object != null) {
+            if (object instanceof SimpleSequence)
+                annotations = ((SimpleSequence) object).toList();
+            else if (object instanceof FtlTemplate) {
+                annotations = ((FtlTemplate) object).getAnnotations();
+            } else
+                throw new IllegalArgumentException(
+                        "Object must be a SimpleSequence or instance of "
+                                + FtlTemplate.class + "but was "
+                                + object.getClass());
+        }
+
+        Writer out = env.getOut();
+        StringBuilder build = new StringBuilder();
+        writeAnnotations(annotations, build, "");
+
+        if (!annotations.isEmpty())
+            out.write(build.toString().toCharArray());
+    }
+
+    static void writeAnnotations(List<Annotation> annotations,
+            StringBuilder build, String linePrefix) {
+        for (Annotation annotation : annotations) {
+            build.append(linePrefix + "@");
+            build.append(annotation.getName());
+            if (!annotation.getParams().isEmpty()) {
+                build.append("(");
+                for (Parameter param : annotation.getParams()) {
+                    build.append(param.getKey());
+                    build.append(" = ");
+                    build.append(fixString(param.getValue()));
+                    build.append(", ");
+                }
+                build.setCharAt(build.length() - 2, ')');
+            }
+            build.append(System.lineSeparator());
+        }
+    }
+
+    private static String fixString(String value) {
+        // TODO replace with compress single line if possible
+        return value.replaceAll("\\r\\n|\\r|\\n", " ").replaceAll(" +", " ");
+    }
+
+}
diff --git a/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/directives/ConstructorsDirective.java b/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/directives/ConstructorsDirective.java
new file mode 100644 (file)
index 0000000..56b7286
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * 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.ftl.directives;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.List;
+import java.util.Map;
+
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.GeneralClassTemplate;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Constructor;
+
+import com.google.common.collect.Lists;
+
+import freemarker.core.Environment;
+import freemarker.template.SimpleSequence;
+import freemarker.template.TemplateDirectiveBody;
+import freemarker.template.TemplateDirectiveModel;
+import freemarker.template.TemplateException;
+import freemarker.template.TemplateModel;
+
+/**
+ * Add annotations to freemarker template.
+ */
+public class ConstructorsDirective implements TemplateDirectiveModel {
+
+    private static final String OBJECT = "object";
+
+    @Override
+    public void execute(Environment env, Map params, TemplateModel[] loopVars,
+            TemplateDirectiveBody body) throws TemplateException, IOException {
+        Object object = params.get(OBJECT);
+        List<Constructor> constructors = Lists.newArrayList();
+
+        if (object != null) {
+            if (object instanceof SimpleSequence)
+                constructors = ((SimpleSequence) object).toList();
+            else if (object instanceof GeneralClassTemplate) {
+                constructors = ((GeneralClassTemplate) object)
+                        .getConstructors();
+            } else
+                throw new IllegalArgumentException(
+                        "Object must be a SimpleSequence or instance of "
+                                + GeneralClassTemplate.class + "but was "
+                                + object.getClass());
+        }
+
+        Writer out = env.getOut();
+        StringBuilder build = new StringBuilder();
+        for (Constructor constr : constructors) {
+            build.append("    ");
+            if (constr.isPublic())
+                build.append("public ");
+            build.append(constr.getTypeName() + " ");
+            build.append("() {");
+            build.append(System.lineSeparator());
+            build.append("    ");
+            build.append("    ");
+            build.append(constr.getBody());
+            build.append(System.lineSeparator());
+            build.append("    ");
+            build.append("}");
+            build.append(System.lineSeparator());
+            build.append(System.lineSeparator());
+        }
+
+        if (!constructors.isEmpty())
+            out.write(build.toString().toCharArray());
+    }
+
+}
diff --git a/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/directives/FieldsDirectiveProg.java b/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/directives/FieldsDirectiveProg.java
new file mode 100644 (file)
index 0000000..7dd0f8e
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * 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.ftl.directives;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.List;
+import java.util.Map;
+
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.FtlTemplate;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Field;
+
+import com.google.common.collect.Lists;
+
+import freemarker.core.Environment;
+import freemarker.template.SimpleSequence;
+import freemarker.template.TemplateDirectiveBody;
+import freemarker.template.TemplateDirectiveModel;
+import freemarker.template.TemplateException;
+import freemarker.template.TemplateModel;
+
+/**
+ * Add fields to freemarker template.
+ */
+public class FieldsDirectiveProg implements TemplateDirectiveModel {
+
+    private static final String OBJECT = "object";
+
+    @Override
+    public void execute(Environment env, Map params, TemplateModel[] loopVars,
+            TemplateDirectiveBody body) throws TemplateException, IOException {
+        Object object = params.get(OBJECT);
+        List<Field> fields = Lists.newArrayList();
+
+        if (object != null) {
+            if (object instanceof SimpleSequence)
+                fields = ((SimpleSequence) object).toList();
+            else if (object instanceof FtlTemplate) {
+                fields = ((FtlTemplate) object).getFields();
+            } else
+                throw new IllegalArgumentException(
+                        "Object must be a SimpleSequence or instance of "
+                                + FtlTemplate.class + "but was "
+                                + object.getClass());
+        }
+
+        Writer out = env.getOut();
+        StringBuilder build = new StringBuilder();
+        for (Field field : fields) {
+            build.append("     private ");
+            for (String mod : field.getModifiers()) {
+                build.append(mod + " ");
+            }
+            build.append(field.getType() + " ");
+            build.append(field.getName());
+            if (field.getDefinition() != null)
+                build.append(" = " + field.getDefinition());
+            build.append(";");
+            build.append(System.lineSeparator());
+        }
+
+        if (!fields.isEmpty())
+            out.write(build.toString().toCharArray());
+    }
+
+    // String templateStr = "Hello ${user}";
+    // Template t = new Template("name", new StringReader(templateStr), new
+    // Configuration());
+}
diff --git a/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/directives/FieldsDirectiveTemplate.java b/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/directives/FieldsDirectiveTemplate.java
new file mode 100644 (file)
index 0000000..268d898
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * 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.ftl.directives;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.io.Writer;
+import java.util.Map;
+
+import com.google.common.collect.Maps;
+
+import freemarker.core.Environment;
+import freemarker.template.Configuration;
+import freemarker.template.Template;
+import freemarker.template.TemplateDirectiveBody;
+import freemarker.template.TemplateDirectiveModel;
+import freemarker.template.TemplateException;
+import freemarker.template.TemplateModel;
+
+/**
+ * Add fields to freemarker template.
+ */
+public class FieldsDirectiveTemplate implements TemplateDirectiveModel {
+
+    private static final String OBJECT = "object";
+
+    @Override
+    public void execute(Environment env, Map params, TemplateModel[] loopVars,
+            TemplateDirectiveBody body) throws TemplateException, IOException {
+        Object object = params.get(OBJECT);
+
+        // TODO check type
+
+        String templateStr = "    <#list fields as field>"
+                + "private <#if field.final==true>final </#if> <#if field.static==true>static </#if>"
+                + "${field.type} ${field.name}<#if field.definition??> = ${field.definition}</#if>;"
+                + System.lineSeparator() + " </#list>";
+        Template t = new Template("name", new StringReader(templateStr),
+                new Configuration());
+
+        try {
+            Map<String, Object> map = Maps.newHashMap();
+            map.put("fields", object);
+            Writer out = env.getOut();
+            t.process(map, out);
+        } catch (TemplateException e) {
+            throw new IllegalStateException(
+                    "Template error while generating fields" + e);
+        }
+    }
+
+}
diff --git a/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/directives/HeaderDirective.java b/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/directives/HeaderDirective.java
new file mode 100644 (file)
index 0000000..93fde59
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * 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.ftl.directives;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.Date;
+import java.util.Map;
+
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Header;
+
+import com.google.common.base.Preconditions;
+
+import freemarker.core.Environment;
+import freemarker.ext.beans.StringModel;
+import freemarker.template.TemplateDirectiveBody;
+import freemarker.template.TemplateDirectiveModel;
+import freemarker.template.TemplateException;
+import freemarker.template.TemplateModel;
+
+/**
+ * Add annotations to freemarker template.
+ */
+public class HeaderDirective implements TemplateDirectiveModel {
+
+    private static final String GENERATOR_CLASS = JMXGenerator.class
+            .getCanonicalName();
+    private static final String OBJECT = "header";
+
+    @Override
+    public void execute(Environment env, Map params, TemplateModel[] loopVars,
+            TemplateDirectiveBody body) throws TemplateException, IOException {
+
+        // FIXME do not allow null header
+        // Preconditions.checkNotNull(object, "Null type declaration");
+        Object object = params.get(OBJECT);
+        Header header = null;
+        if (object != null) {
+            object = ((StringModel) object).getWrappedObject();
+            Preconditions.checkArgument(object instanceof Header,
+                    "Template header should be instance of " + Header.class
+                            + " but was " + object.getClass());
+
+            header = (Header) object;
+        }
+
+        Writer out = env.getOut();
+        StringBuilder build = new StringBuilder();
+        build.append("/**");
+        build.append(System.lineSeparator());
+        build.append("* ");
+        build.append("Generated file");
+        build.append(System.lineSeparator());
+        build.append(System.lineSeparator());
+        build.append("* ");
+        build.append("Generated from: ");
+        build.append(header != null ? header.toString() : "");
+        build.append(System.lineSeparator());
+        build.append("* ");
+        build.append("Generated by: " + GENERATOR_CLASS);
+        build.append(System.lineSeparator());
+        build.append("* ");
+        build.append("Generated at: " + new Date());
+        build.append(System.lineSeparator());
+        build.append("* ");
+        build.append(System.lineSeparator());
+        build.append("* ");
+        build.append("Do not modify this file unless it is present under src/main directory ");
+        build.append(System.lineSeparator());
+        build.append("*/");
+        build.append(System.lineSeparator());
+
+        out.write(build.toString().toCharArray());
+    }
+}
diff --git a/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/directives/JavadocDirective.java b/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/directives/JavadocDirective.java
new file mode 100644 (file)
index 0000000..9817be3
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * 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.ftl.directives;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.Map;
+
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.FtlTemplate;
+
+import freemarker.core.Environment;
+import freemarker.template.SimpleScalar;
+import freemarker.template.TemplateDirectiveBody;
+import freemarker.template.TemplateDirectiveModel;
+import freemarker.template.TemplateException;
+import freemarker.template.TemplateModel;
+
+/**
+ * Add javadoc to freemarker template as String.
+ */
+public class JavadocDirective implements TemplateDirectiveModel {
+
+    private static final String OBJECT = "object";
+
+    @Override
+    public void execute(Environment env, Map params, TemplateModel[] loopVars,
+            TemplateDirectiveBody body) throws TemplateException, IOException {
+        Object object = params.get(OBJECT);
+        String javadoc = "";
+
+        if (object != null) {
+            if (object instanceof SimpleScalar)
+                javadoc = ((SimpleScalar) object).getAsString();
+            else if (object instanceof FtlTemplate) {
+                javadoc = ((FtlTemplate) object).getJavadoc();
+            } else
+                throw new IllegalArgumentException(
+                        "Object must be a String or instance of "
+                                + FtlTemplate.class + "but was "
+                                + object.getClass());
+        }
+
+        Writer out = env.getOut();
+        StringBuilder build = new StringBuilder();
+        writeJavadoc(build, javadoc, "");
+        out.write(build.toString().toCharArray());
+    }
+
+    static void writeJavadoc(StringBuilder build, String javadoc,
+            String linePrefix) {
+        build.append(linePrefix + "/**");
+        build.append(System.lineSeparator());
+        build.append(linePrefix + "* ");
+        build.append(javadoc == null ? "" : javadoc);
+        build.append(System.lineSeparator());
+        build.append(linePrefix + "*/");
+        build.append(System.lineSeparator());
+    }
+
+}
diff --git a/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/directives/MethodsDirective.java b/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/directives/MethodsDirective.java
new file mode 100644 (file)
index 0000000..975d8fd
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * 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.ftl.directives;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.List;
+import java.util.Map;
+
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.FtlTemplate;
+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.ftl.model.MethodDeclaration;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.MethodDefinition;
+
+import com.google.common.collect.Lists;
+
+import freemarker.core.Environment;
+import freemarker.template.SimpleSequence;
+import freemarker.template.TemplateDirectiveBody;
+import freemarker.template.TemplateDirectiveModel;
+import freemarker.template.TemplateException;
+import freemarker.template.TemplateModel;
+
+/**
+ * Add annotations to freemarker template.
+ */
+public class MethodsDirective implements TemplateDirectiveModel {
+
+    private static final String OBJECT = "object";
+
+    @Override
+    public void execute(Environment env, Map params, TemplateModel[] loopVars,
+            TemplateDirectiveBody body) throws TemplateException, IOException {
+        Object object = params.get(OBJECT);
+        List<? extends Method> methods = Lists.newArrayList();
+
+        if (object != null) {
+            if (object instanceof SimpleSequence)
+                methods = ((SimpleSequence) object).toList();
+            else if (object instanceof FtlTemplate) {
+                methods = ((FtlTemplate) object).getMethods();
+            } else
+                throw new IllegalArgumentException(
+                        "Object must be a SimpleSequence or instance of "
+                                + FtlTemplate.class + "but was "
+                                + object.getClass());
+        }
+
+        Writer out = env.getOut();
+        StringBuilder build = new StringBuilder();
+        for (Method method : methods) {
+            if (method.getJavadoc() != null)
+                JavadocDirective.writeJavadoc(build, method.getJavadoc(), "    ");
+
+            if (!method.getAnnotations().isEmpty()) {
+                AnnotationsDirective.writeAnnotations(method.getAnnotations(),
+                        build, "    ");
+            }
+
+            build.append("    " + "public ");
+            for (String mod : method.getModifiers()) {
+                build.append(mod + " ");
+            }
+            build.append(method.getReturnType() + " ");
+
+            build.append(method.getName() + "(");
+            for (Field param : method.getParameters()) {
+                for (String mod : param.getModifiers()) {
+                    build.append(mod + " ");
+                }
+                build.append(param.getType() + " ");
+                build.append(param.getName() + ", ");
+            }
+            if (method.getParameters().isEmpty())
+                build.append(")");
+            else {
+                build.deleteCharAt(build.length() - 1);
+                build.deleteCharAt(build.length() - 1);
+                build.append(')');
+            }
+
+            if (method instanceof MethodDeclaration) {
+                build.append(";");
+                build.append(System.lineSeparator());
+            } else if (method instanceof MethodDefinition) {
+                if (!((MethodDefinition) method).getThrowsExceptions()
+                        .isEmpty())
+                    build.append(" throws ");
+                for (String ex : ((MethodDefinition) method)
+                        .getThrowsExceptions()) {
+                    build.append(ex + " ");
+                }
+                build.append(" {");
+                build.append(System.lineSeparator());
+                build.append("        ");
+                build.append(((MethodDefinition) method).getBody());
+                build.append(System.lineSeparator());
+                build.append("    ");
+                build.append("}");
+                build.append(System.lineSeparator());
+            }
+            build.append(System.lineSeparator());
+
+        }
+
+        if (!methods.isEmpty())
+            out.write(build.toString().toCharArray());
+    }
+}
diff --git a/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/directives/ModuleFieldsDirective.java b/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/directives/ModuleFieldsDirective.java
new file mode 100644 (file)
index 0000000..144419e
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * 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.ftl.directives;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.List;
+import java.util.Map;
+
+import org.opendaylight.controller.config.api.JmxAttribute;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.AbstractModuleTemplate;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.ModuleField;
+
+import com.google.common.collect.Lists;
+
+import freemarker.core.Environment;
+import freemarker.template.SimpleSequence;
+import freemarker.template.TemplateDirectiveBody;
+import freemarker.template.TemplateDirectiveModel;
+import freemarker.template.TemplateException;
+import freemarker.template.TemplateModel;
+
+/**
+ * Add annotations to freemarker template.
+ */
+public class ModuleFieldsDirective implements TemplateDirectiveModel {
+
+    private static final String OBJECT = "moduleFields";
+
+    @Override
+    public void execute(Environment env, Map params, TemplateModel[] loopVars,
+            TemplateDirectiveBody body) throws TemplateException, IOException {
+        Object object = params.get(OBJECT);
+        List<ModuleField> fields = Lists.newArrayList();
+
+        if (object != null) {
+            if (object instanceof SimpleSequence)
+                fields = ((SimpleSequence) object).toList();
+            else if (object instanceof AbstractModuleTemplate) {
+                fields = ((AbstractModuleTemplate) object).getModuleFields();
+            } else
+                throw new IllegalArgumentException(
+                        "Object must be a SimpleSequence or instance of "
+                                + AbstractModuleTemplate.class + "but was "
+                                + object.getClass());
+        }
+
+        Writer out = env.getOut();
+        StringBuilder build = new StringBuilder();
+        for (ModuleField field : fields) {
+            build.append("    ");
+            build.append("protected final "
+                    + JmxAttribute.class.getCanonicalName() + " "
+                    + field.getName() + "JmxAttribute = new "
+                    + JmxAttribute.class.getCanonicalName() + "(\""
+                    + field.getAttributeName() + "\");");
+            build.append(System.lineSeparator());
+
+            build.append("     private ");
+            for (String mod : field.getModifiers()) {
+                build.append(mod + " ");
+            }
+            build.append(field.getType() + " ");
+            build.append(field.getName());
+            if (field.getNullableDefault() != null)
+                build.append(" = " + field.getNullableDefault());
+            build.append(";");
+
+            if (field.isDependent()) {
+                String comment = field.getDependency().isMandatory() ? "mandatory"
+                        : "optional";
+                build.append(" // " + comment);
+            }
+            build.append(System.lineSeparator());
+
+            build.append(System.lineSeparator());
+
+        }
+
+        if (!fields.isEmpty())
+            out.write(build.toString().toCharArray());
+    }
+}
diff --git a/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/directives/TypeDeclarationDirective.java b/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/directives/TypeDeclarationDirective.java
new file mode 100644 (file)
index 0000000..42556c2
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * 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.ftl.directives;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.Collection;
+import java.util.Map;
+
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.TypeDeclaration;
+
+import com.google.common.base.Preconditions;
+
+import freemarker.core.Environment;
+import freemarker.ext.beans.StringModel;
+import freemarker.template.TemplateDirectiveBody;
+import freemarker.template.TemplateDirectiveModel;
+import freemarker.template.TemplateException;
+import freemarker.template.TemplateModel;
+
+/**
+ * Add type declaration to freemarker template.
+ */
+public class TypeDeclarationDirective implements TemplateDirectiveModel {
+
+    private static final String OBJECT = "object";
+
+    @Override
+    public void execute(Environment env, Map params, TemplateModel[] loopVars,
+            TemplateDirectiveBody body) throws TemplateException, IOException {
+        Object object = params.get(OBJECT);
+        Preconditions.checkNotNull(object, "Null type declaration");
+
+        object = ((StringModel) object).getWrappedObject();
+        Preconditions.checkArgument(
+                object instanceof TypeDeclaration,
+                "Type declaration should be instance of "
+                        + TypeDeclaration.class + " but was "
+                        + object.getClass());
+
+        TypeDeclaration type = (TypeDeclaration) object;
+
+        Writer out = env.getOut();
+        StringBuilder build = new StringBuilder("public ");
+        if (type.isAbstract())
+            build.append("abstract ");
+        if (type.isFinal())
+            build.append("final ");
+        build.append(type.getType() + " ");
+        build.append(type.getName() + " ");
+
+        generateExtendOrImplement(build, "extends", type.getExtended());
+
+        generateExtendOrImplement(build, "implements", type.getImplemented());
+
+        build.append(System.lineSeparator());
+        out.write(build.toString().toCharArray());
+    }
+
+    private void generateExtendOrImplement(StringBuilder build, String prefix,
+            Collection<String> elements) {
+        if (elements.isEmpty())
+            return;
+
+        build.append(prefix + " ");
+
+        for (String extended : elements) {
+            build.append(extended);
+            build.append(", ");
+        }
+        build.deleteCharAt(build.length() - 1);
+        build.deleteCharAt(build.length() - 1);
+    }
+
+}
diff --git a/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/directives/UnimplementedExceptionDirective.java b/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/directives/UnimplementedExceptionDirective.java
new file mode 100644 (file)
index 0000000..5175ade
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * 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.ftl.directives;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.Map;
+
+import freemarker.core.Environment;
+import freemarker.template.TemplateDirectiveBody;
+import freemarker.template.TemplateDirectiveModel;
+import freemarker.template.TemplateException;
+import freemarker.template.TemplateModel;
+
+/**
+ * Add annotations to freemarker template.
+ */
+public class UnimplementedExceptionDirective implements TemplateDirectiveModel {
+
+    @Override
+    public void execute(Environment env, Map params, TemplateModel[] loopVars,
+            TemplateDirectiveBody body) throws TemplateException, IOException {
+
+        Writer out = env.getOut();
+        StringBuilder build = new StringBuilder();
+        build.append("        ");
+        build.append("throw new "
+                + UnsupportedOperationException.class.getCanonicalName()
+                + "(\"Unimplemented stub method\");");
+        build.append(System.lineSeparator());
+
+        out.write(build.toString().toCharArray());
+    }
+}
diff --git a/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/model/Annotation.java b/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/model/Annotation.java
new file mode 100644 (file)
index 0000000..5ec359e
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * 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.ftl.model;
+
+import java.util.List;
+
+import org.opendaylight.controller.config.api.annotations.Description;
+import org.opendaylight.controller.config.api.annotations.RequireInterface;
+import org.opendaylight.controller.config.api.annotations.ServiceInterfaceAnnotation;
+import org.opendaylight.controller.config.yangjmxgenerator.ServiceInterfaceEntry;
+import org.opendaylight.yangtools.yang.common.QName;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Lists;
+
+public class Annotation {
+    final String name;
+    final List<Parameter> params;
+
+    public Annotation(String name, List<Parameter> params) {
+        this.name = name;
+        this.params = params;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public List<Parameter> getParams() {
+        return params;
+    }
+
+    public static Annotation createDescriptionAnnotation(String description) {
+        Preconditions.checkNotNull(description,
+                "Cannot create annotation from null description");
+        return new Annotation(Description.class.getCanonicalName(),
+                Lists.newArrayList(new Parameter("value", q(description))));
+    }
+
+    public static Annotation createSieAnnotation(QName qname,
+            String exportedClassName) {
+        Preconditions.checkNotNull(qname,
+                "Cannot create annotation from null qname");
+        Preconditions.checkNotNull(exportedClassName,
+                "Cannot create annotation from null exportedClassName");
+
+        List<Parameter> params = Lists.newArrayList(new Parameter("value",
+                q(qname.getLocalName())));
+        params.add(new Parameter("osgiRegistrationType", exportedClassName
+                + ".class"));
+        return new Annotation(
+                ServiceInterfaceAnnotation.class.getCanonicalName(), params);
+    }
+
+    public static Annotation createRequireIfcAnnotation(
+            ServiceInterfaceEntry sie) {
+        String reqIfc = sie.getFullyQualifiedName() + ".class";
+        return new Annotation(RequireInterface.class.getCanonicalName(),
+                Lists.newArrayList(new Parameter("value", reqIfc)));
+    }
+
+    private static final String quote = "\"";
+
+    private static String q(String nullableDescription) {
+        return nullableDescription == null ? null : quote + nullableDescription
+                + quote;
+    }
+
+    public static class Parameter {
+        private final String key, value;
+
+        public Parameter(String key, String value) {
+            this.key = key;
+            this.value = value;
+        }
+
+        public String getKey() {
+            return key;
+        }
+
+        public String getValue() {
+            return value;
+        }
+    }
+
+}
diff --git a/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/model/Constructor.java b/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/model/Constructor.java
new file mode 100644 (file)
index 0000000..1aeb86d
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * 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.ftl.model;
+
+public class Constructor {
+
+    private final String typeName, body;
+    private final boolean isPublic;
+
+    public Constructor(String typeName, String body, boolean isPublic) {
+        super();
+        this.typeName = typeName;
+        this.body = body;
+        this.isPublic = isPublic;
+    }
+
+    // TODO add arguments if necessary
+
+    public Constructor(String typeName, String body) {
+        this(typeName, body, true);
+    }
+
+    public String getTypeName() {
+        return typeName;
+    }
+
+    public String getBody() {
+        return body;
+    }
+
+    public boolean isPublic() {
+        return isPublic;
+    }
+
+}
diff --git a/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/model/Field.java b/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/model/Field.java
new file mode 100644 (file)
index 0000000..517143e
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * 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.ftl.model;
+
+import java.util.List;
+
+import com.google.common.collect.Lists;
+
+public class Field {
+    private final String type;
+    private final String name;
+    private final String definition;
+    private final List<String> modifiers;
+
+    public Field(String type, String name) {
+        this(Lists.<String> newArrayList(), type, name, null);
+    }
+
+    public Field(List<String> modifiers, String type, String name) {
+        this(modifiers, type, name, null);
+    }
+
+    public Field(List<String> modifiers, String type, String name,
+            String definition) {
+        this.modifiers = modifiers;
+        this.type = type;
+        this.name = name;
+        this.definition = definition;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public List<String> getModifiers() {
+        return modifiers;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public String getDefinition() {
+        return definition;
+    }
+}
diff --git a/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/model/Header.java b/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/model/Header.java
new file mode 100644 (file)
index 0000000..047e87a
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * 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.ftl.model;
+
+public class Header {
+    private final String yangModuleName, yangModuleLocalName;
+
+    public Header(String yangModuleName, String yangModuleLocalName) {
+        super();
+        this.yangModuleName = yangModuleName;
+        this.yangModuleLocalName = yangModuleLocalName;
+    }
+
+    public String getYangModuleName() {
+        return yangModuleName;
+    }
+
+    public String getYangModuleLocalName() {
+        return yangModuleLocalName;
+    }
+
+    @Override
+    public String toString() {
+        return "yang module name: " + yangModuleName + " "
+                + " yang module local name: " + yangModuleLocalName;
+    }
+
+}
diff --git a/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/model/Method.java b/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/model/Method.java
new file mode 100644 (file)
index 0000000..3d2fff7
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * 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.ftl.model;
+
+import java.util.List;
+
+public interface Method {
+    List<String> getModifiers();
+
+    String getReturnType();
+
+    String getName();
+
+    List<Field> getParameters();
+
+    String getJavadoc();
+
+    List<Annotation> getAnnotations();
+}
diff --git a/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/model/MethodDeclaration.java b/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/model/MethodDeclaration.java
new file mode 100644 (file)
index 0000000..bd4e22e
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * 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.ftl.model;
+
+import java.util.Collections;
+import java.util.List;
+
+public class MethodDeclaration implements Method {
+    private final String returnType;
+    private final String name;
+    private final List<Field> parameters;
+    private String javadoc = null;
+    private final List<Annotation> annotations;
+
+    public MethodDeclaration(String returnType, String name,
+            List<Field> parameters) {
+        this(returnType, name, parameters, Collections.<Annotation> emptyList());
+    }
+
+    public MethodDeclaration(String returnType, String name,
+            List<Field> parameters, List<Annotation> annotations) {
+        this.returnType = returnType;
+        this.name = name;
+        this.parameters = parameters;
+        this.annotations = annotations;
+    }
+
+    @Override
+    public List<Annotation> getAnnotations() {
+        return annotations;
+    }
+
+    @Override
+    public String getJavadoc() {
+        return javadoc;
+    }
+
+    public void setJavadoc(String javadoc) {
+        this.javadoc = javadoc;
+    }
+
+    @Override
+    public String getReturnType() {
+        return returnType;
+    }
+
+    @Override
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public List<Field> getParameters() {
+        return parameters;
+    }
+
+    @Override
+    public List<String> getModifiers() {
+        return Collections.emptyList();
+    }
+}
diff --git a/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/model/MethodDefinition.java b/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/model/MethodDefinition.java
new file mode 100644 (file)
index 0000000..bf453ac
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * 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.ftl.model;
+
+import java.util.Collections;
+import java.util.List;
+
+public class MethodDefinition implements Method {
+    private final List<String> modifiers;
+    private final String returnType;
+    private final String name;
+    private final List<Field> parameters;
+    private final List<String> throwsExceptions;
+    private final String body;
+    private String javadoc = null;
+    private final List<Annotation> annotations;
+
+    // TODO remove, Constructor is in separate class
+    public static MethodDefinition createConstructor(String name,
+            List<Field> parameters, String body) {
+        return new MethodDefinition("", name, parameters, body);
+
+    }
+
+    public MethodDefinition(String returnType, String name,
+            List<Field> parameters, String body) {
+        this(Collections.<String> emptyList(), returnType, name, parameters,
+                Collections.<String> emptyList(), Collections
+                        .<Annotation> emptyList(), body);
+    }
+
+    public MethodDefinition(String returnType, String name,
+            List<Field> parameters, List<Annotation> annotations, String body) {
+        this(Collections.<String> emptyList(), returnType, name, parameters,
+                Collections.<String> emptyList(), annotations, body);
+    }
+
+    public MethodDefinition(List<String> modifiers, String returnType,
+            String name, List<Field> parameters, List<String> throwsExceptions,
+            List<Annotation> annotations, String body) {
+        this.modifiers = modifiers;
+        this.returnType = returnType;
+        this.name = name;
+        this.parameters = parameters;
+        this.throwsExceptions = throwsExceptions;
+        this.body = body;
+        this.annotations = annotations;
+    }
+
+    @Override
+    public List<Annotation> getAnnotations() {
+        return annotations;
+    }
+
+    @Override
+    public String getJavadoc() {
+        return javadoc;
+    }
+
+    public void setJavadoc(String javadoc) {
+        this.javadoc = javadoc;
+    }
+
+    @Override
+    public String getReturnType() {
+        return returnType;
+    }
+
+    @Override
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public List<Field> getParameters() {
+        return parameters;
+    }
+
+    public List<String> getThrowsExceptions() {
+        return throwsExceptions;
+    }
+
+    public String getBody() {
+        return body;
+    }
+
+    @Override
+    public List<String> getModifiers() {
+        return modifiers;
+    }
+}
diff --git a/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/model/ModuleField.java b/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/model/ModuleField.java
new file mode 100644 (file)
index 0000000..293696d
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.DependencyAttribute.Dependency;
+
+public class ModuleField extends Field {
+
+    private final String nullableDefault, attributeName;
+    private final boolean dependent;
+    private final Dependency dependency;
+
+    public ModuleField(List<String> modifiers, String type, String name,
+            String attributeName, String nullableDefault, boolean isDependency,
+            Dependency dependency) {
+        super(modifiers, type, name);
+        this.nullableDefault = nullableDefault;
+        this.dependent = isDependency;
+        this.dependency = dependency;
+        this.attributeName = attributeName;
+    }
+
+    public ModuleField(String type, String name, String attributeName,
+            String nullableDefault, boolean isDependency, Dependency dependency) {
+        this(Collections.<String> emptyList(), type, name, attributeName,
+                nullableDefault, isDependency, dependency);
+    }
+
+    public Dependency getDependency() {
+        return dependency;
+    }
+
+    public String getNullableDefault() {
+        return nullableDefault;
+    }
+
+    public boolean isDependent() {
+        return dependent;
+    }
+
+    public String getAttributeName() {
+        return attributeName;
+    }
+}
diff --git a/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/model/TypeDeclaration.java b/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/model/TypeDeclaration.java
new file mode 100644 (file)
index 0000000..ee0aa2f
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * 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.ftl.model;
+
+import java.util.List;
+
+public class TypeDeclaration {
+    private final String type, name;
+    private final List<String> extended, implemented;
+    private final boolean isAbstract, isFinal;
+
+    public TypeDeclaration(String type, String name, List<String> extended,
+            List<String> implemented, boolean isAbstract, boolean isFinal) {
+        super();
+        this.type = type;
+        this.name = name;
+        this.extended = extended;
+        this.implemented = implemented;
+        this.isAbstract = isAbstract;
+        this.isFinal = isFinal;
+    }
+
+    public TypeDeclaration(String type, String name, List<String> extended,
+            List<String> implemented) {
+        this(type, name, extended, implemented, false, false);
+    }
+
+    public boolean isAbstract() {
+        return isAbstract;
+    }
+
+    public boolean isFinal() {
+        return isFinal;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public List<String> getExtended() {
+        return extended;
+    }
+
+    public List<String> getImplemented() {
+        return implemented;
+    }
+
+    @Override
+    public String toString() {
+        return "TypeDeclaration{" + "type='" + type + '\'' + ", name='" + name
+                + '\'' + '}';
+    }
+}
diff --git a/opendaylight/config/yang-jmx-generator-plugin/src/main/resources/freeMarker/abstract_ftl_file.ftl b/opendaylight/config/yang-jmx-generator-plugin/src/main/resources/freeMarker/abstract_ftl_file.ftl
new file mode 100644 (file)
index 0000000..0c6bf83
--- /dev/null
@@ -0,0 +1,15 @@
+<@headerD header=header/>
+package ${packageName};
+
+<@javadocD object=javadoc/>
+<@annotationsD object=annotations/>
+<#-- class/interface -->
+<@typeDeclarationD object=typeDeclaration/>
+{
+<@constructorsD object=constructors>
+</@constructorsD>
+<@fieldsD object=fields/>
+
+<@methodsD object=methods/>
+}
diff --git a/opendaylight/config/yang-jmx-generator-plugin/src/main/resources/freeMarker/factory_abs_template.ftl b/opendaylight/config/yang-jmx-generator-plugin/src/main/resources/freeMarker/factory_abs_template.ftl
new file mode 100644 (file)
index 0000000..adfbceb
--- /dev/null
@@ -0,0 +1,63 @@
+<@headerD header=header/>
+package ${packageName};
+
+<@javadocD object=javadoc/>
+<@typeDeclarationD object=typeDeclaration/>
+{
+
+    public static final java.lang.String NAME = "${globallyUniqueName}";
+    private static final java.util.Set<Class<? extends ${abstractServiceInterfaceType}>> serviceIfcs = new java.util.HashSet<Class<? extends ${abstractServiceInterfaceType}>>();
+    <#if providedServices??>
+    static {
+        <#list providedServices as refId>
+        serviceIfcs.add(${refId});
+        </#list>
+    }
+    </#if>
+
+    @Override
+    public final boolean isModuleImplementingServiceInterface(Class<? extends ${abstractServiceInterfaceType}> serviceInterface) {
+        return serviceIfcs.contains(serviceInterface);
+    }
+
+    @Override
+    public ${moduleType} createModule(String instanceName, ${dependencyResolverType} dependencyResolver) {
+        return instantiateModule(instanceName, dependencyResolver);
+    }
+
+    @Override
+    public ${moduleType} createModule(String instanceName, ${dependencyResolverType} dependencyResolver, ${dynamicMBeanWithInstanceType} old) throws Exception {
+        ${moduleInstanceType} oldModule = null;
+        try {
+            oldModule = (${moduleInstanceType}) old.getModule();
+        } catch(Exception e) {
+            return handleChangedClass(old);
+        }
+        ${moduleInstanceType} module = instantiateModule(instanceName, dependencyResolver, oldModule, old.getInstance());
+
+        <#list fields as attr>
+        module.set${attr.name}(oldModule.get${attr.name}());
+        </#list>
+
+        return module;
+    }
+
+    public ${moduleInstanceType} instantiateModule(String instanceName, ${dependencyResolverType} dependencyResolver, ${moduleInstanceType} oldModule, ${instanceType} oldInstance) {
+        return new ${moduleInstanceType}(new ${moduleNameType}(NAME, instanceName), dependencyResolver, oldModule, oldInstance);
+    }
+
+    public ${moduleInstanceType} instantiateModule(String instanceName, ${dependencyResolverType} dependencyResolver) {
+        return new ${moduleInstanceType}(new ${moduleNameType}(NAME, instanceName), dependencyResolver);
+    }
+
+    @Override
+    public final String getImplementationName() {
+        return NAME;
+    }
+
+
+    public ${moduleInstanceType} handleChangedClass(${dynamicMBeanWithInstanceType} old) throws Exception {
+        throw new UnsupportedOperationException("Class reloading is not supported");
+    }
+
+}
diff --git a/opendaylight/config/yang-jmx-generator-plugin/src/main/resources/freeMarker/module_abs_template_new.ftl b/opendaylight/config/yang-jmx-generator-plugin/src/main/resources/freeMarker/module_abs_template_new.ftl
new file mode 100644 (file)
index 0000000..90412d7
--- /dev/null
@@ -0,0 +1,153 @@
+<@headerD header=header/>
+package ${packageName};
+
+<@javadocD object=javadoc/>
+<@annotationsD object=annotations/>
+<@typeDeclarationD object=typeDeclaration/>
+{
+    // attributes
+    <@moduleFieldsD moduleFields=moduleFields/>
+    //attributes end
+
+    private static final ${loggerType} logger = ${loggerFactoryType}.getLogger(${typeDeclaration.name}.class);
+
+    private final ${typeDeclaration.name} oldModule;
+    private final ${instanceType} oldInstance;
+    private ${instanceType} instance;
+    private final ${dependencyResolverType} dependencyResolver;
+    private final ${moduleNameType} name;
+    <#if runtime=true>
+    private ${registratorType} rootRuntimeBeanRegistratorWrapper;
+    </#if>
+
+    public ${typeDeclaration.name}(${moduleNameType} name, ${dependencyResolverType} dependencyResolver) {
+        this.name = name;
+        this.dependencyResolver = dependencyResolver;
+        this.oldInstance = null;
+        this.oldModule = null;
+    }
+
+    public ${typeDeclaration.name}(${moduleNameType} name, ${dependencyResolverType} dependencyResolver, ${typeDeclaration.name} oldModule, ${instanceType} oldInstance) {
+        this.name = name;
+        this.dependencyResolver = dependencyResolver;
+        this.oldInstance = oldInstance;
+        this.oldModule = oldModule;
+    }
+
+    // getters and setters exported into MXBean
+    <@methodsD object=methods/>
+
+    <#if runtime=true>
+    public ${registratorType} getRootRuntimeBeanRegistratorWrapper(){
+        return rootRuntimeBeanRegistratorWrapper;
+    }
+
+    @Override
+    public void setRuntimeBeanRegistrator(${rootRuntimeRegistratorType} rootRuntimeRegistrator){
+        this.rootRuntimeBeanRegistratorWrapper = new ${registratorType}(rootRuntimeRegistrator);
+    }
+    </#if>
+
+    @Override
+    public void validate(){
+    <#list moduleFields as field>
+        <#if field.dependent==true && field.dependency.mandatory==true>
+        dependencyResolver.validateDependency(${field.dependency.sie.fullyQualifiedName}.class, ${field.name}, ${field.name}JmxAttribute);
+        </#if>
+    </#list>
+    }
+
+    // caches of resolved dependencies
+    <#list moduleFields as field>
+    <#if field.dependent==true>
+        private ${field.dependency.sie.exportedOsgiClassName} ${field.name}Dependency;
+        protected final ${field.dependency.sie.exportedOsgiClassName} get${field.attributeName}Dependency(){
+            return ${field.name}Dependency;
+        }
+    </#if>
+    </#list>
+
+
+    @Override
+    public final ${instanceType} getInstance(){
+        if(instance==null) {
+
+            <#list moduleFields as field>
+                <#if field.dependent==true>
+
+                    <#if field.dependency.mandatory==false>
+                        if(${field.name}!=null) {
+                    </#if>
+
+                    ${field.name}Dependency = dependencyResolver.resolveInstance(${field.dependency.sie.exportedOsgiClassName}.class, ${field.name}, ${field.name}JmxAttribute);
+
+                    <#if field.dependency.mandatory==false>
+                        }
+                    </#if>
+                </#if>
+            </#list>
+
+            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();
+            }
+        }
+        return instance;
+    }
+
+    @Override
+    public final ${moduleNameType} getName() {
+        return name;
+    }
+
+    public boolean canReuseInstance(${typeDeclaration.name} oldModule){
+        // allow reusing of old instance if no parameters was changed
+        return equals(oldModule);
+    }
+
+    public ${instanceType} reuseInstance(${instanceType} oldInstance){
+        // implement if instance reuse should be supported. Override canReuseInstance to change the criteria.
+        return oldInstance;
+    }
+
+    public abstract ${instanceType} createInstance();
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        ${typeDeclaration.name} other = (${typeDeclaration.name}) obj;
+
+
+        <#list moduleFields as field>
+        <#if field.dependent==true>
+        if (${field.name}Dependency == null) {
+            if (other.${field.name}Dependency != null)
+                return false;
+        } else if (!${field.name}Dependency.equals(other.${field.name}Dependency))
+            return false;
+        <#else>
+        if (${field.name} == null) {
+            if (other.${field.name} != null)
+                return false;
+        } else if (!${field.name}.equals(other.${field.name}))
+            return false;
+        </#if>
+        </#list>
+
+        return true;
+    }
+
+}
diff --git a/opendaylight/config/yang-jmx-generator-plugin/src/main/resources/freeMarker/module_stub_template.ftl b/opendaylight/config/yang-jmx-generator-plugin/src/main/resources/freeMarker/module_stub_template.ftl
new file mode 100644 (file)
index 0000000..d2350ac
--- /dev/null
@@ -0,0 +1,27 @@
+<@headerD header=header/>
+package ${packageName};
+
+<@javadocD object=javadoc/>
+<@typeDeclarationD object=typeDeclaration/>
+{
+
+    public ${typeDeclaration.name}(${moduleNameType} name, ${dependencyResolverType} dependencyResolver) {
+        super(name, dependencyResolver);
+    }
+
+    public ${typeDeclaration.name}(${moduleNameType} name, ${dependencyResolverType} dependencyResolver, ${typeDeclaration.name} oldModule, ${instanceType} oldInstance) {
+        super(name, dependencyResolver, oldModule, oldInstance);
+    }
+
+    @Override
+    public void validate(){
+        super.validate();
+        // Add custom validation for module attributes here.
+    }
+
+    @Override
+    public ${instanceType} createInstance() {
+        //TODO:implement
+        <@unimplementedExceptionD/>
+    }
+}
diff --git a/opendaylight/config/yang-jmx-generator-plugin/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/AbstractGeneratorTest.java b/opendaylight/config/yang-jmx-generator-plugin/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/AbstractGeneratorTest.java
new file mode 100644 (file)
index 0000000..9e2f334
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * 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;
+
+import java.io.File;
+
+import org.apache.commons.io.FileUtils;
+import org.junit.Before;
+import org.opendaylight.controller.config.yangjmxgenerator.AbstractYangTest;
+
+public abstract class AbstractGeneratorTest extends AbstractYangTest {
+    private static final File GENERATOR_OUTPUT_PATH_ROOT = new File(
+            "target/testgen");
+    protected final File generatorOutputPath;
+
+    public AbstractGeneratorTest() {
+        generatorOutputPath = new File(GENERATOR_OUTPUT_PATH_ROOT, getClass()
+                .getSimpleName());
+    }
+
+    @Before
+    public void cleanUpDirectory() throws Exception {
+        FileUtils.deleteDirectory(generatorOutputPath);
+    }
+
+}
diff --git a/opendaylight/config/yang-jmx-generator-plugin/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/JMXGeneratorFileNamesValidationTest.java b/opendaylight/config/yang-jmx-generator-plugin/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/JMXGeneratorFileNamesValidationTest.java
new file mode 100644 (file)
index 0000000..b49b17e
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.config.yangjmxgenerator.plugin;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.junit.matchers.JUnitMatchers.containsString;
+
+import org.junit.Test;
+import org.opendaylight.controller.config.yangjmxgenerator.ConfigConstants;
+import org.opendaylight.controller.config.yangjmxgenerator.PackageTranslatorTest;
+
+import com.google.common.collect.Sets;
+
+public class JMXGeneratorFileNamesValidationTest extends JMXGeneratorTest {
+
+    @Test
+    public void test() {
+        map.clear();
+        map.put(JMXGenerator.NAMESPACE_TO_PACKAGE_PREFIX + "1",
+                ConfigConstants.CONFIG_NAMESPACE + ":test:files1"
+                        + JMXGenerator.NAMESPACE_TO_PACKAGE_DIVIDER
+                        + PackageTranslatorTest.EXPECTED_PACKAGE_PREFIX);
+        map.put(JMXGenerator.NAMESPACE_TO_PACKAGE_PREFIX + "2",
+                ConfigConstants.CONFIG_NAMESPACE + ":test:files"
+                        + JMXGenerator.NAMESPACE_TO_PACKAGE_DIVIDER
+                        + PackageTranslatorTest.EXPECTED_PACKAGE_PREFIX);
+
+        map.put(JMXGenerator.MODULE_FACTORY_FILE_BOOLEAN, "randomValue");
+        jmxGenerator.setAdditionalConfig(map);
+        try {
+            jmxGenerator.generateSources(context, outputBaseDir,
+                    Sets.newHashSet(testFilesModule, testFiles1Module));
+            fail();
+        } catch (RuntimeException e) {
+            final Throwable cause = e.getCause();
+            assertNotNull(cause);
+            assertTrue(cause instanceof IllegalStateException);
+            assertThat(cause.getMessage(),
+                    containsString("Name conflict in generated files"));
+            assertThat(cause.getMessage(), containsString("DtoA.java"));
+        }
+    }
+
+}
diff --git a/opendaylight/config/yang-jmx-generator-plugin/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/JMXGeneratorGeneratedFilesTrackerTest.java b/opendaylight/config/yang-jmx-generator-plugin/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/JMXGeneratorGeneratedFilesTrackerTest.java
new file mode 100644 (file)
index 0000000..4112ff0
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * 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;
+
+import java.io.File;
+
+import junit.framework.Assert;
+
+import org.junit.Test;
+
+public class JMXGeneratorGeneratedFilesTrackerTest {
+
+    @Test(expected = IllegalStateException.class)
+    public void testGeneratedFilesTracker() throws Exception {
+        JMXGenerator.GeneratedFilesTracker tracker = new JMXGenerator.GeneratedFilesTracker();
+
+        tracker.addFile(new File("./a/b/c"));
+        Assert.assertEquals(1, tracker.getFiles().size());
+        tracker.addFile(new File("./a/b/c"));
+    }
+}
diff --git a/opendaylight/config/yang-jmx-generator-plugin/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/JMXGeneratorTest.java b/opendaylight/config/yang-jmx-generator-plugin/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/JMXGeneratorTest.java
new file mode 100644 (file)
index 0000000..0c2678f
--- /dev/null
@@ -0,0 +1,712 @@
+/*
+ * 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;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.junit.matchers.JUnitMatchers.containsString;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.maven.plugin.logging.Log;
+import org.apache.maven.project.MavenProject;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.core.compiler.IProblem;
+import org.eclipse.jdt.core.dom.AST;
+import org.eclipse.jdt.core.dom.ASTParser;
+import org.eclipse.jdt.core.dom.ASTVisitor;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.FieldDeclaration;
+import org.eclipse.jdt.core.dom.MethodDeclaration;
+import org.eclipse.jdt.core.dom.NormalAnnotation;
+import org.eclipse.jdt.core.dom.PackageDeclaration;
+import org.eclipse.jdt.core.dom.TypeDeclaration;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.config.api.annotations.Description;
+import org.opendaylight.controller.config.api.annotations.RequireInterface;
+import org.opendaylight.controller.config.api.annotations.ServiceInterfaceAnnotation;
+import org.opendaylight.controller.config.spi.Module;
+import org.opendaylight.controller.config.spi.ModuleFactory;
+import org.opendaylight.controller.config.yangjmxgenerator.ConfigConstants;
+import org.opendaylight.controller.config.yangjmxgenerator.PackageTranslatorTest;
+import org.opendaylight.controller.config.yangjmxgenerator.ServiceInterfaceEntryTest;
+import org.xml.sax.ErrorHandler;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import com.google.common.io.Files;
+
+public class JMXGeneratorTest extends AbstractGeneratorTest {
+
+    JMXGenerator jmxGenerator;
+
+    protected final HashMap<String, String> map = Maps.newHashMap();
+    protected File outputBaseDir;
+    File generatedResourcesDir;
+
+    private static final List<String> expectedModuleFileNames = ServiceInterfaceEntryTest
+            .toFileNames("[AbstractAsyncEventBusModule.java, AbstractAsyncEventBusModuleFactory.java, " +
+                    "AbstractDynamicThreadPoolModule.java, AbstractDynamicThreadPoolModuleFactory.java, " +
+                    "AbstractEventBusModule.java, AbstractEventBusModuleFactory.java, " +
+                    "AbstractNamingThreadFactoryModule.java, AbstractNamingThreadFactoryModuleFactory.java, " +
+                    "AsyncEventBusModule.java, AsyncEventBusModuleFactory.java, AsyncEventBusModuleMXBean.java, " +
+                    "AsyncEventBusRuntimeMXBean.java, AsyncEventBusRuntimeRegistration.java, " +
+                    "AsyncEventBusRuntimeRegistrator.java, DynamicThreadPoolModule.java, " +
+                    "DynamicThreadPoolModuleFactory.java, DynamicThreadPoolModuleMXBean.java, " +
+                    "DynamicThreadPoolRuntimeMXBean.java, DynamicThreadPoolRuntimeRegistration.java, " +
+                    "DynamicThreadPoolRuntimeRegistrator.java, EventBusModule.java, EventBusModuleFactory.java, " +
+                    "EventBusModuleMXBean.java, EventRuntimeMXBean.java, EventRuntimeRegistration.java, " +
+                    "InnerStreamList.java, NamingThreadFactoryModule.java, NamingThreadFactoryModuleFactory.java, " +
+                    "NamingThreadFactoryModuleMXBean.java, NamingThreadFactoryRuntimeMXBean.java, NamingThreadFactoryRuntimeRegistration.java, NamingThreadFactoryRuntimeRegistrator.java, Peer.java, StreamRuntimeMXBean.java, StreamRuntimeRegistration.java, ThreadRuntimeMXBean.java, ThreadRuntimeRegistration.java, ThreadStreamRuntimeMXBean.java, ThreadStreamRuntimeRegistration.java]");
+
+    private static final List<String> expectedBGPNames = ServiceInterfaceEntryTest
+            .toFileNames("[AbstractBgpListenerImplModule.java, " + "AbstractBgpListenerImplModuleFactory.java, " +
+                    "BgpListenerImplModule.java, " + "BgpListenerImplModuleFactory.java, " +
+                    "BgpListenerImplModuleMXBean.java, Peers.java]");
+
+    private static final List<String> expectedNetconfNames = ServiceInterfaceEntryTest
+            .toFileNames("[AbstractNetconfTestImplModule.java, " + "AbstractNetconfTestImplModuleFactory.java, " +
+                    "AbstractTestImplModule.java, " + "AbstractTestImplModuleFactory.java, " +
+                    "AutoCloseableServiceInterface.java, " + "ComplexDtoBInner.java, ComplexList.java, Deep.java, " +
+                    "DtoA.java, DtoA1.java, DtoAInner.java, DtoAInnerInner.java, DtoB.java, DtoC.java," + "NetconfTestImplModule.java, NetconfTestImplModuleFactory.java," + "NetconfTestImplModuleMXBean.java, Peer.java, SimpleList.java, TestImplModule.java, " + "TestImplModuleFactory.java," + " TestImplModuleMXBean.java" + "]");
+    private static final List<String> expectedTestFiles = ServiceInterfaceEntryTest
+            .toFileNames("[AbstractNetconfTestFileImplModule.java, AbstractNetconfTestFileImplModuleFactory.java, " +
+                    "AbstractNetconfTestFiles1ImplModule.java, AbstractNetconfTestFiles1ImplModuleFactory.java, " +
+                    "AbstractTestFileImplModule.java, AbstractTestFileImplModuleFactory.java, " +
+                    "AbstractTestFiles1ImplModule.java, AbstractTestFiles1ImplModuleFactory.java, DtoA.java, " +
+                    "DtoA.java, NetconfTestFileImplModuleMXBean.java, NetconfTestFileImplRuntimeMXBean.java, " +
+                    "NetconfTestFileImplRuntimeRegistration.java, NetconfTestFileImplRuntimeRegistrator.java, " +
+                    "NetconfTestFiles1ImplModule.java, NetconfTestFiles1ImplModuleFactory.java, " +
+                    "NetconfTestFiles1ImplModuleMXBean.java, NetconfTestFiles1ImplRuntimeMXBean.java, " +
+                    "NetconfTestFiles1ImplRuntimeRegistration.java, NetconfTestFiles1ImplRuntimeRegistrator.java, TestFileImplModule.java, TestFileImplModuleFactory.java, TestFileImplModuleMXBean.java, TestFileImplRuntimeMXBean.java, TestFileImplRuntimeRegistration.java, TestFileImplRuntimeRegistrator.java, TestFiles1ImplModule.java, TestFiles1ImplModuleFactory.java, TestFiles1ImplModuleMXBean.java, TestFiles1ImplRuntimeMXBean.java, TestFiles1ImplRuntimeRegistration.java, TestFiles1ImplRuntimeRegistrator.java]");
+    private static final List<String> expectedAllFileNames = ServiceInterfaceEntryTest
+            .toFileNames("[AbstractAsyncEventBusModule.java, AbstractAsyncEventBusModuleFactory.java, " +
+                    "AbstractBgpListenerImplModule.java, AbstractBgpListenerImplModuleFactory.java, " +
+                    "AbstractDynamicThreadPoolModule.java, AbstractDynamicThreadPoolModuleFactory.java, " +
+                    "AbstractEventBusModule.java, AbstractEventBusModuleFactory.java, " +
+                    "AbstractNamingThreadFactoryModule.java, AbstractNamingThreadFactoryModuleFactory.java, " +
+                    "AbstractNetconfTestFileImplModule.java, AbstractNetconfTestFileImplModuleFactory.java, " +
+                    "AbstractNetconfTestFiles1ImplModule.java, AbstractNetconfTestFiles1ImplModuleFactory.java, " +
+                    "AbstractNetconfTestImplModule.java, AbstractNetconfTestImplModuleFactory.java, " +
+                    "AbstractTestFileImplModule.java, AbstractTestFileImplModuleFactory.java, " +
+                    "AbstractTestFiles1ImplModule.java, AbstractTestFiles1ImplModuleFactory.java, " +
+                    "AbstractTestImplModule.java, AbstractTestImplModuleFactory.java, AsyncEventBusModule.java, " +
+                    "AsyncEventBusModuleFactory.java, AsyncEventBusModuleMXBean.java, " +
+                    "AsyncEventBusRuntimeMXBean.java, AsyncEventBusRuntimeRegistration.java, " +
+                    "AsyncEventBusRuntimeRegistrator.java, AutoCloseableServiceInterface.java, " +
+                    "BgpListenerImplModule.java, BgpListenerImplModuleFactory.java, BgpListenerImplModuleMXBean.java," +
+                    " BgpListenerImplRuntimeMXBean.java, BgpListenerImplRuntimeRegistration.java, " +
+                    "BgpListenerImplRuntimeRegistrator.java, ComplexDtoBInner.java, ComplexList.java, Deep.java, " +
+                    "DtoA.java, DtoA.java, DtoA.java, DtoA1.java, DtoAInner.java, DtoAInnerInner.java, DtoB.java, " +
+                    "DtoC.java, DynamicThreadPoolModule.java, DynamicThreadPoolModuleFactory.java, " +
+                    "DynamicThreadPoolModuleMXBean.java, DynamicThreadPoolRuntimeMXBean.java, " +
+                    "DynamicThreadPoolRuntimeRegistration.java, DynamicThreadPoolRuntimeRegistrator.java, " +
+                    "EventBusModule.java, EventBusModuleFactory.java, EventBusModuleMXBean.java, " +
+                    "EventBusServiceInterface.java, EventRuntimeMXBean.java, EventRuntimeRegistration.java, " +
+                    "InnerStreamList.java, NamingThreadFactoryModule.java, NamingThreadFactoryModuleFactory.java, " +
+                    "NamingThreadFactoryModuleMXBean.java, NamingThreadFactoryRuntimeMXBean.java, " +
+                    "NamingThreadFactoryRuntimeRegistration.java, NamingThreadFactoryRuntimeRegistrator.java, " +
+                    "NetconfTestFileImplModule.java, NetconfTestFileImplModuleFactory.java, " +
+                    "NetconfTestFileImplModuleMXBean.java, NetconfTestFileImplRuntimeMXBean.java, " +
+                    "NetconfTestFileImplRuntimeRegistration.java, NetconfTestFileImplRuntimeRegistrator.java, " +
+                    "NetconfTestFiles1ImplModule.java, NetconfTestFiles1ImplModuleFactory.java, " +
+                    "NetconfTestFiles1ImplModuleMXBean.java, NetconfTestFiles1ImplRuntimeMXBean.java, " +
+                    "NetconfTestFiles1ImplRuntimeRegistration.java, NetconfTestFiles1ImplRuntimeRegistrator.java, NetconfTestImplModule.java, NetconfTestImplModuleFactory.java, NetconfTestImplModuleMXBean.java, NetconfTestImplRuntimeMXBean.java, NetconfTestImplRuntimeRegistration.java, NetconfTestImplRuntimeRegistrator.java, Peer.java, Peer.java, PeersRuntimeMXBean.java, PeersRuntimeRegistration.java, ScheduledThreadPoolServiceInterface.java, SimpleList.java, StreamRuntimeMXBean.java, StreamRuntimeRegistration.java, TestFileImplModule.java, TestFileImplModuleFactory.java, TestFileImplModuleMXBean.java, TestFileImplRuntimeMXBean.java, TestFileImplRuntimeRegistration.java, TestFileImplRuntimeRegistrator.java, TestFiles1ImplModule.java, TestFiles1ImplModuleFactory.java, TestFiles1ImplModuleMXBean.java, TestFiles1ImplRuntimeMXBean.java, TestFiles1ImplRuntimeRegistration.java, TestFiles1ImplRuntimeRegistrator.java, TestImplModule.java, TestImplModuleFactory.java, TestImplModuleMXBean.java, TestImplRuntimeMXBean.java, TestImplRuntimeRegistration.java, TestImplRuntimeRegistrator.java, ThreadFactoryServiceInterface.java, ThreadPoolServiceInterface.java, ThreadRuntimeMXBean.java, ThreadRuntimeRegistration.java, ThreadStreamRuntimeMXBean.java, ThreadStreamRuntimeRegistration.java]");
+    private static final List<String> expectedGenerateMBEsListNames = ServiceInterfaceEntryTest
+            .toFileNames("[AbstractBgpListenerImplModule.java, AbstractBgpListenerImplModuleFactory.java, BgpListenerImplModule.java, BgpListenerImplModuleFactory.java, BgpListenerImplModuleMXBean.java, BgpListenerImplRuntimeMXBean.java, BgpListenerImplRuntimeRegistration.java, BgpListenerImplRuntimeRegistrator.java, PeersRuntimeMXBean.java, PeersRuntimeRegistration.java]");
+
+    @Before
+    public void setUp() {
+        map.put(JMXGenerator.NAMESPACE_TO_PACKAGE_PREFIX + "1",
+                ConfigConstants.CONFIG_NAMESPACE
+                        + JMXGenerator.NAMESPACE_TO_PACKAGE_DIVIDER
+                        + PackageTranslatorTest.EXPECTED_PACKAGE_PREFIX);
+        map.put(JMXGenerator.MODULE_FACTORY_FILE_BOOLEAN, "false");
+        jmxGenerator = new JMXGenerator(new FreeMarkerCodeWriterImpl());
+        jmxGenerator.setAdditionalConfig(map);
+        File targetDir = new File(generatorOutputPath, "target");
+        generatedResourcesDir = new File(targetDir, "generated-resources");
+        jmxGenerator.setResourceBaseDir(generatedResourcesDir);
+        Log mockedLog = mock(Log.class);
+        doReturn(false).when(mockedLog).isDebugEnabled();
+        doNothing().when(mockedLog).debug(any(CharSequence.class));
+        doNothing().when(mockedLog).info(any(CharSequence.class));
+        doNothing().when(mockedLog).error(any(CharSequence.class),
+                any(Throwable.class));
+        jmxGenerator.setLog(mockedLog);
+        MavenProject project = mock(MavenProject.class);
+        doReturn(generatorOutputPath).when(project).getBasedir();
+        jmxGenerator.setMavenProject(project);
+        outputBaseDir = JMXGenerator.concatFolders(targetDir,
+                "generated-sources", "config");
+    }
+
+    @Test
+    public void generateSIsMBsTest() {
+        Collection<File> files = jmxGenerator.generateSources(context,
+                outputBaseDir, context.getModules());
+        List<String> expectedFileNames = new ArrayList<>();
+        expectedFileNames
+                .addAll(ServiceInterfaceEntryTest.expectedSIEFileNames);
+        expectedFileNames.addAll(expectedModuleFileNames);
+
+        expectedFileNames.addAll(expectedBGPNames);
+        expectedFileNames.addAll(expectedNetconfNames);
+        expectedFileNames.addAll(expectedTestFiles);
+        Collections.sort(expectedFileNames);
+        // TODO: separate expectedAllFileNames into expectedBGPNames,
+        // expectedNetconfNames
+        assertEquals(expectedAllFileNames, toFileNames(files));
+
+        verifyModuleFactoryFile(false);
+    }
+
+    private void verifyModuleFactoryFile(boolean shouldBePresent) {
+        File factoryFile = new File(generatedResourcesDir, "META-INF"
+                + File.separator + "services" + File.separator
+                + ModuleFactory.class.getName());
+        if (!shouldBePresent)
+            assertFalse("Factory file should not be generated",
+                    factoryFile.exists());
+        else
+            assertTrue("Factory file should be generated", factoryFile.exists());
+    }
+
+    public static List<String> toFileNames(Collection<File> files) {
+        List<String> result = new ArrayList<>();
+        for (File f : files) {
+            result.add(f.getName());
+        }
+        Collections.sort(result);
+        return result;
+    }
+
+    @Test
+    public void generateSIEsTest() throws IOException {
+        Collection<File> files = jmxGenerator.generateSources(context,
+                outputBaseDir, Sets.newHashSet(threadsModule));
+        assertEquals(ServiceInterfaceEntryTest.expectedSIEFileNames,
+                toFileNames(files));
+
+        Map<String, ASTVisitor> verifiers = Maps.newHashMap();
+
+        for (File file : files) {
+            verifiers.put(file.getName(), new SieASTVisitor());
+        }
+
+        processGeneratedCode(files, verifiers);
+
+        for (File file : files) {
+            String fileName = file.getName();
+            SieASTVisitor verifier = (SieASTVisitor) verifiers.get(fileName);
+
+            assertEquals(fileName.substring(0, fileName.length() - 5),
+                    verifier.type);
+            assertThat(
+                    verifier.extnds,
+                    containsString("org.opendaylight.controller.config.api.annotations.AbstractServiceInterface"));
+            assertEquals(PackageTranslatorTest.EXPECTED_PACKAGE_PREFIX
+                    + ".threads", verifier.packageName);
+            assertNotNull(verifier.javadoc);
+
+            if ("ThreadPoolServiceInterface.java".equals(fileName)) {
+                assertContains(verifier.descriptionAnotValue,
+                        "A simple pool of threads able to execute work.");
+                assertContains(verifier.sieAnnotValue, "threadpool");
+                assertContains(verifier.sieAnnotOsgiRegistrationType,
+                        PackageTranslatorTest.EXPECTED_PACKAGE_PREFIX
+                                + ".threadpool.ThreadPool.class");
+            } else if ("ScheduledThreadPoolServiceInterface.java"
+                    .equals(fileName)) {
+                assertContains(verifier.extnds,
+                        PackageTranslatorTest.EXPECTED_PACKAGE_PREFIX
+                                + ".threads.ThreadPoolServiceInterface");
+                assertContains(
+                        verifier.descriptionAnotValue,
+                        "An extension of the simple pool of threads able to schedule work to be executed at some point in time.");
+                assertContains(verifier.sieAnnotValue, "scheduled-threadpool");
+                assertContains(verifier.sieAnnotOsgiRegistrationType,
+                        PackageTranslatorTest.EXPECTED_PACKAGE_PREFIX
+                                + ".threadpool.ScheduledThreadPool.class");
+            } else if ("EventBusServiceInterface.java".equals(fileName)) {
+                assertContains(
+                        verifier.descriptionAnotValue,
+                        "Service representing an event bus. The service acts as message router between event producers and event consumers");
+                assertContains(verifier.sieAnnotValue, "eventbus");
+                assertContains(verifier.sieAnnotOsgiRegistrationType,
+                        "com.google.common.eventbus.EventBus.class");
+            } else if ("ThreadFactoryServiceInterface.java".equals(fileName)) {
+                assertContains(
+                        verifier.descriptionAnotValue,
+                        "Service representing a ThreadFactory instance. It is directly useful in Java world, where various library pieces need to create threads and you may want to inject a customized thread implementation.");
+                assertContains(verifier.sieAnnotValue, "threadfactory");
+                assertContains(verifier.sieAnnotOsgiRegistrationType,
+                        "java.util.concurrent.ThreadFactory.class");
+
+            } else if ("ScheduledExecutorServiceServiceInterface.java"
+                    .equals(fileName)) {
+                assertContains(verifier.sieAnnotOsgiRegistrationType,
+                        "java.util.concurrent.ScheduledExecutorService.class");
+            } else {
+                fail("Unknown generated sie " + fileName);
+            }
+        }
+    }
+
+    @Test
+    public void generateMBEsListTest() throws Exception {
+        // default value for module factory file is true
+        map.put(JMXGenerator.MODULE_FACTORY_FILE_BOOLEAN, "randomValue");
+        jmxGenerator.setAdditionalConfig(map);
+
+        Collection<File> files = jmxGenerator.generateSources(context,
+                outputBaseDir, Sets.newHashSet(bgpListenerJavaModule));
+
+        assertEquals(expectedGenerateMBEsListNames, toFileNames(files));
+    }
+
+    @Test
+    public void generateMBEsTest() throws Exception {
+        // default value for module factory file is true
+        map.put(JMXGenerator.MODULE_FACTORY_FILE_BOOLEAN, "randomValue");
+        jmxGenerator.setAdditionalConfig(map);
+
+        Collection<File> files = jmxGenerator.generateSources(context,
+                outputBaseDir, Sets.newHashSet(threadsJavaModule));
+
+        assertEquals(expectedModuleFileNames, toFileNames(files));
+
+        Map<String, ASTVisitor> verifiers = Maps.newHashMap();
+
+        Collection<File> xmlFiles = Collections2.filter(files,
+                new Predicate<File>() {
+
+                    @Override
+                    public boolean apply(File input) {
+                        return input.getName().endsWith("xml");
+                    }
+                });
+
+        Collection<File> javaFiles = Collections2.filter(files,
+                new Predicate<File>() {
+
+                    @Override
+                    public boolean apply(File input) {
+                        return input.getName().endsWith("java");
+                    }
+                });
+
+        MbeASTVisitor abstractDynamicThreadPoolModuleVisitor = null;
+        MbeASTVisitor asyncEventBusModuleMXBeanVisitor = null;
+        MbeASTVisitor abstractNamingThreadFactoryModuleFactoryVisitor = null;
+        MbeASTVisitor asyncEventBusModuleVisitor = null;
+        MbeASTVisitor eventBusModuleFactoryVisitor = null;
+
+        for (File file : javaFiles) {
+            String name = file.getName();
+            MbeASTVisitor visitor = new MbeASTVisitor();
+            verifiers.put(name, visitor);
+            if (name.equals("AbstractDynamicThreadPoolModule.java"))
+                abstractDynamicThreadPoolModuleVisitor = visitor;
+            if (name.equals("AsyncEventBusModuleMXBean.java"))
+                asyncEventBusModuleMXBeanVisitor = visitor;
+            if (name.equals("AbstractNamingThreadFactoryModuleFactory.java"))
+                abstractNamingThreadFactoryModuleFactoryVisitor = visitor;
+            if (name.equals("AsyncEventBusModule.java"))
+                asyncEventBusModuleVisitor = visitor;
+            if (name.equals("EventBusModuleFactory.java"))
+                eventBusModuleFactoryVisitor = visitor;
+        }
+
+        processGeneratedCode(javaFiles, verifiers);
+
+        assertAbstractDynamicThreadPoolModule(abstractDynamicThreadPoolModuleVisitor);
+        assertAsyncEventBusModuleMXBean(asyncEventBusModuleMXBeanVisitor);
+        assertAbstractNamingThreadFactoryModuleFactory(abstractNamingThreadFactoryModuleFactoryVisitor);
+        assertAsyncEventBusModule(asyncEventBusModuleVisitor);
+        assertEventBusModuleFactory(eventBusModuleFactoryVisitor);
+
+        verifyXmlFiles(xmlFiles);
+        // verify ModuleFactory file
+        File moduleFactoryFile = JMXGenerator.concatFolders(
+                generatedResourcesDir, "META-INF", "services",
+                ModuleFactory.class.getName());
+        assertThat(moduleFactoryFile.exists(), is(true));
+        Set<String> lines = Sets.newHashSet(FileUtils
+                .readLines(moduleFactoryFile));
+        Set<String> expectedLines = Sets.newHashSet(//
+                PackageTranslatorTest.EXPECTED_PACKAGE_PREFIX
+                        + ".threads.java.EventBusModuleFactory",//
+                PackageTranslatorTest.EXPECTED_PACKAGE_PREFIX
+                        + ".threads.java.AsyncEventBusModuleFactory", //
+                PackageTranslatorTest.EXPECTED_PACKAGE_PREFIX
+                        + ".threads.java.DynamicThreadPoolModuleFactory",//
+                PackageTranslatorTest.EXPECTED_PACKAGE_PREFIX
+                        + ".threads.java.NamingThreadFactoryModuleFactory");
+        assertThat(lines, equalTo(expectedLines));
+
+    }
+
+    private void verifyXmlFiles(Collection<File> xmlFiles) throws Exception {
+        ErrorHandler errorHandler = new ErrorHandler() {
+
+            @Override
+            public void warning(SAXParseException exception)
+                    throws SAXException {
+                fail("Generated blueprint xml is not well formed "
+                        + exception.getMessage());
+            }
+
+            @Override
+            public void fatalError(SAXParseException exception)
+                    throws SAXException {
+                fail("Generated blueprint xml is not well formed "
+                        + exception.getMessage());
+            }
+
+            @Override
+            public void error(SAXParseException exception) throws SAXException {
+                fail("Generated blueprint xml is not well formed "
+                        + exception.getMessage());
+            }
+        };
+
+        for (File file : xmlFiles) {
+            DocumentBuilderFactory factory = DocumentBuilderFactory
+                    .newInstance();
+            factory.setValidating(false);
+            factory.setNamespaceAware(true);
+
+            DocumentBuilder builder = factory.newDocumentBuilder();
+
+            builder.setErrorHandler(errorHandler);
+            builder.parse(new InputSource(file.getPath()));
+        }
+
+    }
+
+    private void assertEventBusModuleFactory(MbeASTVisitor visitor) {
+        assertEquals(PackageTranslatorTest.EXPECTED_PACKAGE_PREFIX
+                + ".threads.java", visitor.packageName);
+        assertEquals("EventBusModuleFactory", visitor.type);
+        assertContains(visitor.extnds,
+                PackageTranslatorTest.EXPECTED_PACKAGE_PREFIX
+                        + ".threads.java.AbstractEventBusModuleFactory");
+
+        assertEquals(0, visitor.fieldDeclarations.size());
+
+        assertEquals("Incorrenct number of generated methods", 0,
+                visitor.methods.size());
+        assertEquals("Incorrenct number of generated constructors", 0,
+                visitor.constructors.size());
+        assertEquals("Incorrenct number of generated method descriptions", 0,
+                visitor.methodDescriptions.size());
+        assertEquals("Incorrenct number of generated method javadoc", 0,
+                visitor.methodJavadoc.size());
+    }
+
+    private void assertAsyncEventBusModule(MbeASTVisitor visitor) {
+        assertEquals(PackageTranslatorTest.EXPECTED_PACKAGE_PREFIX
+                + ".threads.java", visitor.packageName);
+        assertEquals("AsyncEventBusModule", visitor.type);
+        assertContains(visitor.extnds,
+                PackageTranslatorTest.EXPECTED_PACKAGE_PREFIX
+                        + ".threads.java.AbstractAsyncEventBusModule");
+
+        assertEquals(0, visitor.fieldDeclarations.size());
+
+        assertEquals("Incorrenct number of generated methods", 2,
+                visitor.methods.size());
+        assertEquals("Incorrenct number of generated constructors", 2,
+                visitor.constructors.size());
+        assertEquals("Incorrenct number of generated method descriptions", 0,
+                visitor.methodDescriptions.size());
+        assertEquals("Incorrenct number of generated method javadoc", 0,
+                visitor.methodJavadoc.size());
+    }
+
+    private void assertAbstractNamingThreadFactoryModuleFactory(
+            MbeASTVisitor visitor) {
+        assertEquals(PackageTranslatorTest.EXPECTED_PACKAGE_PREFIX
+                + ".threads.java", visitor.packageName);
+        assertEquals("AbstractNamingThreadFactoryModuleFactory", visitor.type);
+        assertContains(visitor.implmts,
+                "org.opendaylight.controller.config.spi.ModuleFactory");
+        Set<String> fieldDeclarations = visitor.fieldDeclarations;
+        assertDeclaredField(fieldDeclarations,
+                "public static final java.lang.String NAME=\"threadfactory-naming\"");
+        assertDeclaredField(
+                fieldDeclarations,
+                "private static final java.util.Set<Class<? extends org.opendaylight.controller.config.api.annotations.AbstractServiceInterface>> serviceIfcs=new java.util.HashSet<Class<? extends org.opendaylight.controller.config.api.annotations.AbstractServiceInterface>>()");
+
+        assertEquals(2, fieldDeclarations.size());
+
+        assertEquals("Incorrenct number of generated methods", 5,
+                visitor.methods.size());
+        assertEquals("Incorrenct number of generated method descriptions", 0,
+                visitor.methodDescriptions.size());
+        assertEquals("Incorrenct number of generated method javadoc", 0,
+                visitor.methodJavadoc.size());
+
+    }
+
+    private void assertAsyncEventBusModuleMXBean(MbeASTVisitor visitor) {
+        assertEquals(PackageTranslatorTest.EXPECTED_PACKAGE_PREFIX
+                + ".threads.java", visitor.packageName);
+        assertEquals("AsyncEventBusModuleMXBean", visitor.type);
+
+        assertEquals("Incorrenct number of generated methods", 2,
+                visitor.methods.size());
+    }
+
+    private void assertAbstractDynamicThreadPoolModule(MbeASTVisitor visitor) {
+        assertEquals(PackageTranslatorTest.EXPECTED_PACKAGE_PREFIX
+                + ".threads.java", visitor.packageName);
+        assertNotNull(visitor.javadoc);
+        assertContains(visitor.descriptionAnotValue,
+                "threadpool-dynamic description");
+        assertEquals("AbstractDynamicThreadPoolModule", visitor.type);
+        assertContains(visitor.implmts,
+                PackageTranslatorTest.EXPECTED_PACKAGE_PREFIX
+                        + ".threads.java.DynamicThreadPoolModuleMXBean",
+                Module.class.getCanonicalName(),
+                PackageTranslatorTest.EXPECTED_PACKAGE_PREFIX
+                        + ".threads.ScheduledThreadPoolServiceInterface",
+                PackageTranslatorTest.EXPECTED_PACKAGE_PREFIX
+                        + ".threads.ThreadPoolServiceInterface");
+        assertEquals(2, visitor.constructors.size());
+        Set<String> fieldDeclarations = visitor.fieldDeclarations;
+        assertDeclaredField(fieldDeclarations,
+                "private java.lang.Long maximumSize");
+        assertDeclaredField(fieldDeclarations,
+                "private javax.management.ObjectName threadfactory");
+        assertDeclaredField(fieldDeclarations,
+                "private java.util.concurrent.ThreadFactory threadfactoryDependency");
+        assertDeclaredField(fieldDeclarations,
+                "private java.lang.Long keepAlive=10");
+        assertDeclaredField(fieldDeclarations,
+                "private java.lang.Long coreSize");
+        assertDeclaredField(fieldDeclarations, "private byte[] binary");
+        assertEquals(22, fieldDeclarations.size());
+
+        assertEquals(1, visitor.requireIfc.size());
+        String reqIfc = visitor.requireIfc.get("setThreadfactory");
+        assertNotNull("Missing generated setter for threadfactory", reqIfc);
+        assertContains(reqIfc, PackageTranslatorTest.EXPECTED_PACKAGE_PREFIX
+                + ".threads.ThreadFactoryServiceInterface");
+
+        assertEquals("Incorrenct number of generated methods", 24,
+                visitor.methods.size());
+        assertEquals("Incorrenct number of generated method descriptions", 3,
+                visitor.methodDescriptions.size());
+        assertEquals("Incorrenct number of generated method javadoc", 3,
+                visitor.methodJavadoc.size());
+        assertNotNull("Missing javadoc for setMaximumSize method",
+                visitor.methodJavadoc.get("setMaximumSize"));
+    }
+
+    private void assertDeclaredField(Set<String> fieldDeclarations,
+            String declaration) {
+        assertTrue("Missing field " + declaration + ", got: "
+                + fieldDeclarations,
+                fieldDeclarations.contains(declaration + ";\n"));
+    }
+
+    private static class SieASTVisitor extends ASTVisitor {
+        protected String packageName, descriptionAnotValue, sieAnnotValue,
+                sieAnnotOsgiRegistrationType, type, extnds, javadoc;
+        protected Map<String, String> methodDescriptions = Maps.newHashMap();
+
+        @Override
+        public boolean visit(PackageDeclaration node) {
+            packageName = node.getName().toString();
+            return super.visit(node);
+        }
+
+        @Override
+        public boolean visit(NormalAnnotation node) {
+            if (node.getTypeName().toString()
+                    .equals(Description.class.getCanonicalName())) {
+                if (node.getParent() instanceof TypeDeclaration) {
+                    descriptionAnotValue = node.values().get(0).toString();
+                } else if (node.getParent() instanceof MethodDeclaration) {
+                    String descr = node.values().get(0).toString();
+                    methodDescriptions.put(((MethodDeclaration) node
+                            .getParent()).getName().toString(), descr);
+                }
+            } else if (node
+                    .getTypeName()
+                    .toString()
+                    .equals(ServiceInterfaceAnnotation.class.getCanonicalName())) {
+                String text1 = node.values().get(0).toString();
+                String text2 = node.values().get(1).toString();
+                if (text1.contains("value")) {
+                    sieAnnotValue = text1;
+                    sieAnnotOsgiRegistrationType = text2;
+                } else {
+                    sieAnnotValue = text2;
+                    sieAnnotOsgiRegistrationType = text1;
+                }
+            }
+            return super.visit(node);
+        }
+
+        @Override
+        public boolean visit(TypeDeclaration node) {
+            javadoc = node.getJavadoc() == null ? null : node.getJavadoc()
+                    .toString();
+            type = node.getName().toString();
+            List<?> superIfcs = node.superInterfaceTypes();
+            extnds = superIfcs != null && !superIfcs.isEmpty() ? superIfcs
+                    .toString() : null;
+            return super.visit(node);
+        }
+    }
+
+    private static class MbeASTVisitor extends SieASTVisitor {
+        private String implmts;
+        private final Set<String> fieldDeclarations = Sets.newHashSet();
+        private final Set<String> constructors = Sets.newHashSet();
+        private final Map<String, String> methods = Maps.newHashMap();
+        private final Map<String, String> requireIfc = Maps.newHashMap();
+        private final Map<String, String> methodJavadoc = Maps.newHashMap();
+
+        @Override
+        public boolean visit(NormalAnnotation node) {
+            boolean result = super.visit(node);
+            if (node.getTypeName().toString()
+                    .equals(RequireInterface.class.getCanonicalName())
+                    && node.getParent() instanceof MethodDeclaration) {
+                // remember only top level description annotation
+                String reqVal = node.values().get(0).toString();
+                requireIfc.put(((MethodDeclaration) node.getParent()).getName()
+                        .toString(), reqVal);
+            }
+            return result;
+        }
+
+        @Override
+        public boolean visit(FieldDeclaration node) {
+            fieldDeclarations.add(node.toString());
+            return super.visit(node);
+        }
+
+        @Override
+        public boolean visit(MethodDeclaration node) {
+            if (node.isConstructor())
+                constructors.add(node.toString());
+            else {
+                String methodName = node.getName().toString();
+                if (node.getJavadoc() != null)
+                    methodJavadoc.put(methodName, node.getJavadoc().toString());
+                methods.put(methodName, node.toString());
+            }
+            return super.visit(node);
+        }
+
+        @Override
+        public boolean visit(TypeDeclaration node) {
+            boolean visit = super.visit(node);
+            List<?> superIfcs = node.superInterfaceTypes();
+            implmts = superIfcs != null && !superIfcs.isEmpty() ? superIfcs
+                    .toString() : null;
+            extnds = node.getSuperclassType() == null ? null : node
+                    .getSuperclassType().toString();
+            return visit;
+        }
+
+    }
+
+    private void assertContains(String source, String... contained) {
+        for (String string : contained) {
+            assertThat(source, containsString(string));
+        }
+    }
+
+    private void processGeneratedCode(Collection<File> files,
+            Map<String, ASTVisitor> verifiers) throws IOException {
+        ASTParser parser = ASTParser.newParser(AST.JLS3);
+        Map<?, ?> options = JavaCore.getOptions();
+        JavaCore.setComplianceOptions(JavaCore.VERSION_1_7, options);
+        parser.setCompilerOptions(options);
+
+        parser.setKind(ASTParser.K_COMPILATION_UNIT);
+
+        for (File file : files) {
+            char[] source = readFileAsChars(file);
+            parser.setSource(source);
+            parser.setResolveBindings(true);
+
+            CompilationUnit cu = (CompilationUnit) parser.createAST(null);
+            // Check for compilation problems in generated file
+            for (IProblem c : cu.getProblems()) {
+                // 1610613332 = Syntax error, annotations are only available if
+                // source level is 5.0
+                if (c.getID() == 1610613332)
+                    continue;
+                // 1610613332 = Syntax error, parameterized types are only
+                // available if source level is 5.0
+                if (c.getID() == 1610613329)
+                    continue;
+                fail("Error in generated source code " + file + ":"
+                        + c.getSourceLineNumber() + " " + c.toString());
+            }
+
+            ASTVisitor visitor = verifiers.get(file.getName());
+            if (visitor == null)
+                fail("Unknown generated file " + file.getName());
+            cu.accept(visitor);
+
+        }
+    }
+
+    public static char[] readFileAsChars(File file) throws IOException {
+        List<String> readLines = Files
+                .readLines(file, Charset.forName("utf-8"));
+        char[] retVal = new char[0];
+        for (String string : readLines) {
+            char[] line = string.toCharArray();
+            int beforeLength = retVal.length;
+            retVal = Arrays.copyOf(retVal, retVal.length + line.length + 1);
+            System.arraycopy(line, 0, retVal, beforeLength, line.length);
+            retVal[retVal.length - 1] = '\n';
+        }
+        return retVal;
+    }
+
+}
diff --git a/opendaylight/config/yang-jmx-generator-plugin/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ModuleMXBeanEntryPluginTest.java b/opendaylight/config/yang-jmx-generator-plugin/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ModuleMXBeanEntryPluginTest.java
new file mode 100644 (file)
index 0000000..b457216
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ * 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;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.Test;
+import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry;
+import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntryTest;
+import org.opendaylight.controller.config.yangjmxgenerator.RuntimeBeanEntry;
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc;
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.JavaAttribute;
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.ListAttribute;
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.TOAttribute;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.FtlTemplate;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.GeneralClassTemplate;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.GeneralInterfaceTemplate;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.TemplateFactory;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Method;
+
+public class ModuleMXBeanEntryPluginTest extends ModuleMXBeanEntryTest {
+
+    @Test
+    public void testThreadsJavaPlugin() {
+        Map<String /* identity local name */, ModuleMXBeanEntry> namesToMBEs = loadThreadsJava();
+        {// check threadfactory-naming
+            ModuleMXBeanEntry threadFactoryNaming = namesToMBEs
+                    .get(THREADFACTORY_NAMING_MXB_NAME);
+            Collection<RuntimeBeanEntry> runtimeBeans = threadFactoryNaming
+                    .getRuntimeBeans();
+            assertThat(runtimeBeans.size(), is(4));
+            // first one should be root
+            {
+                RuntimeBeanEntry rootRB = findFirstByYangName(runtimeBeans,
+                        THREADFACTORY_NAMING_MXB_NAME);
+                assertThat(rootRB.isRoot(), is(true));
+                assertThat(rootRB.getAttributes().size(), is(1));
+                JavaAttribute attribute = (JavaAttribute) rootRB
+                        .getAttributes().iterator().next();
+                assertThat(attribute.getAttributeYangName(),
+                        is("created-sessions"));
+                assertThat(rootRB.getYangName(),
+                        is(THREADFACTORY_NAMING_MXB_NAME));
+                Map<String, FtlTemplate> ftlMap = TemplateFactory
+                        .getTOAndMXInterfaceFtlFiles(rootRB);
+                assertThat(ftlMap.size(), is(1));
+                GeneralInterfaceTemplate rootGeneratorInterface = (GeneralInterfaceTemplate) ftlMap
+                        .get("NamingThreadFactoryRuntimeMXBean.java");
+                assertNotNull(rootGeneratorInterface);
+                assertThat(rootGeneratorInterface.getPackageName(),
+                        is(PACKAGE_NAME));
+                assertThat(rootGeneratorInterface.getFullyQualifiedName(),
+                        is(PACKAGE_NAME + ".NamingThreadFactoryRuntimeMXBean"));
+                assertThat(
+                        rootGeneratorInterface.getTypeDeclaration()
+                                .getExtended(),
+                        is(Arrays
+                                .asList("org.opendaylight.controller.config.api.runtime.RuntimeBean")));
+
+                assertThat(rootGeneratorInterface.getMethods().size(), is(1));
+                Method getCreatedSessions = findFirstMethodByName(
+                        rootGeneratorInterface.getMethods(),
+                        "getCreatedSessions");
+                assertThat(getCreatedSessions.getName(),
+                        is("getCreatedSessions"));
+                assertThat(getCreatedSessions.getParameters().isEmpty(),
+                        is(true));
+                assertThat(getCreatedSessions.getReturnType(),
+                        is(Long.class.getName()));
+            }
+        }
+        {
+            ModuleMXBeanEntry threadFactoryNaming = namesToMBEs
+                    .get(THREADFACTORY_NAMING_MXB_NAME);
+            Collection<RuntimeBeanEntry> runtimeBeans = threadFactoryNaming
+                    .getRuntimeBeans();
+            assertThat(runtimeBeans.size(), is(4));
+
+            {
+                RuntimeBeanEntry streamRB = findFirstByYangName(runtimeBeans,
+                        "stream");
+                assertNotNull(streamRB);
+                assertFalse(streamRB.getKeyYangName().isPresent());
+                assertFalse(streamRB.getKeyJavaName().isPresent());
+                Map<String, AttributeIfc> attributeMap = streamRB
+                        .getYangPropertiesToTypesMap();
+                assertEquals(4, attributeMap.size());
+                TOAttribute toAttr = (TOAttribute) attributeMap.get("peer");
+                assertNotNull(toAttr);
+                JavaAttribute timestampAttr = (JavaAttribute) attributeMap
+                        .get("timestamp");
+                assertNotNull(timestampAttr);
+                JavaAttribute stateAttr = (JavaAttribute) attributeMap
+                        .get("state");
+                assertNotNull(stateAttr);
+                ListAttribute innerStreamList = (ListAttribute) attributeMap
+                        .get("inner-stream-list");
+                assertNotNull(innerStreamList);
+
+                Map<String, FtlTemplate> ftlMap = TemplateFactory
+                        .getTOAndMXInterfaceFtlFiles(streamRB);
+                assertThat(ftlMap.size(), is(3));
+                GeneralInterfaceTemplate streamGeneralInterface = (GeneralInterfaceTemplate) ftlMap
+                        .get("ThreadStreamRuntimeMXBean.java");
+                assertThat(streamGeneralInterface.getMethods().size(), is(4));
+                Method getPeer = findFirstMethodByName(
+                        streamGeneralInterface.getMethods(), "getPeer");
+                assertNotNull(getPeer);
+                assertThat(getPeer.getReturnType(), is(PACKAGE_NAME + ".Peer"));
+
+                // test TO
+                GeneralClassTemplate peerTO = (GeneralClassTemplate) ftlMap
+                        .get("pack2.Peer");
+                assertThat(peerTO.getPackageName(), is(PACKAGE_NAME));
+                assertThat(peerTO.getTypeDeclaration().getExtended().isEmpty(),
+                        is(true));
+                assertThat(peerTO.getFullyQualifiedName(), is(PACKAGE_NAME
+                        + ".Peer"));
+                assertThat(peerTO.getMethods().size(), is(4));
+                Method getPort = findFirstMethodByName(peerTO.getMethods(),
+                        "getPort");
+                assertNotNull(getPort);
+                Method setPort = findFirstMethodByName(peerTO.getMethods(),
+                        "setPort");
+                assertNotNull(setPort);
+                Method getCoreSize = findFirstMethodByName(peerTO.getMethods(),
+                        "getCoreSize");
+                Method setCoreSize = findFirstMethodByName(peerTO.getMethods(),
+                        "setCoreSize");
+                assertNotNull(setCoreSize);
+                assertNotNull(getCoreSize);
+
+            }
+        }
+    }
+
+    private Method findFirstMethodByName(List<? extends Method> methods,
+            String name) {
+        for (Method ms : methods) {
+            if (name.equals(ms.getName())) {
+                return ms;
+            }
+        }
+        throw new IllegalArgumentException("Method with given name not found");
+    }
+}
diff --git a/opendaylight/config/yang-jmx-generator-plugin/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ModuleMXBeanEntryTemplatesTest.java b/opendaylight/config/yang-jmx-generator-plugin/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ModuleMXBeanEntryTemplatesTest.java
new file mode 100644 (file)
index 0000000..3c47931
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * 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;
+
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
+import java.util.Collections;
+import java.util.Map;
+
+import org.junit.Test;
+import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry;
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc;
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.JavaAttribute;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.AbstractFactoryTemplate;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.TemplateFactory;
+import org.opendaylight.yangtools.sal.binding.model.api.Type;
+
+import com.google.common.collect.Maps;
+
+public class ModuleMXBeanEntryTemplatesTest {
+
+    @Test
+    public void test() {
+        ModuleMXBeanEntry mbe = mockMbe("package");
+        AbstractFactoryTemplate template = TemplateFactory
+                .abstractFactoryTemplateFromMbe(mbe);
+        assertNotNull(template);
+    }
+
+    private ModuleMXBeanEntry mockMbe(String packageName) {
+        ModuleMXBeanEntry mbe = mock(ModuleMXBeanEntry.class);
+        Map<String, AttributeIfc> a = Maps.newHashMap();
+        JavaAttribute attr = mockJavaAttr();
+
+        a.put("attr1", attr);
+        doReturn(a).when(mbe).getAttributes();
+        doReturn(packageName).when(mbe).getPackageName();
+        doReturn(Collections.emptyMap()).when(mbe).getProvidedServices();
+        doReturn("yang-module").when(mbe).getYangModuleName();
+        doReturn("local").when(mbe).getYangModuleLocalname();
+        doReturn("AbstractType").when(mbe).getAbstractFactoryName();
+        doReturn("Module").when(mbe).getStubModuleName();
+        doReturn("fullA").when(mbe).getFullyQualifiedName(anyString());
+        doReturn("uniq").when(mbe).getGloballyUniqueName();
+        return mbe;
+    }
+
+    private JavaAttribute mockJavaAttr() {
+        JavaAttribute attr = mock(JavaAttribute.class);
+        Type typeA = mock(Type.class);
+        doReturn("package").when(typeA).getName();
+        doReturn("type").when(typeA).getPackageName();
+        doReturn("package.type").when(typeA).getFullyQualifiedName();
+        doReturn(typeA).when(attr).getType();
+        doReturn("Type").when(attr).getUpperCaseCammelCase();
+        return attr;
+    }
+
+}
diff --git a/opendaylight/config/yang-jmx-generator-plugin/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/RuntimeRegistratorFtlFileTest.java b/opendaylight/config/yang-jmx-generator-plugin/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/RuntimeRegistratorFtlFileTest.java
new file mode 100644 (file)
index 0000000..b12ee50
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * 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;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.not;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.internal.matchers.StringContains.containsString;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Map;
+
+import org.junit.Test;
+import org.opendaylight.controller.config.yangjmxgenerator.RuntimeBeanEntry;
+import org.opendaylight.controller.config.yangjmxgenerator.RuntimeRegistratorTest;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.FtlFilePersister;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.FtlTemplate;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.RuntimeRegistratorFtlTemplate;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.util.FormattingUtil;
+
+public class RuntimeRegistratorFtlFileTest extends RuntimeRegistratorTest {
+    private final FtlFilePersister ftlFilePersister = new FtlFilePersister();
+
+    @Test
+    public void testRootWithoutAnything() {
+        RuntimeBeanEntry rootRB = prepareRootRB(Collections
+                .<RuntimeBeanEntry> emptyList());
+        Map<String, FtlTemplate> createdFtls = RuntimeRegistratorFtlTemplate
+                .create(rootRB);
+        assertThat(createdFtls.size(), is(2));
+        String rootRegistratorName = RuntimeRegistratorFtlTemplate
+                .getJavaNameOfRuntimeRegistration(rootRB.getJavaNamePrefix());
+        FtlTemplate rootFtlFile = createdFtls.get(rootRegistratorName);
+        assertNotNull(rootFtlFile);
+
+        Map<FtlTemplate, String> serializedFtls = ftlFilePersister
+                .serializeFtls(createdFtls.values());
+        assertThat(serializedFtls.size(), is(2));
+    }
+
+    @Test
+    public void testHierarchy2() {
+        RuntimeBeanEntry grandChildRB = prepareChildRB(
+                Collections.<RuntimeBeanEntry> emptyList(), "grand");
+        RuntimeBeanEntry childRB = prepareChildRB(Arrays.asList(grandChildRB),
+                "");
+        RuntimeBeanEntry rootRB = prepareRootRB(Arrays.asList(childRB));
+
+        Map<String, FtlTemplate> createdFtls = RuntimeRegistratorFtlTemplate
+                .create(rootRB);
+        Map<FtlTemplate, String> serializedFtls = ftlFilePersister
+                .serializeFtls(createdFtls.values());
+        assertThat(serializedFtls.size(), is(4));
+
+        assertThat(
+                findRegistrationOutput(createdFtls, grandChildRB,
+                        serializedFtls), not(containsString(" register(")));
+
+        FtlTemplate registrator = createdFtls.get(RuntimeRegistratorFtlTemplate
+                .getJavaNameOfRuntimeRegistrator(rootRB));
+        FormattingUtil.cleanUpEmptyLinesAndIndent(serializedFtls
+                .get(registrator));
+
+    }
+
+    private String findRegistrationOutput(Map<String, FtlTemplate> createdFtls,
+            RuntimeBeanEntry rb, Map<FtlTemplate, String> serializedFtls) {
+        RuntimeRegistratorFtlTemplate rbFtlFile = (RuntimeRegistratorFtlTemplate) createdFtls
+                .get(RuntimeRegistratorFtlTemplate.getJavaNameOfRuntimeRegistration(rb.getJavaNamePrefix()));
+        assertNotNull(rbFtlFile);
+        String unformatted = serializedFtls.get(rbFtlFile);
+        assertNotNull(unformatted);
+        return FormattingUtil.cleanUpEmptyLinesAndIndent(unformatted);
+    }
+
+}
diff --git a/opendaylight/config/yang-jmx-generator-plugin/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/FtlFilePersisterTest.java b/opendaylight/config/yang-jmx-generator-plugin/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/FtlFilePersisterTest.java
new file mode 100644 (file)
index 0000000..2582a60
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * 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.ftl;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Field;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.MethodDeclaration;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.util.FormattingUtil;
+
+import com.google.common.collect.Lists;
+
+public class FtlFilePersisterTest {
+    private final FtlFilePersister tested = new FtlFilePersister();
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    @Test
+    public void testGeneralInterface() {
+        String packageName = "pa.cka.ge";
+        String name = "GeneralClassImpl";
+        List<String> extendedInterfaces = Arrays.asList("List", "Set");
+        List<MethodDeclaration> methods = new ArrayList<>();
+        methods.add(new MethodDeclaration("String", "executeOperation",
+                Collections.<Field> emptyList()));
+
+        List<String> mods = Lists.newArrayList();
+        List<String> mods2 = Lists.newArrayList("final");
+        methods.add(new MethodDeclaration("String", "executeOperation", Arrays
+                .asList(new Field(mods, "int", "param1"), new Field(mods2, "long", "param2"))));
+
+        GeneralInterfaceTemplate generalInterface = new GeneralInterfaceTemplate(
+                null, packageName, name, extendedInterfaces, methods);
+
+        Map<FtlTemplate, String> abstractFtlFileStringMap = tested
+                .serializeFtls(Arrays.asList(generalInterface));
+        String content = FormattingUtil
+                .cleanUpEmptyLinesAndIndent(abstractFtlFileStringMap.get(generalInterface));
+
+        // skip header
+        content = content.substring(content.indexOf("package"));
+
+        String expected = "package pa.cka.ge;\n"
+                + "/**\n"
+                + "*\n"
+                + "*/\n"
+                + "public interface GeneralClassImpl extends List, Set\n{\n"
+                + "public String executeOperation();\n"
+                + "public String executeOperation(int param1, final long param2);\n"
+                + "}\n";
+
+        assertEquals(expected, content);
+    }
+
+}
diff --git a/opendaylight/config/yang-jmx-generator-plugin/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/util/FormattingUtil.java b/opendaylight/config/yang-jmx-generator-plugin/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/util/FormattingUtil.java
new file mode 100644 (file)
index 0000000..e62cb7c
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * 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.util;
+
+import java.util.Scanner;
+
+public class FormattingUtil {
+
+    public static String cleanUpEmptyLinesAndIndent(String input) {
+        StringBuffer output = new StringBuffer();
+        Scanner scanner = new Scanner(input);
+        while (scanner.hasNextLine()) {
+            String line = scanner.nextLine();
+            line = line.replaceAll("\t", " ");
+            while (line.contains("  ")) {
+                line = line.replaceAll("  ", " ");
+            }
+            line = line.trim();
+            if (line.length() > 0) {
+                output.append(line);
+                output.append("\n");
+            }
+        }
+
+        return output.toString();
+    }
+}
diff --git a/opendaylight/config/yang-jmx-generator/pom.xml b/opendaylight/config/yang-jmx-generator/pom.xml
new file mode 100644 (file)
index 0000000..e89b61e
--- /dev/null
@@ -0,0 +1,109 @@
+<?xml version="1.0"?>
+<project
+        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
+        xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <artifactId>config-subsystem</artifactId>
+        <groupId>org.opendaylight</groupId>
+        <version>0.2.1-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>yang-jmx-generator</artifactId>
+    <name>${project.artifactId}</name>
+    <packaging>bundle</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>binding-generator-util</artifactId>
+            <version>${opendaylight.binding.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>binding-generator-spi</artifactId>
+            <version>${opendaylight.binding.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.opendaylight.bgpcep</groupId>
+            <artifactId>mockito-configuration</artifactId>
+            <version>0.2.0-SNAPSHOT</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-parser-impl</artifactId>
+            <version>${opendaylight.yang.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>config-api</artifactId>
+            <version>0.2.1-SNAPSHOT</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>binding-generator-impl</artifactId>
+            <version>${opendaylight.binding.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Private-Package>
+                            org.opendaylight.controller.config.yangjmxgenerator.plugin.util,
+                        </Private-Package>
+                        <Import-Package>
+                            org.slf4j,
+                            com.google.common.base,
+                            com.google.common.collect,
+                            javax.management.*,
+                        </Import-Package>
+                        <Export-Package>
+                            org.opendaylight.controller.config.yangjmxgenerator,
+                            org.opendaylight.controller.config.yangjmxgenerator.attribute,
+                            <!-- OPENDAYLIGHT NEEDED BY YANG-STORE -->
+                            org.opendaylight.yangtools.binding.generator.util,
+                            org.opendaylight.yangtools.sal.binding.generator.spi,
+                            org.opendaylight.yangtools.sal.binding.model.api,
+                            org.opendaylight.yangtools.yang.binding,
+                            org.opendaylight.yangtools.yang.common,
+                            org.opendaylight.yangtools.yang.model.api,
+                            org.opendaylight.yangtools.yang.model.api.type,
+
+                            org.opendaylight.yangtools.binding.generator.util.generated.type.builder,
+                            org.opendaylight.yangtools.sal.binding.model.api.type.builder,
+                            org.opendaylight.yangtools.yang.binding,
+                        </Export-Package>
+                    </instructions>
+                </configuration>
+            </plugin>
+            <!-- test jar -->
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jar-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/AbstractEntry.java b/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/AbstractEntry.java
new file mode 100644 (file)
index 0000000..0b27218
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * 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;
+
+/**
+ * Base for entries that provides information about their yang source.
+ */
+public class AbstractEntry {
+
+    private String yangModuleName;
+    private String yangModuleLocalname;
+
+    public AbstractEntry() {
+        super();
+    }
+
+    protected void setYangModuleName(String name) {
+        this.yangModuleName = name;
+    }
+
+    public String getYangModuleLocalname() {
+        return yangModuleLocalname;
+    }
+
+    protected void setYangModuleLocalname(String yangModuleLocalname) {
+        this.yangModuleLocalname = yangModuleLocalname;
+    }
+
+    public String getYangModuleName() {
+        return yangModuleName;
+    }
+
+}
diff --git a/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/ConfigConstants.java b/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/ConfigConstants.java
new file mode 100644 (file)
index 0000000..ae06400
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * 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;
+
+import java.net.URI;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import org.opendaylight.yangtools.yang.common.QName;
+
+public class ConfigConstants {
+
+    public static final String CONFIG_NAMESPACE = "urn:opendaylight:params:xml:ns:yang:controller:config";
+
+    public static final String CONFIG_MODULE = "config";
+    public static final String CONFIG_THREADS_MODULE = "config-threads";
+    public static final String IETF_INET_TYPES = "ietf-inet-types";
+
+    public static final QName SERVICE_TYPE_Q_NAME = createConfigQName("service-type");
+    public static final QName MODULE_TYPE_Q_NAME = createConfigQName("module-type");
+    public static final QName JAVA_CLASS_EXTENSION_QNAME = createConfigQName("java-class");
+    public static final QName REQUIRED_IDENTITY_EXTENSION_QNAME = createConfigQName("required-identity");
+    public static final QName INNER_STATE_BEAN_EXTENSION_QNAME = createConfigQName("inner-state-bean");
+    public static final QName PROVIDED_SERVICE_EXTENSION_QNAME = createConfigQName("provided-service");
+    public static final QName JAVA_NAME_PREFIX_EXTENSION_QNAME = createConfigQName("java-name-prefix");
+    public static final QName RPC_CONTEXT_REF_GROUPING_QNAME = createRpcXQName("rpc-context-ref");
+    public static final QName RPC_CONTEXT_REF_GROUPING_LEAF = createRpcXQName("context-instance");
+    public static final QName RPC_CONTEXT_INSTANCE_EXTENSION_QNAME = createRpcXQName("rpc-context-instance");
+
+    public static QName createConfigQName(String localName) {
+        return createQName(CONFIG_NAMESPACE, "2013-04-05", localName);
+    }
+
+    public static QName createRpcXQName(String localName) {
+        return createQName("urn:ietf:params:xml:ns:yang:rpc-context",
+                "2013-06-17", localName);
+    }
+
+    /**
+     *
+     * @param uri
+     * @param revisionDate
+     *            in format yyyy-MM-dd
+     * @param localName
+     * @return
+     */
+    private static QName createQName(String uri, String revisionDate,
+            String localName) {
+        SimpleDateFormat revisionFormat = new SimpleDateFormat("yyyy-MM-dd");
+        Date revision;
+        try {
+            revision = revisionFormat.parse(revisionDate);
+        } catch (ParseException e) {
+            throw new RuntimeException(e);
+        }
+        return new QName(URI.create(uri), revision, localName);
+    }
+
+}
diff --git a/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/ModuleMXBeanEntry.java b/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/ModuleMXBeanEntry.java
new file mode 100644 (file)
index 0000000..5900810
--- /dev/null
@@ -0,0 +1,675 @@
+/*
+ * 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;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+import static java.lang.String.format;
+import static org.opendaylight.controller.config.yangjmxgenerator.ConfigConstants.createConfigQName;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc;
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.DependencyAttribute;
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.JavaAttribute;
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.ListAttribute;
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.TOAttribute;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.util.FullyQualifiedNameHelper;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.util.NameConflictException;
+import org.opendaylight.yangtools.binding.generator.util.BindingGeneratorUtil;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
+import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.ModuleImport;
+import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.UsesNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.Sets;
+
+/**
+ * Represents part of yang model that describes a module.
+ *
+ * Example:
+ * <p>
+ * <blockquote>
+ *
+ * <pre>
+ *  identity threadpool-dynamic {
+ *      base config:module-type;
+ *      description "threadpool-dynamic description";
+ *      config:provided-service "th2:threadpool";
+ *      config:provided-service "th2:scheduled-threadpool";
+ *      config:java-name-prefix DynamicThreadPool
+ *  }
+ *  augment "/config:modules/config:module/config:module-type" {
+ *     case threadpool-dynamic {
+ *         when "/config:modules/config:module/config:module-type = 'threadpool-dynamic'";
+ *
+ *         container "configuration" {
+ *             // regular java attribute
+ *             leaf core-size {
+ *                 type uint32;
+ *          }
+ *
+ *             ...
+ *          // dependency
+ *             container threadfactory {
+ *                 uses config:service-ref {
+ *                     refine type {
+ *                         config:required-identity th:threadfactory;
+ *                  }
+ *              }
+ *          }
+ *      }
+ * }
+ * </pre>
+ *
+ * </blockquote>
+ * </p>
+ */
+public class ModuleMXBeanEntry extends AbstractEntry {
+    private static final Logger logger = LoggerFactory
+            .getLogger(ModuleMXBeanEntry.class);
+
+    // TODO: the XPath should be parsed by code generator IMO
+    private static final String MAGIC_STRING = "MAGIC_STRING";
+    private static final String MODULE_CONDITION_XPATH_TEMPLATE = "^/MAGIC_STRING:modules/MAGIC_STRING:module/MAGIC_STRING:type\\s*=\\s*['\"](.+)['\"]$";
+    private static final SchemaPath expectedConfigurationAugmentationSchemaPath = new SchemaPath(
+            Arrays.asList(createConfigQName("modules"),
+                    createConfigQName("module"),
+                    createConfigQName("configuration")), true);
+    private static final SchemaPath expectedStateAugmentationSchemaPath = new SchemaPath(
+            Arrays.asList(createConfigQName("modules"),
+                    createConfigQName("module"), createConfigQName("state")),
+            true);
+
+    private static final Pattern PREFIX_COLON_LOCAL_NAME = Pattern
+            .compile("^(.+):(.+)$");
+
+    private static final String MODULE_SUFFIX = "Module";
+    private static final String FACTORY_SUFFIX = MODULE_SUFFIX + "Factory";
+    private static final String CLASS_NAME_SUFFIX = MODULE_SUFFIX + "MXBean";
+    private static final String ABSTRACT_PREFIX = "Abstract";
+
+    /*
+     * threadpool-dynamic from the example above, taken from when condition, not
+     * the case name
+     */
+    private final String globallyUniqueName;
+
+    private Map<String, AttributeIfc> yangToAttributes;
+
+    private final String nullableDescription, packageName, javaNamePrefix,
+            namespace;
+
+    private final Map<String, String> providedServices;
+
+    private Collection<RuntimeBeanEntry> runtimeBeans;
+
+    public ModuleMXBeanEntry(IdentitySchemaNode id,
+            Map<String, AttributeIfc> yangToAttributes, String packageName,
+            Map<String, String> providedServices2, String javaNamePrefix,
+            String namespace, Collection<RuntimeBeanEntry> runtimeBeans) {
+        this.globallyUniqueName = id.getQName().getLocalName();
+        this.yangToAttributes = yangToAttributes;
+        this.nullableDescription = id.getDescription();
+        this.packageName = packageName;
+        this.javaNamePrefix = checkNotNull(javaNamePrefix);
+        this.namespace = checkNotNull(namespace);
+        this.providedServices = Collections.unmodifiableMap(providedServices2);
+        this.runtimeBeans = runtimeBeans;
+    }
+
+    public String getMXBeanInterfaceName() {
+        return javaNamePrefix + CLASS_NAME_SUFFIX;
+    }
+
+    public String getStubFactoryName() {
+        return javaNamePrefix + FACTORY_SUFFIX;
+    }
+
+    public String getAbstractFactoryName() {
+        return ABSTRACT_PREFIX + getStubFactoryName();
+    }
+
+    public String getStubModuleName() {
+        return javaNamePrefix + MODULE_SUFFIX;
+    }
+
+    public String getAbstractModuleName() {
+        return ABSTRACT_PREFIX + getStubModuleName();
+    }
+
+    public String getFullyQualifiedName(String typeName) {
+        return FullyQualifiedNameHelper.getFullyQualifiedName(packageName,
+                typeName);
+    }
+
+    public String getGloballyUniqueName() {
+        return globallyUniqueName;
+    }
+
+    public String getPackageName() {
+        return packageName;
+    }
+
+    public Map<String, String> getProvidedServices() {
+        return providedServices;
+    }
+
+    public void setRuntimeBeans(Collection<RuntimeBeanEntry> newRuntimeBeans) {
+        runtimeBeans = newRuntimeBeans;
+    }
+
+    public Collection<RuntimeBeanEntry> getRuntimeBeans() {
+        return runtimeBeans;
+    }
+
+    public String getJavaNamePrefix() {
+        return javaNamePrefix;
+    }
+
+    public String getNamespace() {
+        return namespace;
+    }
+
+    @VisibleForTesting
+    static Matcher getWhenConditionMatcher(String prefix,
+            RevisionAwareXPath whenConstraint) {
+        String xpathRegex = MODULE_CONDITION_XPATH_TEMPLATE.replace(
+                MAGIC_STRING, prefix);
+        Pattern pattern = Pattern.compile(xpathRegex);
+        return pattern.matcher(whenConstraint.toString());
+    }
+
+    static String getConfigModulePrefixFromImport(Module currentModule) {
+        for (ModuleImport currentImport : currentModule.getImports()) {
+            if (currentImport.getModuleName().equals(
+                    ConfigConstants.CONFIG_MODULE)) {
+                return currentImport.getPrefix();
+            }
+        }
+        throw new IllegalArgumentException("Cannot find import "
+                + ConfigConstants.CONFIG_MODULE + " in " + currentModule);
+    }
+
+    /**
+     * Transform module to zero or more ModuleMXBeanEntry instances. Each
+     * instance must have a globally unique local name.
+     *
+     * @return Map of identity local names as keys, and ModuleMXBeanEntry
+     *         instances as values
+     */
+    public static Map<String/* identity local name */, ModuleMXBeanEntry> create(
+            Module currentModule,
+            Map<QName, ServiceInterfaceEntry> qNamesToSIEs,
+            SchemaContext schemaContext,
+            TypeProviderWrapper typeProviderWrapper, String packageName) {
+        Map<String, QName> uniqueGeneratedClassesNames = new HashMap<>();
+        logger.debug("Generating ModuleMXBeans of {} to package {}",
+                currentModule.getNamespace(), packageName);
+        String configModulePrefix;
+        try {
+            configModulePrefix = getConfigModulePrefixFromImport(currentModule);
+        } catch (IllegalArgumentException e) {
+            // this module does not import config module
+            return Collections.emptyMap();
+        }
+
+        // get identities of base config:module-type
+        Map<String, IdentitySchemaNode> moduleIdentities = new HashMap<>();
+
+        for (IdentitySchemaNode id : currentModule.getIdentities()) {
+            if (id.getBaseIdentity() != null
+                    && ConfigConstants.MODULE_TYPE_Q_NAME.equals(id
+                            .getBaseIdentity().getQName())) {
+                String identityLocalName = id.getQName().getLocalName();
+                if (moduleIdentities.containsKey(identityLocalName)) {
+                    throw new IllegalStateException(
+                            "Module name already defined in this module: "
+                                    + identityLocalName);
+                } else {
+                    moduleIdentities.put(identityLocalName, id);
+                    logger.debug("Found identity {}", identityLocalName);
+                }
+                // validation check on unknown schema nodes
+                boolean providedServiceWasSet = false;
+                for (UnknownSchemaNode unknownNode : id.getUnknownSchemaNodes()) {
+                    // TODO: test this
+                    if (ConfigConstants.PROVIDED_SERVICE_EXTENSION_QNAME
+                            .equals(unknownNode.getNodeType())) {
+                        // no op: 0 or more provided identities are allowed
+                    } else if (ConfigConstants.JAVA_NAME_PREFIX_EXTENSION_QNAME
+                            .equals(unknownNode.getNodeType())) {
+                        // 0..1 allowed
+                        checkState(
+                                providedServiceWasSet == false,
+                                format("More than one language extension %s is not allowed here: %s",
+                                        ConfigConstants.JAVA_NAME_PREFIX_EXTENSION_QNAME,
+                                        id));
+                        providedServiceWasSet = true;
+                    } else {
+                        throw new IllegalStateException(
+                                "Unexpected language extension "
+                                        + unknownNode.getNodeType());
+                    }
+                }
+            }
+        }
+        Map<String, ModuleMXBeanEntry> result = new HashMap<>();
+        // each module name should have an augmentation defined
+        Map<String, IdentitySchemaNode> unaugmentedModuleIdentities = new HashMap<>(
+                moduleIdentities);
+        for (AugmentationSchema augmentation : currentModule.getAugmentations()) {
+            Set<DataSchemaNode> childNodes = augmentation.getChildNodes();
+            if (childNodes.size() == 1) {
+                DataSchemaNode when = childNodes.iterator().next();
+                if (when instanceof ChoiceCaseNode) {
+                    ChoiceCaseNode choiceCaseNode = (ChoiceCaseNode) when;
+                    if (choiceCaseNode.getConstraints() == null
+                            || choiceCaseNode.getConstraints()
+                                    .getWhenCondition() == null) {
+                        continue;
+                    }
+                    RevisionAwareXPath xPath = choiceCaseNode.getConstraints()
+                            .getWhenCondition();
+                    Matcher matcher = getWhenConditionMatcher(
+                            configModulePrefix, xPath);
+                    if (matcher.matches() == false) {
+                        continue;
+                    }
+                    String moduleLocalNameFromXPath = matcher.group(1);
+                    IdentitySchemaNode moduleIdentity = moduleIdentities
+                            .get(moduleLocalNameFromXPath);
+                    unaugmentedModuleIdentities
+                            .remove(moduleLocalNameFromXPath);
+                    checkState(moduleIdentity != null, "Cannot find identity "
+                            + moduleLocalNameFromXPath
+                            + " matching augmentation " + augmentation);
+                    Map<String, String> providedServices = findProvidedServices(
+                            moduleIdentity, currentModule, qNamesToSIEs,
+                            schemaContext);
+
+                    if (moduleIdentity == null) {
+                        throw new IllegalStateException(
+                                "Cannot find identity specified by augmentation xpath constraint: "
+                                        + moduleLocalNameFromXPath + " of "
+                                        + augmentation);
+                    }
+                    String javaNamePrefix = findJavaNamePrefix(moduleIdentity);
+
+                    Map<String, AttributeIfc> yangToAttributes = null;
+                    // runtime-data
+                    Collection<RuntimeBeanEntry> runtimeBeans = null;
+
+                    if (expectedConfigurationAugmentationSchemaPath
+                            .equals(augmentation.getTargetPath())) {
+                        logger.debug("Parsing configuration of {}",
+                                moduleLocalNameFromXPath);
+                        yangToAttributes = fillConfiguration(choiceCaseNode,
+                                currentModule, typeProviderWrapper,
+                                qNamesToSIEs, schemaContext);
+                        checkUniqueAttributesWithGeneratedClass(
+                                uniqueGeneratedClassesNames, when.getQName(),
+                                yangToAttributes);
+                    } else if (expectedStateAugmentationSchemaPath
+                            .equals(augmentation.getTargetPath())) {
+                        logger.debug("Parsing state of {}",
+                                moduleLocalNameFromXPath);
+                        try {
+                            runtimeBeans = fillRuntimeBeans(choiceCaseNode,
+                                    currentModule, typeProviderWrapper,
+                                    packageName, moduleLocalNameFromXPath,
+                                    javaNamePrefix);
+                        } catch (NameConflictException e) {
+                            throw new NameConflictException(
+                                    e.getConflictingName(), when.getQName(),
+                                    when.getQName());
+                        }
+
+                        checkUniqueRuntimeBeansGeneratedClasses(
+                                uniqueGeneratedClassesNames, when, runtimeBeans);
+                        Set<RuntimeBeanEntry> runtimeBeanEntryValues = Sets
+                                .newHashSet(runtimeBeans);
+                        for (RuntimeBeanEntry entry : runtimeBeanEntryValues) {
+                            checkUniqueAttributesWithGeneratedClass(
+                                    uniqueGeneratedClassesNames,
+                                    when.getQName(),
+                                    entry.getYangPropertiesToTypesMap());
+                        }
+
+                    } else {
+                        throw new IllegalArgumentException(
+                                "Cannot parse augmentation " + augmentation);
+                    }
+                    if (result.containsKey(moduleLocalNameFromXPath)) {
+                        // either fill runtimeBeans or yangToAttributes
+                        ModuleMXBeanEntry moduleMXBeanEntry = result
+                                .get(moduleLocalNameFromXPath);
+                        if (yangToAttributes != null
+                                && moduleMXBeanEntry.getAttributes() == null) {
+                            moduleMXBeanEntry
+                                    .setYangToAttributes(yangToAttributes);
+                        } else if (runtimeBeans != null
+                                && moduleMXBeanEntry.getRuntimeBeans() == null) {
+                            moduleMXBeanEntry.setRuntimeBeans(runtimeBeans);
+                        }
+                    } else {
+                        // construct ModuleMXBeanEntry
+                        ModuleMXBeanEntry moduleMXBeanEntry = new ModuleMXBeanEntry(
+                                moduleIdentity, yangToAttributes, packageName,
+                                providedServices, javaNamePrefix, currentModule
+                                        .getNamespace().toString(),
+                                runtimeBeans);
+                        moduleMXBeanEntry.setYangModuleName(currentModule
+                                .getName());
+                        moduleMXBeanEntry
+                                .setYangModuleLocalname(moduleLocalNameFromXPath);
+                        result.put(moduleLocalNameFromXPath, moduleMXBeanEntry);
+                    }
+                } // skip if child node is not ChoiceCaseNode
+            } // skip if childNodes != 1
+        }
+        // clean up nulls
+        for (Entry<String, ModuleMXBeanEntry> entry : result.entrySet()) {
+            ModuleMXBeanEntry module = entry.getValue();
+            if (module.getAttributes() == null) {
+                module.setYangToAttributes(Collections
+                        .<String, AttributeIfc> emptyMap());
+            } else if (module.getRuntimeBeans() == null) {
+                module.setRuntimeBeans(Collections
+                        .<RuntimeBeanEntry> emptyList());
+            }
+        }
+        if (unaugmentedModuleIdentities.size() > 0) {
+            logger.warn("Augmentation not found for all module identities: {}",
+                    unaugmentedModuleIdentities.keySet());
+        }
+
+        logger.debug("Number of ModuleMXBeans to be generated: {}",
+                result.size());
+        return result;
+    }
+
+    private static void checkUniqueRuntimeBeansGeneratedClasses(
+            Map<String, QName> uniqueGeneratedClassesNames,
+            DataSchemaNode when, Collection<RuntimeBeanEntry> runtimeBeans) {
+        for (RuntimeBeanEntry runtimeBean : runtimeBeans) {
+            final String javaNameOfRuntimeMXBean = runtimeBean
+                    .getJavaNameOfRuntimeMXBean();
+            if (uniqueGeneratedClassesNames
+                    .containsKey(javaNameOfRuntimeMXBean)) {
+                QName firstDefinedQName = uniqueGeneratedClassesNames
+                        .get(javaNameOfRuntimeMXBean);
+                throw new NameConflictException(javaNameOfRuntimeMXBean,
+                        firstDefinedQName, when.getQName());
+            }
+            uniqueGeneratedClassesNames.put(javaNameOfRuntimeMXBean,
+                    when.getQName());
+        }
+    }
+
+    private static void checkUniqueAttributesWithGeneratedClass(
+            Map<String, QName> uniqueGeneratedClassNames, QName parentQName,
+            Map<String, AttributeIfc> yangToAttributes) {
+        for (Entry<String, AttributeIfc> attr : yangToAttributes.entrySet()) {
+            if (attr.getValue() instanceof TOAttribute) {
+                checkUniqueTOAttr(uniqueGeneratedClassNames, parentQName,
+                        (TOAttribute) attr.getValue());
+            } else if (attr.getValue() instanceof ListAttribute
+                    && ((ListAttribute) attr.getValue()).getInnerAttribute() instanceof TOAttribute) {
+                checkUniqueTOAttr(uniqueGeneratedClassNames, parentQName,
+                        (TOAttribute) ((ListAttribute) attr.getValue())
+                                .getInnerAttribute());
+            }
+        }
+    }
+
+    private static void checkUniqueTOAttr(
+            Map<String, QName> uniqueGeneratedClassNames, QName parentQName,
+            TOAttribute attr) {
+        final String upperCaseCammelCase = attr.getUpperCaseCammelCase();
+        if (uniqueGeneratedClassNames.containsKey(upperCaseCammelCase)) {
+            QName firstDefinedQName = uniqueGeneratedClassNames
+                    .get(upperCaseCammelCase);
+            throw new NameConflictException(upperCaseCammelCase,
+                    firstDefinedQName, parentQName);
+        } else {
+            uniqueGeneratedClassNames.put(upperCaseCammelCase, parentQName);
+        }
+    }
+
+    private static Collection<RuntimeBeanEntry> fillRuntimeBeans(
+            ChoiceCaseNode choiceCaseNode, Module currentModule,
+            TypeProviderWrapper typeProviderWrapper, String packageName,
+            String moduleLocalNameFromXPath, String javaNamePrefix) {
+
+        return RuntimeBeanEntry.extractClassNameToRuntimeBeanMap(packageName,
+                choiceCaseNode, moduleLocalNameFromXPath, typeProviderWrapper,
+                javaNamePrefix, currentModule).values();
+
+    }
+
+    private static Map<String, AttributeIfc> fillConfiguration(
+            ChoiceCaseNode choiceCaseNode, Module currentModule,
+            TypeProviderWrapper typeProviderWrapper,
+            Map<QName, ServiceInterfaceEntry> qNamesToSIEs,
+            SchemaContext schemaContext) {
+        Map<String, AttributeIfc> yangToAttributes = new HashMap<>();
+        for (DataSchemaNode attrNode : choiceCaseNode.getChildNodes()) {
+            AttributeIfc attributeValue = getAttributeValue(attrNode,
+                    currentModule, qNamesToSIEs, typeProviderWrapper,
+                    schemaContext);
+            yangToAttributes.put(attributeValue.getAttributeYangName(),
+                    attributeValue);
+        }
+        return yangToAttributes;
+    }
+
+    private static Map<String, String> findProvidedServices(
+            IdentitySchemaNode moduleIdentity, Module currentModule,
+            Map<QName, ServiceInterfaceEntry> qNamesToSIEs,
+            SchemaContext schemaContext) {
+        Map<String, String> result = new HashMap<>();
+        for (UnknownSchemaNode unknownNode : moduleIdentity
+                .getUnknownSchemaNodes()) {
+            if (ConfigConstants.PROVIDED_SERVICE_EXTENSION_QNAME
+                    .equals(unknownNode.getNodeType())) {
+                String prefixAndIdentityLocalName = unknownNode
+                        .getNodeParameter();
+                ServiceInterfaceEntry sie = findSIE(prefixAndIdentityLocalName,
+                        currentModule, qNamesToSIEs, schemaContext);
+                result.put(sie.getFullyQualifiedName(), sie.getQName()
+                        .getLocalName());
+            }
+        }
+        return result;
+    }
+
+    /**
+     * For input node, find if it contains config:java-name-prefix extension. If
+     * not found, convert local name of node converted to cammel case.
+     */
+    public static String findJavaNamePrefix(SchemaNode schemaNode) {
+        return convertToJavaName(schemaNode, true);
+    }
+
+    public static String findJavaParameter(SchemaNode schemaNode) {
+        return convertToJavaName(schemaNode, false);
+    }
+
+    public static String convertToJavaName(SchemaNode schemaNode,
+            boolean capitalizeFirstLetter) {
+        for (UnknownSchemaNode unknownNode : schemaNode.getUnknownSchemaNodes()) {
+            if (ConfigConstants.JAVA_NAME_PREFIX_EXTENSION_QNAME
+                    .equals(unknownNode.getNodeType())) {
+                String value = unknownNode.getNodeParameter();
+                return convertToJavaName(value, capitalizeFirstLetter);
+            }
+        }
+        return convertToJavaName(schemaNode.getQName().getLocalName(),
+                capitalizeFirstLetter);
+    }
+
+    public static String convertToJavaName(String localName,
+            boolean capitalizeFirstLetter) {
+        if (capitalizeFirstLetter) {
+            return BindingGeneratorUtil.parseToClassName(localName);
+        } else {
+            return BindingGeneratorUtil.parseToValidParamName(localName);
+        }
+    }
+
+    private static int getChildNodeSizeWithoutUses(ContainerSchemaNode csn) {
+        int result = 0;
+        for (DataSchemaNode dsn : csn.getChildNodes()) {
+            if (dsn.isAddedByUses() == false)
+                result++;
+        }
+        return result;
+    }
+
+    private static AttributeIfc getAttributeValue(DataSchemaNode attrNode,
+            Module currentModule,
+            Map<QName, ServiceInterfaceEntry> qNamesToSIEs,
+            TypeProviderWrapper typeProviderWrapper, SchemaContext schemaContext) {
+
+        if (attrNode instanceof LeafSchemaNode) {
+            // simple type
+            LeafSchemaNode leaf = (LeafSchemaNode) attrNode;
+            return new JavaAttribute(leaf, typeProviderWrapper);
+        } else if (attrNode instanceof ContainerSchemaNode) {
+            // reference or TO
+            ContainerSchemaNode containerSchemaNode = (ContainerSchemaNode) attrNode;
+            if (containerSchemaNode.getUses().size() == 1
+                    && getChildNodeSizeWithoutUses(containerSchemaNode) == 0) {
+                // reference
+                UsesNode usesNode = containerSchemaNode.getUses().iterator()
+                        .next();
+                checkState(usesNode.getRefines().size() == 1,
+                        "Unexpected 'refine' child node size of "
+                                + containerSchemaNode);
+                LeafSchemaNode refine = (LeafSchemaNode) usesNode.getRefines()
+                        .values().iterator().next();
+                checkState(refine.getUnknownSchemaNodes().size() == 1,
+                        "Unexpected unknown schema node size of " + refine);
+                UnknownSchemaNode requiredIdentity = refine
+                        .getUnknownSchemaNodes().iterator().next();
+                checkState(
+                        ConfigConstants.REQUIRED_IDENTITY_EXTENSION_QNAME.equals(requiredIdentity
+                                .getNodeType()),
+                        "Unexpected language extension " + requiredIdentity);
+                String prefixAndIdentityLocalName = requiredIdentity
+                        .getNodeParameter();
+                // import should point to a module
+                ServiceInterfaceEntry serviceInterfaceEntry = findSIE(
+                        prefixAndIdentityLocalName, currentModule,
+                        qNamesToSIEs, schemaContext);
+                boolean mandatory = refine.getConstraints().isMandatory();
+                return new DependencyAttribute(attrNode, serviceInterfaceEntry,
+                        mandatory, attrNode.getDescription());
+            } else {
+                return TOAttribute.create(containerSchemaNode,
+                        typeProviderWrapper);
+            }
+        } else if (attrNode instanceof LeafListSchemaNode) {
+            return ListAttribute.create((LeafListSchemaNode) attrNode,
+                    typeProviderWrapper);
+        } else if (attrNode instanceof ListSchemaNode) {
+            return ListAttribute.create((ListSchemaNode) attrNode,
+                    typeProviderWrapper);
+        } else {
+            throw new UnsupportedOperationException(
+                    "Unknown configuration node " + attrNode.toString());
+        }
+    }
+
+    private static ServiceInterfaceEntry findSIE(
+            String prefixAndIdentityLocalName, Module currentModule,
+            Map<QName, ServiceInterfaceEntry> qNamesToSIEs,
+            SchemaContext schemaContext) {
+
+        Matcher m = PREFIX_COLON_LOCAL_NAME.matcher(prefixAndIdentityLocalName);
+        Module foundModule;
+        String localSIName;
+        if (m.matches()) {
+            // if there is a prefix, look for ModuleImport with this prefix. Get
+            // Module from SchemaContext
+            String prefix = m.group(1);
+            ModuleImport moduleImport = findModuleImport(currentModule, prefix);
+            foundModule = schemaContext.findModuleByName(
+                    moduleImport.getModuleName(), moduleImport.getRevision());
+            checkState(
+                    foundModule != null,
+                    format("Module not found in SchemaContext by %s",
+                            moduleImport));
+            localSIName = m.group(2);
+        } else {
+            foundModule = currentModule; // no prefix => SIE is in currentModule
+            localSIName = prefixAndIdentityLocalName;
+        }
+        QName siQName = new QName(foundModule.getNamespace(),
+                foundModule.getRevision(), localSIName);
+        ServiceInterfaceEntry sie = qNamesToSIEs.get(siQName);
+        checkState(sie != null, "Cannot find referenced Service Interface by "
+                + prefixAndIdentityLocalName);
+        return sie;
+    }
+
+    private static ModuleImport findModuleImport(Module module, String prefix) {
+        for (ModuleImport moduleImport : module.getImports()) {
+            if (moduleImport.getPrefix().equals(prefix)) {
+                return moduleImport;
+            }
+        }
+        throw new IllegalStateException(format(
+                "Import not found with prefix %s in %s", prefix, module));
+    }
+
+    public Map<String, AttributeIfc> getAttributes() {
+        return yangToAttributes;
+    }
+
+    private void setYangToAttributes(Map<String, AttributeIfc> newAttributes) {
+        this.yangToAttributes = newAttributes;
+
+    }
+
+    public String getNullableDescription() {
+        return nullableDescription;
+    }
+
+    @Override
+    public String toString() {
+        return "ModuleMXBeanEntry{" + "globallyUniqueName='"
+                + globallyUniqueName + '\'' + ", packageName='" + packageName
+                + '\'' + '}';
+    }
+}
diff --git a/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/PackageTranslator.java b/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/PackageTranslator.java
new file mode 100644 (file)
index 0000000..a6a1ac7
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * 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;
+
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.opendaylight.yangtools.binding.generator.util.BindingGeneratorUtil;
+import org.opendaylight.yangtools.yang.model.api.Module;
+
+/**
+ * Maps from module namespaces to java package names using a Map<String,
+ * String>, where key is namespace prefix and value is package that replaces
+ * matched prefix.
+ */
+public class PackageTranslator {
+    private final Map<String, String> namespacePrefixToPackageMap;
+
+    public PackageTranslator(Map<String, String> namespacePrefixToPackageMap) {
+        this.namespacePrefixToPackageMap = namespacePrefixToPackageMap;
+    }
+
+    /**
+     * Based on mapping, find longest matching key and return value plus the
+     * remaining part of namespace, with colons replaced by dots. Example:
+     * Mapping [ 'urn:opendaylight:params:xml:ns:yang:controller' :
+     * 'org.opendaylight.controller'] and module with namespace
+     * 'urn:opendaylight:params:xml:ns:yang:controller:threads:api' will result
+     * in 'org.opendaylight.controller.threads.api' .
+     *
+     * @throws IllegalStateException
+     *             if there is no mapping found.
+     */
+    public String getPackageName(Module module) {
+        Entry<String, String> longestMatch = null;
+        int longestMatchLength = 0;
+        String namespace = module.getNamespace().toString();
+        for (Entry<String, String> entry : namespacePrefixToPackageMap
+                .entrySet()) {
+            if (namespace.startsWith(entry.getKey())
+                    && entry.getKey().length() > longestMatchLength) {
+                longestMatch = entry;
+                longestMatchLength = entry.getKey().length();
+            }
+        }
+        if (longestMatch != null) {
+            return longestMatch.getValue()
+                    + sanitizePackage(namespace.substring(longestMatchLength));
+        } else {
+            return BindingGeneratorUtil.moduleNamespaceToPackageName(module);
+        }
+    }
+
+    // TODO add to PackageTranslator
+    private static String sanitizePackage(String namespace) {
+        namespace = namespace.replace("://", ".");
+        namespace = namespace.replace("/", ".");
+        namespace = namespace.replace(":", ".");
+        namespace = namespace.replace("-", "_");
+        namespace = namespace.replace("@", ".");
+        namespace = namespace.replace("$", ".");
+        namespace = namespace.replace("#", ".");
+        namespace = namespace.replace("'", ".");
+        namespace = namespace.replace("*", ".");
+        namespace = namespace.replace("+", ".");
+        namespace = namespace.replace(",", ".");
+        namespace = namespace.replace(";", ".");
+        namespace = namespace.replace("=", ".");
+        return namespace;
+    }
+}
diff --git a/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/RuntimeBeanEntry.java b/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/RuntimeBeanEntry.java
new file mode 100644 (file)
index 0000000..6d1eca1
--- /dev/null
@@ -0,0 +1,500 @@
+/*
+ * 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;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc;
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.JavaAttribute;
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.ListAttribute;
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.TOAttribute;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.util.FullyQualifiedNameHelper;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.util.NameConflictException;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.UsesNode;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Optional;
+import com.google.common.collect.Lists;
+
+/**
+ * Holds information about runtime bean to be generated. There are two kinds of
+ * RuntimeBeanEntry instances: if isRoot flag is set to true, this bean
+ * represents state that must be present at time of configuration module
+ * instantiation. Root RB must have depthLevel set to 0 and cannot have
+ * children. There might be other RBs defined in yang, but no other RB can have
+ * isRoot set to true. At least one RB must be root and all other RBs must be
+ * lined via children so that a tree with all beans can be created.
+ */
+public class RuntimeBeanEntry {
+    private final String packageName;
+    private final String yangName, javaNamePrefix;
+    private final boolean isRoot;
+    private final Optional<String> keyYangName, keyJavaName;
+    private final Map<String, AttributeIfc> attributeMap;
+    private final List<RuntimeBeanEntry> children;
+    private final Set<Rpc> rpcs;
+
+    @VisibleForTesting
+    public RuntimeBeanEntry(String packageName,
+            DataSchemaNode nodeForReporting, String yangName,
+            String javaNamePrefix, boolean isRoot,
+            Optional<String> keyYangName, List<AttributeIfc> attributes,
+            List<RuntimeBeanEntry> children, Set<Rpc> rpcs) {
+
+        checkArgument(isRoot == false || keyYangName.isPresent() == false,
+                "Root RuntimeBeanEntry must not have key " + "set");
+        this.packageName = packageName;
+        this.isRoot = isRoot;
+        this.yangName = yangName;
+        this.javaNamePrefix = javaNamePrefix;
+        this.children = Collections.unmodifiableList(children);
+        this.rpcs = Collections.unmodifiableSet(rpcs);
+
+        this.keyYangName = keyYangName;
+        Map<String, AttributeIfc> map = new HashMap<>();
+
+        for (AttributeIfc a : attributes) {
+            checkState(map.containsKey(a.getAttributeYangName()) == false,
+                    "Attribute already defined: " + a.getAttributeYangName()
+                            + " in " + nodeForReporting);
+            map.put(a.getAttributeYangName(), a);
+        }
+
+        if (keyYangName.isPresent()) {
+            AttributeIfc keyJavaName = map.get(keyYangName.get());
+            checkArgument(keyJavaName != null, "Key " + keyYangName.get()
+                    + " not found in attribute " + "list " + attributes
+                    + " in " + nodeForReporting);
+            this.keyJavaName = Optional
+                    .of(keyJavaName.getUpperCaseCammelCase());
+        } else {
+            keyJavaName = Optional.absent();
+        }
+        attributeMap = Collections.unmodifiableMap(map);
+    }
+
+    /**
+     * @return map containing all class names as key, extracted RuntimeBeans as
+     *         values. If more than zero values is returned, exactly one
+     *         RuntimeBeanEntry will have isRoot set to true, even if yang does
+     *         not contain special configuration for it.
+     */
+    public static Map<String, RuntimeBeanEntry> extractClassNameToRuntimeBeanMap(
+            String packageName, ChoiceCaseNode container,
+            String moduleYangName, TypeProviderWrapper typeProviderWrapper,
+            String javaNamePrefix, Module currentModule) {
+
+        Map<QName, Set<RpcDefinition>> identitiesToRpcs = getIdentitiesToRpcs(currentModule);
+
+        AttributesRpcsAndRuntimeBeans attributesRpcsAndRuntimeBeans = extractSubtree(
+                packageName, container, typeProviderWrapper, currentModule,
+                identitiesToRpcs);
+        Map<String, RuntimeBeanEntry> result = new HashMap<>();
+
+        List<AttributeIfc> attributes;
+        Set<Rpc> rpcs;
+        if (attributesRpcsAndRuntimeBeans.isEmpty() == false) {
+            attributes = attributesRpcsAndRuntimeBeans.getAttributes();
+            rpcs = attributesRpcsAndRuntimeBeans.getRpcs();
+        } else {
+            // create artificial root if not defined in yang
+            attributes = Collections.emptyList();
+            rpcs = Collections.emptySet();
+        }
+        RuntimeBeanEntry rootRuntimeBeanEntry = createRoot(packageName,
+                container, moduleYangName, attributes, javaNamePrefix,
+                attributesRpcsAndRuntimeBeans.getRuntimeBeanEntries(), rpcs);
+
+        Deque<RuntimeBeanEntry> stack = new LinkedList<>();
+        stack.add(rootRuntimeBeanEntry);
+
+        while (stack.isEmpty() == false) {
+            RuntimeBeanEntry first = stack.pollFirst();
+            if (result.containsKey(first.getJavaNameOfRuntimeMXBean())) {
+                throw new NameConflictException(
+                        first.getJavaNameOfRuntimeMXBean(), null, null);
+            }
+            result.put(first.getJavaNameOfRuntimeMXBean(), first);
+            stack.addAll(first.getChildren());
+        }
+        return result;
+    }
+
+    private static Map<QName/* of identity */, Set<RpcDefinition>> getIdentitiesToRpcs(
+            Module currentModule) {
+        // currently only looks for local identities (found in currentModule)
+        Map<QName, Set<RpcDefinition>> result = new HashMap<>();
+        for (IdentitySchemaNode identity : currentModule.getIdentities()) {
+            // add all
+            result.put(identity.getQName(), new HashSet<RpcDefinition>());
+        }
+
+        for (RpcDefinition rpc : currentModule.getRpcs()) {
+            ContainerSchemaNode input = rpc.getInput();
+            for (UsesNode uses : input.getUses()) {
+
+                if (uses.getGroupingPath().getPath().size() != 1)
+                    continue;
+
+                // check grouping path
+                QName qname = uses.getGroupingPath().getPath().get(0);
+                if (false == qname
+                        .equals(ConfigConstants.RPC_CONTEXT_REF_GROUPING_QNAME))
+                    continue;
+
+                for (SchemaNode refinedNode : uses.getRefines().values()) {
+
+                    for (UnknownSchemaNode unknownSchemaNode : refinedNode
+                            .getUnknownSchemaNodes()) {
+                        if (ConfigConstants.RPC_CONTEXT_INSTANCE_EXTENSION_QNAME
+                                .equals(unknownSchemaNode.getNodeType())) {
+                            String localIdentityName = unknownSchemaNode
+                                    .getNodeParameter();
+                            QName identityQName = new QName(
+                                    currentModule.getNamespace(),
+                                    currentModule.getRevision(),
+                                    localIdentityName);
+                            Set<RpcDefinition> rpcDefinitions = result
+                                    .get(identityQName);
+                            if (rpcDefinitions == null) {
+                                throw new IllegalArgumentException(
+                                        "Identity referenced by rpc not found. Identity:"
+                                                + localIdentityName + " , rpc "
+                                                + rpc);
+                            }
+                            rpcDefinitions.add(rpc);
+                        }
+                    }
+
+                }
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Get direct descendants of this subtree, together with attributes defined
+     * in subtree.
+     */
+    private static AttributesRpcsAndRuntimeBeans extractSubtree(
+            String packageName, DataNodeContainer subtree,
+            TypeProviderWrapper typeProviderWrapper, Module currentModule,
+            Map<QName, Set<RpcDefinition>> identitiesToRpcs) {
+
+        List<AttributeIfc> attributes = Lists.newArrayList();
+        // List<JavaAttribute> javaAttributes = new ArrayList<>();
+        // List<TOAttribute> toAttributes = new ArrayList<>();
+        List<RuntimeBeanEntry> runtimeBeanEntries = new ArrayList<>();
+        for (DataSchemaNode child : subtree.getChildNodes()) {
+            // child leaves can be java attributes, TO attributes, or child
+            // runtime beans
+            if (child instanceof LeafSchemaNode) {
+                // just save the attribute
+                LeafSchemaNode leaf = (LeafSchemaNode) child;
+                attributes.add(new JavaAttribute(leaf, typeProviderWrapper));
+            } else if (child instanceof ContainerSchemaNode) {
+                ContainerSchemaNode container = (ContainerSchemaNode) child;
+                // this can be either TO or hierarchical RB
+                TOAttribute toAttribute = TOAttribute.create(container,
+                        typeProviderWrapper);
+                attributes.add(toAttribute);
+            } else if (child instanceof ListSchemaNode) {
+                if (isInnerStateBean(child)) {
+                    ListSchemaNode listSchemaNode = (ListSchemaNode) child;
+                    RuntimeBeanEntry hierarchicalChild = createHierarchical(
+                            packageName, listSchemaNode, typeProviderWrapper,
+                            currentModule, identitiesToRpcs);
+                    runtimeBeanEntries.add(hierarchicalChild);
+                } else /* ordinary list attribute */{
+                    ListAttribute listAttribute = ListAttribute.create(
+                            (ListSchemaNode) child, typeProviderWrapper);
+                    attributes.add(listAttribute);
+                }
+
+            } else {
+                throw new IllegalStateException("Unknown running-data node "
+                        + child + " , " + "" + "expected leaf or container");
+            }
+        }
+        Set<Rpc> rpcs = new HashSet<>();
+        SchemaNode subtreeSchemaNode = (SchemaNode) subtree;
+        for (UnknownSchemaNode unknownSchemaNode : subtreeSchemaNode
+                .getUnknownSchemaNodes()) {
+            if (ConfigConstants.RPC_CONTEXT_INSTANCE_EXTENSION_QNAME
+                    .equals(unknownSchemaNode.getNodeType())) {
+                String localIdentityName = unknownSchemaNode.getNodeParameter();
+                QName identityQName = new QName(currentModule.getNamespace(),
+                        currentModule.getRevision(), localIdentityName);
+                Set<RpcDefinition> rpcDefinitions = identitiesToRpcs
+                        .get(identityQName);
+                if (rpcDefinitions == null) {
+                    throw new IllegalArgumentException("Cannot find identity "
+                            + localIdentityName + " to be used as "
+                            + "context reference when resolving "
+                            + unknownSchemaNode);
+                }
+                // convert RpcDefinition to Rpc
+                for (RpcDefinition rpcDefinition : rpcDefinitions) {
+                    String name = ModuleMXBeanEntry
+                            .findJavaParameter(rpcDefinition);
+                    String returnType;
+                    if (rpcDefinition.getOutput() == null
+                            || rpcDefinition.getOutput().getChildNodes().size() == 0) {
+                        returnType = "void";
+                    } else if (rpcDefinition.getOutput().getChildNodes().size() == 1) {
+                        DataSchemaNode returnDSN = rpcDefinition.getOutput()
+                                .getChildNodes().iterator().next();
+                        checkArgument(
+                                returnDSN instanceof LeafSchemaNode,
+                                "Unexpected type of rpc return type. "
+                                        + "Currently only leafs and empty output nodes are supported, got "
+                                        + returnDSN);
+                        LeafSchemaNode returnLeaf = (LeafSchemaNode) returnDSN;
+                        // We currently expect leaf defined in output element in yang to be named result
+                        // FIXME: value of result is fully qualified name - should be extended to accept TOs
+                        String localName = returnLeaf.getQName().getLocalName();
+                        checkArgument(
+                                localName.equals("result"),
+                                "Unexpected name of leaf in output element, expected leaf named result, was %s at %s",
+                                localName, currentModule.getName());
+
+                        returnType = typeProviderWrapper.getType(returnLeaf)
+                                .getFullyQualifiedName();
+                    } else {
+                        throw new IllegalArgumentException(
+                                "More than one child node in rpc output is not supported. "
+                                        + "Error occured in " + rpcDefinition);
+                    }
+                    List<JavaAttribute> parameters = new ArrayList<>();
+                    for (DataSchemaNode childNode : rpcDefinition.getInput()
+                            .getChildNodes()) {
+                        if (childNode.isAddedByUses() == false) { // skip
+                                                                  // refined
+                                                                  // context-instance
+                            checkArgument(childNode instanceof LeafSchemaNode, "Unexpected type of rpc input type. "
+                                    + "Currently only leafs and empty output nodes are supported, got " + childNode);
+                            JavaAttribute javaAttribute = new JavaAttribute(
+                                    (LeafSchemaNode) childNode,
+                                    typeProviderWrapper);
+                            parameters.add(javaAttribute);
+                        }
+                    }
+                    Rpc newRpc = new Rpc(returnType, name, rpcDefinition
+                            .getQName().getLocalName(), parameters);
+                    rpcs.add(newRpc);
+                }
+            }
+        }
+        return new AttributesRpcsAndRuntimeBeans(runtimeBeanEntries,
+                attributes, rpcs);
+    }
+
+    private static boolean isInnerStateBean(DataSchemaNode child) {
+        for (UnknownSchemaNode unknownSchemaNode : child
+                .getUnknownSchemaNodes()) {
+            if (unknownSchemaNode.getNodeType().equals(
+                    ConfigConstants.INNER_STATE_BEAN_EXTENSION_QNAME))
+                return true;
+        }
+        return false;
+    }
+
+    private static RuntimeBeanEntry createHierarchical(String packageName,
+            ListSchemaNode listSchemaNode,
+            TypeProviderWrapper typeProviderWrapper, Module currentModule,
+            Map<QName, Set<RpcDefinition>> identitiesToRpcs) {
+
+        // supported are numeric types, strings, enums
+        // get all attributes
+        AttributesRpcsAndRuntimeBeans attributesRpcsAndRuntimeBeans = extractSubtree(
+                packageName, listSchemaNode, typeProviderWrapper,
+                currentModule, identitiesToRpcs);
+
+        Optional<String> keyYangName;
+        if (listSchemaNode.getKeyDefinition().size() == 0) {
+            keyYangName = Optional.absent();
+        } else if (listSchemaNode.getKeyDefinition().size() == 1) {
+            // key must be either null or one of supported key types
+            QName keyQName = listSchemaNode.getKeyDefinition().iterator()
+                    .next();
+            keyYangName = Optional.of(keyQName.getLocalName());
+
+        } else {
+            throw new IllegalArgumentException(
+                    "More than one key is not supported in " + listSchemaNode);
+        }
+
+        String javaNamePrefix = ModuleMXBeanEntry
+                .findJavaNamePrefix(listSchemaNode);
+
+        RuntimeBeanEntry rbFromAttributes = new RuntimeBeanEntry(packageName,
+                listSchemaNode, listSchemaNode.getQName().getLocalName(),
+                javaNamePrefix, false, keyYangName,
+                attributesRpcsAndRuntimeBeans.getAttributes(),
+                attributesRpcsAndRuntimeBeans.getRuntimeBeanEntries(),
+                attributesRpcsAndRuntimeBeans.getRpcs());
+
+        return rbFromAttributes;
+    }
+
+    private static RuntimeBeanEntry createRoot(String packageName,
+            DataSchemaNode nodeForReporting, String attributeYangName,
+            List<AttributeIfc> attributes, String javaNamePrefix,
+            List<RuntimeBeanEntry> children, Set<Rpc> rpcs) {
+        return new RuntimeBeanEntry(packageName, nodeForReporting,
+                attributeYangName, javaNamePrefix, true,
+                Optional.<String> absent(), attributes, children, rpcs);
+    }
+
+    public boolean isRoot() {
+        return isRoot;
+    }
+
+    public Optional<String> getKeyYangName() {
+        return keyYangName;
+    }
+
+    public Optional<String> getKeyJavaName() {
+        return keyJavaName;
+    }
+
+    public Collection<AttributeIfc> getAttributes() {
+        return attributeMap.values();
+    }
+
+    public Map<String, AttributeIfc> getYangPropertiesToTypesMap() {
+        return attributeMap;
+    }
+
+    public String getYangName() {
+        return yangName;
+    }
+
+    public String getPackageName() {
+        return packageName;
+    }
+
+    public String getJavaNamePrefix() {
+        return javaNamePrefix;
+    }
+
+    public List<RuntimeBeanEntry> getChildren() {
+        return children;
+    }
+
+    public Set<Rpc> getRpcs() {
+        return rpcs;
+    }
+
+    private static class AttributesRpcsAndRuntimeBeans {
+        private final List<RuntimeBeanEntry> runtimeBeanEntries;
+        private final List<AttributeIfc> attributes;
+        private final Set<Rpc> rpcs;
+
+        public AttributesRpcsAndRuntimeBeans(
+                List<RuntimeBeanEntry> runtimeBeanEntries,
+                List<AttributeIfc> attributes, Set<Rpc> rpcs) {
+            this.runtimeBeanEntries = runtimeBeanEntries;
+            this.attributes = attributes;
+            this.rpcs = rpcs;
+        }
+
+        private List<AttributeIfc> getAttributes() {
+            return attributes;
+        }
+
+        public List<RuntimeBeanEntry> getRuntimeBeanEntries() {
+            return runtimeBeanEntries;
+        }
+
+        public boolean isEmpty() {
+            return attributes.isEmpty() && rpcs.isEmpty();
+        }
+
+        private Set<Rpc> getRpcs() {
+            return rpcs;
+        }
+    }
+
+    public static class Rpc {
+        private final String name;
+        private final List<JavaAttribute> parameters;
+        private final String returnType;
+        private final String yangName;
+
+        Rpc(String returnType, String name, String yangName,
+                List<JavaAttribute> parameters) {
+            this.returnType = returnType;
+            this.name = name;
+            this.parameters = parameters;
+            this.yangName = yangName;
+        }
+
+        public String getYangName() {
+            return yangName;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public List<JavaAttribute> getParameters() {
+            return parameters;
+        }
+
+        public String getReturnType() {
+            return returnType;
+        }
+    }
+
+    private static final String MXBEAN_SUFFIX = "RuntimeMXBean";
+
+    public String getJavaNameOfRuntimeMXBean() {
+        return getJavaNameOfRuntimeMXBean(javaNamePrefix);
+    }
+
+    public String getFullyQualifiedName(String typeName) {
+        return FullyQualifiedNameHelper.getFullyQualifiedName(packageName,
+                typeName);
+    }
+
+    private static String getJavaNameOfRuntimeMXBean(String javaNamePrefix) {
+        return javaNamePrefix + MXBEAN_SUFFIX;
+    }
+
+    @Override
+    public String toString() {
+        return "RuntimeBeanEntry{" + "isRoot=" + isRoot + ", yangName='"
+                + yangName + '\'' + ", packageName='" + packageName + '\''
+                + ", keyYangName=" + keyYangName + '}';
+    }
+}
diff --git a/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/ServiceInterfaceEntry.java b/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/ServiceInterfaceEntry.java
new file mode 100644 (file)
index 0000000..71cd090
--- /dev/null
@@ -0,0 +1,237 @@
+/*
+ * 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;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static java.lang.String.format;
+import static org.opendaylight.controller.config.yangjmxgenerator.ConfigConstants.SERVICE_TYPE_Q_NAME;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Optional;
+
+/**
+ * Represents identity derived from {@link ConfigConstants#SERVICE_TYPE_Q_NAME}.
+ * Example:
+ * <p>
+ * <blockquote>
+ *
+ * <pre>
+ *  identity eventbus {
+ *  description
+ *  "Service representing an event bus. The service acts as message
+ *  router between event producers and event consumers";
+ *
+ *  base "config:service-type";
+ *  config:java-class "com.google.common.eventbus.EventBus";
+ *  }
+ * </pre>
+ *
+ * </blockquote>
+ * </p>
+ */
+public class ServiceInterfaceEntry extends AbstractEntry {
+    private static final Logger logger = LoggerFactory
+            .getLogger(ServiceInterfaceEntry.class);
+
+    private static final String CLASS_NAME_SUFFIX = "ServiceInterface";
+    private final Optional<ServiceInterfaceEntry> maybeBaseCache;
+    private final String exportedOsgiClassName;
+    private final QName qName;
+    private final String nullableDescription, packageName, typeName;
+
+    private ServiceInterfaceEntry(IdentitySchemaNode id, String packageName) {
+        this(Optional.<ServiceInterfaceEntry> absent(), id, packageName);
+    }
+
+    private ServiceInterfaceEntry(Optional<ServiceInterfaceEntry> base,
+            IdentitySchemaNode id, String packageName) {
+        checkNotNull(base);
+        this.maybeBaseCache = base;
+        List<UnknownSchemaNode> unknownSchemaNodes = id.getUnknownSchemaNodes();
+        List<String> exportedOsgiClassNames = new ArrayList<>(
+                unknownSchemaNodes.size());
+        for (UnknownSchemaNode usn : unknownSchemaNodes) {
+            if (ConfigConstants.JAVA_CLASS_EXTENSION_QNAME.equals(usn
+                    .getNodeType())) {
+                String localName = usn.getNodeParameter();
+                exportedOsgiClassNames.add(localName);
+            } else {
+                throw new IllegalStateException(format(
+                        "Unexpected unknown schema node. Expected %s, got %s",
+                        ConfigConstants.JAVA_CLASS_EXTENSION_QNAME,
+                        usn.getNodeType()));
+            }
+        }
+        if (exportedOsgiClassNames.size() != 1) {
+            throw new IllegalArgumentException(
+                    format("Cannot find one to one mapping from %s to "
+                            + "java class defined by %s language extension in %s",
+                            getClass(),
+                            ConfigConstants.JAVA_CLASS_EXTENSION_QNAME, id));
+        }
+        this.exportedOsgiClassName = exportedOsgiClassNames.get(0);
+        qName = id.getQName();
+        nullableDescription = id.getDescription();
+        typeName = getSimpleName(exportedOsgiClassName) + CLASS_NAME_SUFFIX;
+        this.packageName = packageName;
+    }
+
+    private static final String getSimpleName(String fullyQualifiedName) {
+        int lastDotPosition = fullyQualifiedName.lastIndexOf(".");
+        return fullyQualifiedName.substring(lastDotPosition + 1);
+    }
+
+    public String getNullableDescription() {
+        return nullableDescription;
+    }
+
+    public Optional<ServiceInterfaceEntry> getBase() {
+        return maybeBaseCache;
+    }
+
+    public String getExportedOsgiClassName() {
+        return exportedOsgiClassName;
+    }
+
+    public QName getQName() {
+        return qName;
+    }
+
+    /**
+     * @return Map of QNames as keys and ServiceInterfaceEntry instances as
+     *         values
+     */
+    public static Map<QName, ServiceInterfaceEntry> create(Module module,
+            String packageName) {
+        logger.debug("Generating ServiceInterfaces from {} to package {}",
+                module.getNamespace(), packageName);
+
+        Map<IdentitySchemaNode, ServiceInterfaceEntry> identitiesToSIs = new HashMap<>();
+        Set<IdentitySchemaNode> notVisited = new HashSet<>(
+                module.getIdentities());
+        int lastSize = notVisited.size() + 1;
+        while (notVisited.size() > 0) {
+            if (notVisited.size() == lastSize) {
+                logger.debug(
+                        "Following identities will be ignored while generating ServiceInterfaces, as they are not derived from {} : {}",
+                        SERVICE_TYPE_Q_NAME, notVisited);
+                break;
+            }
+            lastSize = notVisited.size();
+            for (Iterator<IdentitySchemaNode> iterator = notVisited.iterator(); iterator
+                    .hasNext();) {
+                IdentitySchemaNode identity = iterator.next();
+                ServiceInterfaceEntry created = null;
+                if (identity.getBaseIdentity() == null) {
+                    // this can happen while loading config module, just skip
+                    // the identity
+                    continue;
+                } else if (identity.getBaseIdentity().getQName()
+                        .equals(SERVICE_TYPE_Q_NAME)) {
+                    // this is a base type
+                    created = new ServiceInterfaceEntry(identity, packageName);
+                } else {
+                    ServiceInterfaceEntry foundBase = identitiesToSIs
+                            .get(identity.getBaseIdentity());
+                    // derived type, did we convert the parent?
+                    if (foundBase != null) {
+                        created = new ServiceInterfaceEntry(
+                                Optional.of(foundBase), identity, packageName);
+                    }
+                }
+                if (created != null) {
+                    created.setYangModuleName(module.getName());
+                    // TODO how to get local name
+                    created.setYangModuleLocalname(identity.getQName()
+                            .getLocalName());
+                    identitiesToSIs.put(identity, created);
+                    iterator.remove();
+                }
+            }
+        }
+        // create result map
+        Map<QName, ServiceInterfaceEntry> resultMap = new HashMap<>();
+        for (ServiceInterfaceEntry sie : identitiesToSIs.values()) {
+            resultMap.put(sie.getQName(), sie);
+        }
+        logger.debug("Number of ServiceInterfaces to be generated: {}",
+                resultMap.size());
+        return resultMap;
+    }
+
+    public String getFullyQualifiedName() {
+        return packageName + "." + typeName;
+    }
+
+    public String getPackageName() {
+        return packageName;
+    }
+
+    public String getTypeName() {
+        return typeName;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o)
+            return true;
+        if (o == null || getClass() != o.getClass())
+            return false;
+
+        ServiceInterfaceEntry that = (ServiceInterfaceEntry) o;
+
+        if (!maybeBaseCache.equals(that.maybeBaseCache))
+            return false;
+        if (!nullableDescription.equals(that.nullableDescription))
+            return false;
+        if (!exportedOsgiClassName.equals(that.exportedOsgiClassName))
+            return false;
+        if (!qName.equals(that.qName))
+            return false;
+        if (!packageName.equals(that.packageName))
+            return false;
+        if (!typeName.equals(that.typeName))
+            return false;
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = maybeBaseCache.hashCode();
+        result = 31 * result + exportedOsgiClassName.hashCode();
+        result = 31 * result + nullableDescription.hashCode();
+        result = 31 * result + typeName.hashCode();
+        result = 31 * result + packageName.hashCode();
+        result = 31 * result + qName.hashCode();
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        return "ServiceInterfaceEntry{" + "maybeBaseCache=" + maybeBaseCache
+                + ", qName='" + qName + '\'' + ", fullyQualifiedName='"
+                + getFullyQualifiedName() + '\'' + ", exportedOsgiClassName="
+                + exportedOsgiClassName + ", nullableDescription='"
+                + nullableDescription + '\'' + '}';
+    }
+}
diff --git a/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/TypeProviderWrapper.java b/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/TypeProviderWrapper.java
new file mode 100644 (file)
index 0000000..7490ad6
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * 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;
+
+import org.opendaylight.yangtools.sal.binding.generator.spi.TypeProvider;
+import org.opendaylight.yangtools.sal.binding.model.api.Type;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+
+public class TypeProviderWrapper {
+    private final TypeProvider typeProvider;
+
+    public TypeProviderWrapper(TypeProvider typeProvider) {
+        this.typeProvider = typeProvider;
+    }
+
+    public Type getType(LeafSchemaNode leaf) {
+        Type javaType;
+        try {
+            javaType = typeProvider.javaTypeForSchemaDefinitionType(
+                    leaf.getType(), leaf);
+            if (javaType == null)
+                throw new IllegalArgumentException("Unknown type received for "
+                        + leaf.toString());
+        } catch (IllegalArgumentException e) {
+            throw new IllegalArgumentException("Error while resolving type of "
+                    + leaf, e);
+        }
+        return javaType;
+    }
+
+    // there is no getType in common interface
+    public Type getType(LeafListSchemaNode leaf) {
+        Type javaType;
+        try {
+            javaType = typeProvider.javaTypeForSchemaDefinitionType(
+                    leaf.getType(), leaf);
+            if (javaType == null)
+                throw new IllegalArgumentException(
+                        "Unknown type received for  " + leaf.toString());
+        } catch (IllegalArgumentException e) {
+            throw new IllegalArgumentException("Error while resolving type of "
+                    + leaf, e);
+        }
+        return javaType;
+    }
+
+}
diff --git a/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/AbstractAttribute.java b/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/AbstractAttribute.java
new file mode 100644 (file)
index 0000000..5ffc192
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.config.yangjmxgenerator.attribute;
+
+import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+
+public abstract class AbstractAttribute implements AttributeIfc {
+    private final String attributeYangName, upperCaseCammelCase,
+            lowerCaseCammelCase;
+    private final DataSchemaNode node;
+
+    private static String getLocalName(DataSchemaNode attrNode) {
+        return attrNode.getQName().getLocalName();
+    }
+
+    AbstractAttribute(DataSchemaNode attrNode) {
+        this.attributeYangName = getLocalName(attrNode);
+        this.node = attrNode;
+        this.upperCaseCammelCase = ModuleMXBeanEntry.findJavaNamePrefix(node);
+        this.lowerCaseCammelCase = ModuleMXBeanEntry.findJavaParameter(node);
+    }
+
+    @Override
+    public String getAttributeYangName() {
+        return attributeYangName;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o)
+            return true;
+        if (!(o instanceof AbstractAttribute))
+            return false;
+
+        AbstractAttribute that = (AbstractAttribute) o;
+
+        if (attributeYangName != null ? !attributeYangName
+                .equals(that.attributeYangName)
+                : that.attributeYangName != null)
+            return false;
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        return attributeYangName != null ? attributeYangName.hashCode() : 0;
+    }
+
+    /**
+     *
+     * @return Yang name converted to cammel case, starting with a capital
+     *         letter. For details see
+     *         {@link ModuleMXBeanEntry#findJavaNamePrefix(org.opendaylight.yangtools.yang.model.api.SchemaNode)}
+     */
+    @Override
+    public String getUpperCaseCammelCase() {
+        return upperCaseCammelCase;
+    }
+
+    public String getLowerCaseCammelCase() {
+        return lowerCaseCammelCase;
+    }
+}
diff --git a/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/AttributeIfc.java b/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/AttributeIfc.java
new file mode 100644 (file)
index 0000000..23985b2
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.config.yangjmxgenerator.attribute;
+
+import javax.management.openmbean.OpenType;
+
+public interface AttributeIfc {
+
+    /**
+     * Name of attribute, starting with low case
+     */
+    String getAttributeYangName();
+
+    String getNullableDescription();
+
+    String getNullableDefault();
+
+    String getUpperCaseCammelCase();
+
+    String getLowerCaseCammelCase();
+
+    OpenType<?> getOpenType();
+}
diff --git a/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/DependencyAttribute.java b/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/DependencyAttribute.java
new file mode 100644 (file)
index 0000000..b6d60e4
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.config.yangjmxgenerator.attribute;
+
+import javax.management.ObjectName;
+import javax.management.openmbean.OpenType;
+import javax.management.openmbean.SimpleType;
+
+import org.opendaylight.controller.config.yangjmxgenerator.ServiceInterfaceEntry;
+import org.opendaylight.yangtools.binding.generator.util.Types;
+import org.opendaylight.yangtools.sal.binding.model.api.Type;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+
+public class DependencyAttribute extends AbstractAttribute implements
+        TypedAttribute {
+
+    private final Dependency dependency;
+    private final String nullableDescription, nullableDefault;
+
+    public DependencyAttribute(DataSchemaNode attrNode,
+            ServiceInterfaceEntry sie, boolean mandatory,
+            String nullableDescription) {
+        super(attrNode);
+        dependency = new Dependency(sie, mandatory);
+        this.nullableDescription = nullableDescription;
+        nullableDefault = null;
+    }
+
+    @Override
+    public Type getType() {
+        return Types.typeForClass(ObjectName.class);
+    }
+
+    public Dependency getDependency() {
+        return dependency;
+    }
+
+    @Override
+    public String getNullableDescription() {
+        return nullableDescription;
+    }
+
+    @Override
+    public String getNullableDefault() {
+        return nullableDefault;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o)
+            return true;
+        if (o == null || getClass() != o.getClass())
+            return false;
+        if (!super.equals(o))
+            return false;
+
+        DependencyAttribute that = (DependencyAttribute) o;
+
+        if (dependency != null ? !dependency.equals(that.dependency)
+                : that.dependency != null)
+            return false;
+        if (nullableDefault != null ? !nullableDefault
+                .equals(that.nullableDefault) : that.nullableDefault != null)
+            return false;
+        if (nullableDescription != null ? !nullableDescription
+                .equals(that.nullableDescription)
+                : that.nullableDescription != null)
+            return false;
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = super.hashCode();
+        result = 31 * result + (dependency != null ? dependency.hashCode() : 0);
+        result = 31
+                * result
+                + (nullableDescription != null ? nullableDescription.hashCode()
+                        : 0);
+        result = 31 * result
+                + (nullableDefault != null ? nullableDefault.hashCode() : 0);
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        return "DependencyAttribute{" + getAttributeYangName() + ","
+                + "dependency=" + dependency + '}';
+    }
+
+    @Override
+    public OpenType<?> getOpenType() {
+        return SimpleType.OBJECTNAME;
+    }
+
+    public static class Dependency {
+        private final ServiceInterfaceEntry sie;
+        private final boolean mandatory;
+
+        public Dependency(ServiceInterfaceEntry sie, boolean mandatory) {
+            this.sie = sie;
+            this.mandatory = mandatory;
+        }
+
+        public ServiceInterfaceEntry getSie() {
+            return sie;
+        }
+
+        public boolean isMandatory() {
+            return mandatory;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o)
+                return true;
+            if (o == null || getClass() != o.getClass())
+                return false;
+
+            Dependency that = (Dependency) o;
+
+            if (mandatory != that.mandatory)
+                return false;
+            if (!sie.equals(that.sie))
+                return false;
+
+            return true;
+        }
+
+        @Override
+        public int hashCode() {
+            int result = sie.hashCode();
+            result = 31 * result + (mandatory ? 1 : 0);
+            return result;
+        }
+    }
+
+}
diff --git a/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/JavaAttribute.java b/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/JavaAttribute.java
new file mode 100644 (file)
index 0000000..8f516ef
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.config.yangjmxgenerator.attribute;
+
+import javax.management.openmbean.ArrayType;
+import javax.management.openmbean.OpenDataException;
+import javax.management.openmbean.OpenType;
+import javax.management.openmbean.SimpleType;
+
+import org.opendaylight.controller.config.yangjmxgenerator.TypeProviderWrapper;
+import org.opendaylight.yangtools.sal.binding.model.api.Type;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+
+public class JavaAttribute extends AbstractAttribute implements TypedAttribute {
+    private final Type type;
+    private final String nullableDescription, nullableDefault;
+
+    public JavaAttribute(LeafSchemaNode leaf,
+            TypeProviderWrapper typeProviderWrapper) {
+        super(leaf);
+        this.type = typeProviderWrapper.getType(leaf);
+        this.nullableDefault = leaf.getDefault();
+        this.nullableDescription = leaf.getDescription();
+    }
+
+    public JavaAttribute(LeafListSchemaNode leaf,
+            TypeProviderWrapper typeProviderWrapper) {
+        super(leaf);
+        this.type = typeProviderWrapper.getType(leaf);
+        this.nullableDefault = null;
+        this.nullableDescription = leaf.getDescription();
+    }
+
+    @Override
+    public Type getType() {
+        return type;
+    }
+
+    @Override
+    public String getNullableDescription() {
+        return nullableDescription;
+    }
+
+    @Override
+    public String getNullableDefault() {
+        return nullableDefault;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o)
+            return true;
+        if (o == null || getClass() != o.getClass())
+            return false;
+        if (!super.equals(o))
+            return false;
+
+        JavaAttribute that = (JavaAttribute) o;
+
+        if (nullableDefault != null ? !nullableDefault
+                .equals(that.nullableDefault) : that.nullableDefault != null)
+            return false;
+        if (nullableDescription != null ? !nullableDescription
+                .equals(that.nullableDescription)
+                : that.nullableDescription != null)
+            return false;
+        if (type != null ? !type.equals(that.type) : that.type != null)
+            return false;
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = super.hashCode();
+        result = 31 * result + (type != null ? type.hashCode() : 0);
+        result = 31
+                * result
+                + (nullableDescription != null ? nullableDescription.hashCode()
+                        : 0);
+        result = 31 * result
+                + (nullableDefault != null ? nullableDefault.hashCode() : 0);
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        return "JavaAttribute{" + getAttributeYangName() + "," + "type=" + type
+                + '}';
+    }
+
+    @Override
+    public OpenType<?> getOpenType() {
+        // If is array => arrayType
+        if (isArray(getType())) {
+            String innerTypeFullyQName = getInnerType(getType());
+            SimpleType<?> innerSimpleType = SimpleTypeResolver
+                    .getSimpleType(innerTypeFullyQName);
+            try {
+                ArrayType<Object> arrayType = isPrimitive(innerTypeFullyQName) ? new ArrayType<>(
+                        innerSimpleType, true) : new ArrayType<>(1,
+                        innerSimpleType);
+                return arrayType;
+            } catch (OpenDataException e) {
+                throw new RuntimeException("Unable to create "
+                        + ArrayType.class + " with inner element of type "
+                        + innerSimpleType, e);
+            }
+        }
+        // else simple type
+        SimpleType<?> simpleType = SimpleTypeResolver.getSimpleType(getType());
+        return simpleType;
+    }
+
+    // TODO verify
+    private boolean isPrimitive(String innerTypeFullyQName) {
+        if (innerTypeFullyQName.contains("."))
+            return false;
+
+        return true;
+    }
+
+    private static String getInnerType(Type type) {
+        String fullyQualifiedName = type.getFullyQualifiedName();
+        return fullyQualifiedName.substring(0, fullyQualifiedName.length() - 2);
+    }
+
+    private static boolean isArray(Type type) {
+        return type.getName().endsWith("[]");
+    }
+
+}
diff --git a/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/ListAttribute.java b/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/ListAttribute.java
new file mode 100644 (file)
index 0000000..083b0b5
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.config.yangjmxgenerator.attribute;
+
+import javax.management.openmbean.ArrayType;
+import javax.management.openmbean.OpenDataException;
+import javax.management.openmbean.OpenType;
+
+import org.opendaylight.controller.config.yangjmxgenerator.TypeProviderWrapper;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+
+public class ListAttribute extends AbstractAttribute {
+
+    private final String nullableDescription, nullableDefault;
+    private final AttributeIfc innerAttribute;
+
+    public static ListAttribute create(ListSchemaNode node,
+            TypeProviderWrapper typeProvider) {
+
+        AttributeIfc innerAttribute = TOAttribute.create(node, typeProvider);
+
+        return new ListAttribute(node, innerAttribute, node.getDescription());
+    }
+
+    public static ListAttribute create(LeafListSchemaNode node,
+            TypeProviderWrapper typeProvider) {
+
+        AttributeIfc innerAttribute = new JavaAttribute(node, typeProvider);
+
+        return new ListAttribute(node, innerAttribute, node.getDescription());
+    }
+
+    ListAttribute(DataSchemaNode attrNode, AttributeIfc innerAttribute,
+            String description) {
+        super(attrNode);
+        this.nullableDescription = description;
+        this.innerAttribute = innerAttribute;
+        this.nullableDefault = null;
+    }
+
+    @Override
+    public String getNullableDescription() {
+        return nullableDescription;
+    }
+
+    @Override
+    public String getNullableDefault() {
+        return nullableDefault;
+    }
+
+    public AttributeIfc getInnerAttribute() {
+        return innerAttribute;
+    }
+
+    @Override
+    public String toString() {
+        return "ListAttribute{" + getAttributeYangName() + "," + "to="
+                + innerAttribute + '}';
+    }
+
+    @Override
+    public int hashCode() {
+        int result = super.hashCode();
+        result = 31
+                * result
+                + (nullableDescription != null ? nullableDescription.hashCode()
+                        : 0);
+        result = 31 * result
+                + (nullableDefault != null ? nullableDefault.hashCode() : 0);
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o)
+            return true;
+        if (o == null || getClass() != o.getClass())
+            return false;
+        if (!super.equals(o))
+            return false;
+
+        ListAttribute that = (ListAttribute) o;
+
+        if (nullableDefault != null ? !nullableDefault
+                .equals(that.nullableDefault) : that.nullableDefault != null)
+            return false;
+        if (nullableDescription != null ? !nullableDescription
+                .equals(that.nullableDescription)
+                : that.nullableDescription != null)
+            return false;
+
+        return true;
+    }
+
+    @Override
+    public ArrayType<?> getOpenType() {
+        OpenType<?> inerOpenType = innerAttribute.getOpenType();
+        try {
+            return new ArrayType<>(1, inerOpenType);
+        } catch (OpenDataException e) {
+            throw new RuntimeException("Unable to create " + ArrayType.class
+                    + " with inner element of type " + inerOpenType, e);
+        }
+    }
+
+}
diff --git a/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/SimpleTypeResolver.java b/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/SimpleTypeResolver.java
new file mode 100644 (file)
index 0000000..349459a
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.config.yangjmxgenerator.attribute;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Date;
+import java.util.Map;
+
+import javax.management.openmbean.SimpleType;
+
+import org.opendaylight.yangtools.sal.binding.model.api.Type;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Maps;
+
+public class SimpleTypeResolver {
+
+    public static SimpleType<?> getSimpleType(Type type) {
+        SimpleType<?> expectedSimpleType = JAVA_TYPE_TO_SIMPLE_TYPE.get(type
+                .getFullyQualifiedName());
+        Preconditions.checkState(expectedSimpleType != null,
+                "Cannot find simple type for " + type.getFullyQualifiedName());
+        return expectedSimpleType;
+    }
+
+    public static SimpleType<?> getSimpleType(String fullyQualifiedName) {
+        SimpleType<?> expectedSimpleType = JAVA_TYPE_TO_SIMPLE_TYPE
+                .get(fullyQualifiedName);
+        Preconditions.checkState(expectedSimpleType != null,
+                "Cannot find simple type for " + fullyQualifiedName);
+        return expectedSimpleType;
+    }
+
+    private static final Map<String, SimpleType<?>> JAVA_TYPE_TO_SIMPLE_TYPE = Maps
+            .newHashMap();
+    static {
+        // TODO add all
+        JAVA_TYPE_TO_SIMPLE_TYPE.put(Integer.class.getName(),
+                SimpleType.INTEGER);
+        JAVA_TYPE_TO_SIMPLE_TYPE.put(int.class.getName(), SimpleType.INTEGER);
+        JAVA_TYPE_TO_SIMPLE_TYPE.put(Short.class.getName(), SimpleType.SHORT);
+        JAVA_TYPE_TO_SIMPLE_TYPE.put(short.class.getName(), SimpleType.SHORT);
+        JAVA_TYPE_TO_SIMPLE_TYPE.put(Long.class.getName(), SimpleType.LONG);
+        JAVA_TYPE_TO_SIMPLE_TYPE.put(long.class.getName(), SimpleType.LONG);
+        JAVA_TYPE_TO_SIMPLE_TYPE.put(String.class.getName(), SimpleType.STRING);
+        JAVA_TYPE_TO_SIMPLE_TYPE.put(Boolean.class.getName(),
+                SimpleType.BOOLEAN);
+        JAVA_TYPE_TO_SIMPLE_TYPE.put(boolean.class.getName(),
+                SimpleType.BOOLEAN);
+        JAVA_TYPE_TO_SIMPLE_TYPE.put(BigInteger.class.getName(),
+                SimpleType.BIGINTEGER);
+        JAVA_TYPE_TO_SIMPLE_TYPE.put(BigDecimal.class.getName(),
+                SimpleType.BIGDECIMAL);
+        JAVA_TYPE_TO_SIMPLE_TYPE.put(Byte.class.getName(), SimpleType.BYTE);
+        JAVA_TYPE_TO_SIMPLE_TYPE.put(byte.class.getName(), SimpleType.BYTE);
+        JAVA_TYPE_TO_SIMPLE_TYPE.put(Date.class.getName(), SimpleType.DATE);
+        JAVA_TYPE_TO_SIMPLE_TYPE.put(Double.class.getName(), SimpleType.DOUBLE);
+        JAVA_TYPE_TO_SIMPLE_TYPE.put(double.class.getName(), SimpleType.DOUBLE);
+    }
+
+}
diff --git a/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/TOAttribute.java b/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/TOAttribute.java
new file mode 100644 (file)
index 0000000..20029d0
--- /dev/null
@@ -0,0 +1,243 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.config.yangjmxgenerator.attribute;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import javax.management.openmbean.CompositeType;
+import javax.management.openmbean.OpenDataException;
+import javax.management.openmbean.OpenType;
+
+import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry;
+import org.opendaylight.controller.config.yangjmxgenerator.TypeProviderWrapper;
+import org.opendaylight.yangtools.yang.model.api.AugmentationTarget;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+
+import com.google.common.base.Function;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+
+public class TOAttribute extends AbstractAttribute {
+
+    private final String nullableDescription, nullableDefault;
+    private final Map<String, AttributeIfc> yangNameToAttributeMap;
+    private final Map<String, String> attributeNameMap;
+
+    private static final Set<Class<? extends DataSchemaNode>> ALLOWED_CHILDREN = Sets
+            .newHashSet();
+    static {
+        ALLOWED_CHILDREN.add(LeafListSchemaNode.class);
+        ALLOWED_CHILDREN.add(ListSchemaNode.class);
+        ALLOWED_CHILDREN.add(LeafSchemaNode.class);
+        ALLOWED_CHILDREN.add(ContainerSchemaNode.class);
+    }
+
+    public static <T extends DataNodeContainer & AugmentationTarget & DataSchemaNode> TOAttribute create(
+            T containerSchemaNode, TypeProviderWrapper typeProviderWrapper) {
+        // Transfer Object: get the leaves
+        Map<String, AttributeIfc> map = new HashMap<>();
+        Map<String, String> attributeNameMap = new HashMap<>();
+        for (DataSchemaNode dataSchemaNode : containerSchemaNode
+                .getChildNodes()) {
+            try {
+                String yangName = dataSchemaNode.getQName().getLocalName();
+                map.put(yangName,
+                        createInnerAttribute(dataSchemaNode,
+                                typeProviderWrapper));
+            } catch (IllegalArgumentException e) {
+                throw new IllegalStateException("Unable to create TO");
+            }
+        }
+        return new TOAttribute(containerSchemaNode, map, attributeNameMap,
+                containerSchemaNode.getDescription());
+    }
+
+    private static AttributeIfc createInnerAttribute(
+            DataSchemaNode dataSchemaNode,
+            TypeProviderWrapper typeProviderWrapper) {
+        Class<? extends DataSchemaNode> type = isAllowedType(dataSchemaNode);
+
+        if (type.equals(LeafSchemaNode.class))
+            return new JavaAttribute((LeafSchemaNode) dataSchemaNode,
+                    typeProviderWrapper);
+        else if (type.equals(ListSchemaNode.class))
+            return ListAttribute.create((ListSchemaNode) dataSchemaNode,
+                    typeProviderWrapper);
+        else if (type.equals(LeafListSchemaNode.class))
+            return ListAttribute.create((LeafListSchemaNode) dataSchemaNode,
+                    typeProviderWrapper);
+        else if (type.equals(ContainerSchemaNode.class))
+            return TOAttribute.create((ContainerSchemaNode) dataSchemaNode,
+                    typeProviderWrapper);
+
+        throw new IllegalStateException("This should never happen");
+    }
+
+    private static Class<? extends DataSchemaNode> isAllowedType(
+            DataSchemaNode dataSchemaNode) {
+        for (Class<? extends DataSchemaNode> allowedType : ALLOWED_CHILDREN) {
+            if (allowedType.isAssignableFrom(dataSchemaNode.getClass()) == true)
+                return allowedType;
+        }
+        throw new IllegalArgumentException("Illegal child node for TO: "
+                + dataSchemaNode.getClass() + " allowed node types: "
+                + ALLOWED_CHILDREN);
+    }
+
+    private TOAttribute(DataSchemaNode attrNode,
+            Map<String, AttributeIfc> transferObject,
+            Map<String, String> attributeNameMap, String nullableDescription) {
+        super(attrNode);
+        yangNameToAttributeMap = transferObject;
+        this.attributeNameMap = attributeNameMap;
+        this.nullableDescription = nullableDescription;
+        nullableDefault = null;
+    }
+
+    public Map<String, String> getAttributeNameMap() {
+        return attributeNameMap;
+    }
+
+    public Map<String, AttributeIfc> getCapitalizedPropertiesToTypesMap() {
+        Map<String, AttributeIfc> capitalizedPropertiesToTypesMap = Maps
+                .newHashMap();
+        for (Entry<String, AttributeIfc> entry : yangNameToAttributeMap
+                .entrySet()) {
+
+            capitalizedPropertiesToTypesMap.put(
+                    ModuleMXBeanEntry.convertToJavaName(entry.getKey(), true),
+                    entry.getValue());
+        }
+        return capitalizedPropertiesToTypesMap;
+    }
+
+    public Map<String, AttributeIfc> getJmxPropertiesToTypesMap() {
+        Map<String, AttributeIfc> jmxPropertiesToTypesMap = Maps.newHashMap();
+        for (Entry<String, AttributeIfc> entry : yangNameToAttributeMap
+                .entrySet()) {
+
+            jmxPropertiesToTypesMap.put(
+                    ModuleMXBeanEntry.convertToJavaName(entry.getKey(), false),
+                    entry.getValue());
+        }
+        return jmxPropertiesToTypesMap;
+    }
+
+    public Map<String, AttributeIfc> getYangPropertiesToTypesMap() {
+        return yangNameToAttributeMap;
+    }
+
+    @Override
+    public String getNullableDescription() {
+        return nullableDescription;
+    }
+
+    @Override
+    public String getNullableDefault() {
+        return nullableDefault;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o)
+            return true;
+        if (o == null || getClass() != o.getClass())
+            return false;
+        if (!super.equals(o))
+            return false;
+
+        TOAttribute that = (TOAttribute) o;
+
+        if (nullableDefault != null ? !nullableDefault
+                .equals(that.nullableDefault) : that.nullableDefault != null)
+            return false;
+        if (nullableDescription != null ? !nullableDescription
+                .equals(that.nullableDescription)
+                : that.nullableDescription != null)
+            return false;
+        if (yangNameToAttributeMap != null ? !yangNameToAttributeMap
+                .equals(that.yangNameToAttributeMap)
+                : that.yangNameToAttributeMap != null)
+            return false;
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = super.hashCode();
+        result = 31
+                * result
+                + (nullableDescription != null ? nullableDescription.hashCode()
+                        : 0);
+        result = 31 * result
+                + (nullableDefault != null ? nullableDefault.hashCode() : 0);
+        result = 31
+                * result
+                + (yangNameToAttributeMap != null ? yangNameToAttributeMap
+                        .hashCode() : 0);
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        return "TOAttribute{" + getAttributeYangName() + "," + "to="
+                + yangNameToAttributeMap + '}';
+    }
+
+    @Override
+    public OpenType<?> getOpenType() {
+        String description = getNullableDescription() == null ? getAttributeYangName()
+                : getNullableDescription();
+        final String[] itemNames = new String[yangNameToAttributeMap.keySet()
+                .size()];
+        String[] itemDescriptions = itemNames;
+        FunctionImpl functionImpl = new FunctionImpl(itemNames);
+        Map<String, AttributeIfc> jmxPropertiesToTypesMap = getJmxPropertiesToTypesMap();
+        OpenType<?>[] itemTypes = Collections2.transform(
+                jmxPropertiesToTypesMap.entrySet(), functionImpl).toArray(
+                new OpenType<?>[] {});
+        try {
+            // TODO add package name to create fully qualified name for this
+            // type
+            CompositeType compositeType = new CompositeType(
+                    getUpperCaseCammelCase(), description, itemNames,
+                    itemDescriptions, itemTypes);
+            return compositeType;
+        } catch (OpenDataException e) {
+            throw new RuntimeException("Unable to create CompositeType for "
+                    + this, e);
+        }
+    }
+
+    private static final class FunctionImpl implements
+            Function<Entry<String, AttributeIfc>, OpenType<?>> {
+        private final String[] itemNames;
+        int i = 0;
+
+        private FunctionImpl(String[] itemNames) {
+            this.itemNames = itemNames;
+        }
+
+        @Override
+        public OpenType<?> apply(Entry<String, AttributeIfc> input) {
+            AttributeIfc innerType = input.getValue();
+            itemNames[i++] = input.getKey();
+            return innerType.getOpenType();
+        }
+    }
+}
diff --git a/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/TypedAttribute.java b/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/TypedAttribute.java
new file mode 100644 (file)
index 0000000..98a85d4
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.config.yangjmxgenerator.attribute;
+
+import org.opendaylight.yangtools.sal.binding.model.api.Type;
+
+public interface TypedAttribute extends AttributeIfc {
+
+    Type getType();
+
+}
diff --git a/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/Util.java b/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/Util.java
new file mode 100644 (file)
index 0000000..30c7919
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.config.yangjmxgenerator.attribute;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+final class Util {
+
+    /**
+     * Used for date <-> xml serialization
+     */
+    private static final SimpleDateFormat dateFormat = new SimpleDateFormat(
+            "yyyy-MM-dd");
+
+    public static String writeDate(Date date) {
+        return dateFormat.format(date);
+    }
+
+    public static Date readDate(String s) throws ParseException {
+        return dateFormat.parse(s);
+    }
+}
diff --git a/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/util/FullyQualifiedNameHelper.java b/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/util/FullyQualifiedNameHelper.java
new file mode 100644 (file)
index 0000000..50b4485
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * 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.util;
+
+public class FullyQualifiedNameHelper {
+    public static String getFullyQualifiedName(String packageName,
+            String className) {
+        if (packageName.isEmpty())
+            return className;
+        return packageName + "." + className;
+    }
+}
diff --git a/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/util/NameConflictException.java b/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/util/NameConflictException.java
new file mode 100644 (file)
index 0000000..2ae7056
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * 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.util;
+
+import org.opendaylight.yangtools.yang.common.QName;
+
+public class NameConflictException extends RuntimeException {
+
+    private static final String messageBlueprint = "Name conflict for name: %s, first defined in: %s, then defined in: %s";
+    private final String conflictingName;
+    private final QName secondParentQName;
+    private final QName firstParentQName;
+
+    public NameConflictException(String conflictingName,
+            QName firstDefinedParentQName, QName secondDefinedParentQName) {
+        super(String.format(messageBlueprint, conflictingName,
+                firstDefinedParentQName, secondDefinedParentQName));
+        this.conflictingName = conflictingName;
+        this.firstParentQName = firstDefinedParentQName;
+        this.secondParentQName = secondDefinedParentQName;
+    }
+
+    // TODO add yang local names
+
+    public String getConflictingName() {
+        return conflictingName;
+    }
+
+    public QName getSecondParentQName() {
+        return secondParentQName;
+    }
+
+    public QName getFirstParentQName() {
+        return firstParentQName;
+    }
+}
diff --git a/opendaylight/config/yang-jmx-generator/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/AbstractYangTest.java b/opendaylight/config/yang-jmx-generator/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/AbstractYangTest.java
new file mode 100644 (file)
index 0000000..1100b35
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * 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;
+
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.format;
+
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.junit.Before;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.util.YangModelSearchUtils;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
+
+import com.google.common.base.Preconditions;
+
+public abstract class AbstractYangTest {
+    protected SchemaContext context;
+    protected Map<String, Module> namesToModules; // are module names globally
+                                                  // unique?
+    protected Module configModule, rpcContextModule, threadsModule,
+            threadsJavaModule, bgpListenerJavaModule, ietfInetTypesModule,
+            jmxModule, jmxImplModule, testFilesModule, testFiles1Module;
+
+    @Before
+    public void loadYangFiles() throws Exception {
+        List<InputStream> yangISs = new ArrayList<>();
+        yangISs.addAll(getStreams("/test-config-threads.yang",
+                "/test-config-threads-java.yang",
+                "/config-bgp-listener-impl.yang", "/ietf-inet-types.yang",
+                "/config-jmx-it.yang", "/config-jmx-it-impl.yang",
+                "/test-config-files.yang", "/test-config-files1.yang"));
+
+        yangISs.addAll(getConfigApiYangInputStreams());
+
+        YangParserImpl parser = new YangParserImpl();
+        Set<Module> modulesToBuild = parser.parseYangModelsFromStreams(yangISs);
+        // close ISs
+        for (InputStream is : yangISs) {
+            is.close();
+        }
+        context = parser.resolveSchemaContext(modulesToBuild);
+        namesToModules = YangModelSearchUtils.mapModulesByNames(context
+                .getModules());
+        configModule = namesToModules.get(ConfigConstants.CONFIG_MODULE);
+        rpcContextModule = namesToModules.get(ConfigConstants.CONFIG_MODULE);
+        threadsModule = namesToModules
+                .get(ConfigConstants.CONFIG_THREADS_MODULE);
+        threadsJavaModule = namesToModules.get("config-threads-java");
+        bgpListenerJavaModule = namesToModules.get("config-bgp-listener-impl");
+        ietfInetTypesModule = namesToModules
+                .get(ConfigConstants.IETF_INET_TYPES);
+        jmxModule = namesToModules.get("config-jmx-it");
+        jmxImplModule = namesToModules.get("config-jmx-it-impl");
+        testFilesModule = namesToModules.get("test-config-files");
+        testFiles1Module = namesToModules.get("test-config-files1");
+
+    }
+
+    public static List<InputStream> getConfigApiYangInputStreams() {
+        return getStreams("/META-INF/yang/config.yang",
+                "/META-INF/yang/rpc-context.yang");
+    }
+
+    public Map<QName, IdentitySchemaNode> mapIdentitiesByQNames(Module module) {
+        Map<QName, IdentitySchemaNode> result = new HashMap<>();
+        for (IdentitySchemaNode identitySchemaNode : module.getIdentities()) {
+            QName qName = identitySchemaNode.getQName();
+            Preconditions.checkArgument(
+                    result.containsKey(qName) == false,
+                    format("Two identities of %s contain same " + "qname %s",
+                            module, qName));
+            result.put(qName, identitySchemaNode);
+        }
+        return result;
+    }
+
+    protected static List<InputStream> getStreams(String... paths) {
+        List<InputStream> result = new ArrayList<>();
+        for (String path : paths) {
+            InputStream is = AbstractYangTest.class.getResourceAsStream(path);
+            assertNotNull(path + " is null", is);
+            result.add(is);
+        }
+        return result;
+    }
+}
diff --git a/opendaylight/config/yang-jmx-generator/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/ModuleMXBeanEntryNameConflictTest.java b/opendaylight/config/yang-jmx-generator/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/ModuleMXBeanEntryNameConflictTest.java
new file mode 100644 (file)
index 0000000..deef08a
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * 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;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.junit.Test;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.util.NameConflictException;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.util.YangModelSearchUtils;
+import org.opendaylight.yangtools.sal.binding.yang.types.TypeProviderImpl;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Preconditions;
+
+public class ModuleMXBeanEntryNameConflictTest extends AbstractYangTest {
+
+    private static final Logger logger = LoggerFactory
+            .getLogger(ModuleMXBeanEntryNameConflictTest.class);
+
+    public static final String PACKAGE_NAME = "pack2";
+    Map<File, String> testedFilesToYangModules = new HashMap<>();
+    Map<String, String> testedYangModulesToExpectedConflictingName = new HashMap<>();
+
+    @Test
+    public void testNameConflicts() throws Exception {
+        prepareSamples();
+        prepareExceptionAssertions();
+
+        for (Map.Entry<File, String> currentTestEntry : testedFilesToYangModules
+                .entrySet()) {
+            final String moduleName = currentTestEntry.getValue();
+            final File yangFile = currentTestEntry.getKey();
+            Module testedModule = loadYangs(yangFile, moduleName);
+
+            try {
+                logger.debug("Testing {}", yangFile);
+                ModuleMXBeanEntry.create(testedModule,
+                        new HashMap<QName, ServiceInterfaceEntry>(), context,
+                        new TypeProviderWrapper(new TypeProviderImpl(context)),
+                        PACKAGE_NAME);
+                fail(yangFile.toString()
+                        + " did not cause a name conflict and should");
+            } catch (NameConflictException e) {
+                assertEquals(
+                        testedYangModulesToExpectedConflictingName
+                                .get(moduleName),
+                        e.getConflictingName());
+            }
+        }
+    }
+
+    private void prepareSamples() {
+        File first = new File(getClass().getResource(
+                "/duplicates/config-test-duplicate-attribute-in-list.yang")
+                .getFile());
+        File dir = first.getParentFile();
+
+        for (File testYang : dir.listFiles()) {
+            String moduleName = getYangModuleName(testYang.getName());
+            testedFilesToYangModules.put(testYang, moduleName);
+        }
+    }
+
+    private void prepareExceptionAssertions() {
+        testedYangModulesToExpectedConflictingName.put(
+                "config-test-duplicate-attribute", "DtoA");
+        testedYangModulesToExpectedConflictingName.put(
+                "config-test-duplicate-attribute-in-list", "DtoA");
+        testedYangModulesToExpectedConflictingName.put(
+                "config-test-duplicate-attribute-runtime-bean", "DtoA");
+        testedYangModulesToExpectedConflictingName.put(
+                "config-test-generated-attributes-name-conflict", "StateB");
+        testedYangModulesToExpectedConflictingName.put(
+                "config-test-runtime-bean-list-name-conflict",
+                "StateARuntimeMXBean");
+        testedYangModulesToExpectedConflictingName.put(
+                "config-test-runtime-bean-list-name-conflict2",
+                "StateARuntimeMXBean");
+        testedYangModulesToExpectedConflictingName
+                .put("config-test-runtime-bean-name-conflict", "StateARuntimeMXBean");
+        testedYangModulesToExpectedConflictingName.put(
+                "config-test-runtime-bean-name-conflict2",
+                "StateARuntimeMXBean");
+    }
+
+    private String getYangModuleName(String name) {
+        int startIndex = 0;
+        int endIndex = name.indexOf(".yang");
+        return name.substring(startIndex, endIndex);
+    }
+
+    private Module loadYangs(File testedModule, String moduleName)
+            throws Exception {
+        List<InputStream> yangISs = new ArrayList<>();
+        yangISs.addAll(getStreams("/ietf-inet-types.yang"));
+
+        yangISs.add(new FileInputStream(testedModule));
+
+        yangISs.addAll(getConfigApiYangInputStreams());
+
+        YangParserImpl parser = new YangParserImpl();
+        Set<Module> modulesToBuild = parser.parseYangModelsFromStreams(yangISs);
+        // close ISs
+        for (InputStream is : yangISs) {
+            is.close();
+        }
+        context = parser.resolveSchemaContext(modulesToBuild);
+        namesToModules = YangModelSearchUtils.mapModulesByNames(context
+                .getModules());
+        configModule = namesToModules.get(ConfigConstants.CONFIG_MODULE);
+        final Module module = namesToModules.get(moduleName);
+        Preconditions.checkNotNull(module, "Cannot get module %s from %s",
+                moduleName, namesToModules.keySet());
+        return module;
+    }
+
+}
diff --git a/opendaylight/config/yang-jmx-generator/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/ModuleMXBeanEntryTest.java b/opendaylight/config/yang-jmx-generator/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/ModuleMXBeanEntryTest.java
new file mode 100644 (file)
index 0000000..661dbd7
--- /dev/null
@@ -0,0 +1,304 @@
+/*
+ * 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;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Matcher;
+
+import javax.management.openmbean.ArrayType;
+import javax.management.openmbean.CompositeType;
+import javax.management.openmbean.SimpleType;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc;
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.DependencyAttribute;
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.JavaAttribute;
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.ListAttribute;
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.TOAttribute;
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.TypedAttribute;
+import org.opendaylight.yangtools.binding.generator.util.Types;
+import org.opendaylight.yangtools.sal.binding.model.api.Type;
+import org.opendaylight.yangtools.sal.binding.yang.types.TypeProviderImpl;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath;
+
+import com.google.common.collect.Sets;
+
+public class ModuleMXBeanEntryTest extends AbstractYangTest {
+    public static final String EVENTBUS_MXB_NAME = "eventbus";
+    public static final String ASYNC_EVENTBUS_MXB_NAME = "async-eventbus";
+    public static final String THREADFACTORY_NAMING_MXB_NAME = "threadfactory-naming";
+    public static final String THREADPOOL_DYNAMIC_MXB_NAME = "threadpool-dynamic";
+
+    public static final String BGP_LISTENER_IMPL_MXB_NAME = "bgp-listener-impl";
+
+    public static final String PACKAGE_NAME = "pack2";
+
+    protected static final URI THREADS_NAMESPACE;
+    protected static final Date THREADS_REVISION_DATE;
+
+    static {
+        try {
+            THREADS_NAMESPACE = new URI(ConfigConstants.CONFIG_NAMESPACE
+                    + ":threads");
+        } catch (URISyntaxException e) {
+            throw new Error(e);
+        }
+        SimpleDateFormat revisionFormat = new SimpleDateFormat("yyyy-MM-dd");
+        try {
+            THREADS_REVISION_DATE = revisionFormat.parse("2013-04-09");
+        } catch (ParseException e) {
+            throw new Error(e);
+        }
+    }
+
+    protected Map<QName, ServiceInterfaceEntry> modulesToSIEs;
+
+    protected Map<String /* identity local name */, ModuleMXBeanEntry> loadThreadsJava() {
+        Map<String /* identity local name */, ModuleMXBeanEntry> namesToMBEs = ModuleMXBeanEntry
+                .create(threadsJavaModule, modulesToSIEs, context, new TypeProviderWrapper(new TypeProviderImpl
+                        (context)), PACKAGE_NAME);
+        assertNotNull(namesToMBEs);
+        Set<String> expectedMXBeanNames = Sets.newHashSet(EVENTBUS_MXB_NAME,
+                ASYNC_EVENTBUS_MXB_NAME, THREADFACTORY_NAMING_MXB_NAME,
+                THREADPOOL_DYNAMIC_MXB_NAME);
+        assertThat(namesToMBEs.keySet(), is(expectedMXBeanNames));
+        return namesToMBEs;
+    }
+
+    @Before
+    public void setUp() {
+        modulesToSIEs = ServiceInterfaceEntry.create(threadsModule,
+                "packages.sis");
+    }
+
+    @Test
+    public void test_jmxImplModule() {
+        Map<QName, ServiceInterfaceEntry> modulesToSIEs = ServiceInterfaceEntry
+                .create(threadsModule, PACKAGE_NAME);
+        modulesToSIEs.putAll(ServiceInterfaceEntry.create(jmxModule,
+                PACKAGE_NAME));
+        Map<String /* identity local name */, ModuleMXBeanEntry> namesToMBEs = ModuleMXBeanEntry
+                .create(jmxImplModule, modulesToSIEs, context, new TypeProviderWrapper(new TypeProviderImpl(context))
+                        , PACKAGE_NAME);
+        Map<String, AttributeIfc> attributes = namesToMBEs.get("impl-netconf")
+                .getAttributes();
+        //
+        DependencyAttribute threadFactoryAttribute = (DependencyAttribute) attributes
+                .get("thread-factory");
+        assertNotNull(threadFactoryAttribute);
+        assertFalse(threadFactoryAttribute.getDependency().isMandatory());
+        assertThat(threadFactoryAttribute.getDependency().getSie()
+                .getTypeName(), is("ThreadFactoryServiceInterface"));
+        assertThat(threadFactoryAttribute.getAttributeYangName(),
+                is("thread-factory"));
+        assertThat(threadFactoryAttribute.getLowerCaseCammelCase(),
+                is("threadFactory"));
+        assertThat(threadFactoryAttribute.getUpperCaseCammelCase(),
+                is("ThreadFactory"));
+        assertThat(threadFactoryAttribute.getOpenType(), is(SimpleType.class));
+        assertNull(threadFactoryAttribute.getNullableDefault());
+        assertNull(threadFactoryAttribute.getNullableDescription());
+        assertThat(threadFactoryAttribute.getType().getName(), is("ObjectName"));
+    }
+
+    protected RuntimeBeanEntry findFirstByYangName(
+            Collection<RuntimeBeanEntry> runtimeBeans, String yangName) {
+        for (RuntimeBeanEntry rb : runtimeBeans) {
+            if (yangName.equals(rb.getYangName()))
+                return rb;
+        }
+        throw new IllegalArgumentException("Yang name not found:" + yangName
+                + " in " + runtimeBeans);
+    }
+
+    @Test
+    public void testGetWhenConditionMatcher() {
+        assertMatches("config",
+                "/config:modules/config:module/config:type = 'threadpool-dynamic'");
+        assertMatches("ns",
+                "/ns:modules/ns:module/ns:type = 'threadpool-dynamic'");
+        assertMatches("config",
+                "/config:modules/config:module/config:type=\"threadpool-dynamic\"");
+    }
+
+    private void assertMatches(String prefix, String input) {
+        RevisionAwareXPath whenConstraint = mock(RevisionAwareXPath.class);
+        doReturn(input).when(whenConstraint).toString();
+        Matcher output = ModuleMXBeanEntry.getWhenConditionMatcher(prefix,
+                whenConstraint);
+        assertTrue(output.matches());
+        assertEquals("threadpool-dynamic", output.group(1));
+    }
+
+    @Test
+    public void testThreadsJava() {
+        Map<String /* identity local name */, ModuleMXBeanEntry> namesToMBEs = loadThreadsJava();
+
+        { // check threadpool-dynamic
+            ModuleMXBeanEntry dynamicThreadPool = namesToMBEs
+                    .get(THREADPOOL_DYNAMIC_MXB_NAME);
+            Map<String, AttributeIfc> attributes = dynamicThreadPool
+                    .getAttributes();
+            // core-size, keepalive, maximum-size
+            // threadfactory
+            Set<String> longAttribs = Sets.newHashSet("core-size",
+                    "maximum-size");
+            for (String longAttrib : longAttribs) {
+
+                TypedAttribute attribute = (TypedAttribute) attributes
+                        .get(longAttrib);
+                assertThat("Failed to check " + longAttrib,
+                        attribute.getType(),
+                        is((Type) Types.typeForClass(Long.class)));
+            }
+            // check dependency on thread factory
+            QName threadfactoryQName = new QName(THREADS_NAMESPACE,
+                    THREADS_REVISION_DATE, "threadfactory");
+            ServiceInterfaceEntry threadFactorySIEntry = modulesToSIEs
+                    .get(threadfactoryQName);
+            assertNotNull(threadFactorySIEntry);
+            boolean expectedMandatory = true;
+            TypedAttribute actualThreadFactory = (TypedAttribute) attributes
+                    .get("threadfactory");
+
+            DataSchemaNode mockedDataSchemaNode = mock(DataSchemaNode.class);
+            doReturn(Collections.emptyList()).when(mockedDataSchemaNode)
+                    .getUnknownSchemaNodes();
+            doReturn(threadfactoryQName).when(mockedDataSchemaNode).getQName();
+            AttributeIfc expectedDependencyAttribute = new DependencyAttribute(
+                    mockedDataSchemaNode, threadFactorySIEntry,
+                    expectedMandatory, "threadfactory description");
+            assertThat(actualThreadFactory, is(expectedDependencyAttribute));
+            assertThat(
+                    dynamicThreadPool
+                            .getFullyQualifiedName("DynamicThreadPoolModuleMXBean"),
+                    is(PACKAGE_NAME + ".DynamicThreadPoolModuleMXBean"));
+            assertThat(dynamicThreadPool.getNullableDescription(),
+                    is("threadpool-dynamic description"));
+            assertThat(dynamicThreadPool.getYangModuleName(),
+                    is("config-threads-java"));
+            assertThat(dynamicThreadPool.getYangModuleLocalname(),
+                    is(THREADPOOL_DYNAMIC_MXB_NAME));
+
+            // check root runtime bean
+            Collection<RuntimeBeanEntry> runtimeBeans = dynamicThreadPool
+                    .getRuntimeBeans();
+            assertThat(runtimeBeans.size(), is(1));
+            RuntimeBeanEntry rootRB = findFirstByYangName(runtimeBeans,
+                    THREADPOOL_DYNAMIC_MXB_NAME);
+            assertThat(rootRB.isRoot(), is(true));
+            assertThat(rootRB.getAttributes().size(), is(1));
+            JavaAttribute attribute = (JavaAttribute) rootRB.getAttributes()
+                    .iterator().next();
+            assertThat(attribute.getAttributeYangName(), is("created-sessions"));
+            assertThat(rootRB.getYangName(), is(THREADPOOL_DYNAMIC_MXB_NAME));
+            assertThat(attribute.getType().getFullyQualifiedName(),
+                    is(Long.class.getName()));
+        }
+        {// check threadfactory-naming
+            ModuleMXBeanEntry threadFactoryNaming = namesToMBEs
+                    .get(THREADFACTORY_NAMING_MXB_NAME);
+            Collection<RuntimeBeanEntry> runtimeBeans = threadFactoryNaming
+                    .getRuntimeBeans();
+            assertThat(runtimeBeans.size(), is(4));
+            {
+                RuntimeBeanEntry threadRB = findFirstByYangName(runtimeBeans,
+                        "thread");
+                assertNotNull(threadRB);
+                assertFalse(threadRB.isRoot());
+                assertEquals("name", threadRB.getKeyYangName().get());
+                assertEquals("Name", threadRB.getKeyJavaName().get());
+                assertThat(threadRB.getAttributes().size(), is(1));
+                AttributeIfc threadNameAttr = threadRB.getAttributes()
+                        .iterator().next();
+                assertThat(threadNameAttr.getAttributeYangName(), is("name"));
+                assertTrue(threadNameAttr instanceof JavaAttribute);
+                assertThat(((JavaAttribute) threadNameAttr).getType()
+                        .getFullyQualifiedName(), is(String.class.getName()));
+                assertThat(threadRB.getRpcs().size(), is(2));
+            }
+            {
+                RuntimeBeanEntry streamRB = findFirstByYangName(runtimeBeans,
+                        "stream");
+                assertNotNull(streamRB);
+                assertFalse(streamRB.getKeyYangName().isPresent());
+                assertFalse(streamRB.getKeyJavaName().isPresent());
+                Map<String, AttributeIfc> attributeMap = streamRB
+                        .getYangPropertiesToTypesMap();
+                assertEquals(4, attributeMap.size());
+
+                TOAttribute toAttr = (TOAttribute) attributeMap.get("peer");
+                assertNotNull(toAttr);
+                assertThat(toAttr.getAttributeYangName(), is("peer"));
+                assertThat(toAttr.getLowerCaseCammelCase(), is("peer"));
+                assertThat(toAttr.getUpperCaseCammelCase(), is("Peer"));
+                assertThat(toAttr.getOpenType(), is(CompositeType.class));
+                Set<String> propsExpected = new HashSet<String>(2);
+                propsExpected.add("port");
+                propsExpected.add("core-size");
+                assertThat(toAttr.getYangPropertiesToTypesMap().keySet(),
+                        is(propsExpected));
+                propsExpected = new HashSet<String>(2);
+                propsExpected.add("Port");
+                propsExpected.add("CoreSize");
+                assertThat(
+                        toAttr.getCapitalizedPropertiesToTypesMap().keySet(),
+                        is(propsExpected));
+                propsExpected = new HashSet<String>(2);
+                propsExpected.add("port");
+                propsExpected.add("coreSize");
+                assertThat(toAttr.getJmxPropertiesToTypesMap().keySet(),
+                        is(propsExpected));
+
+                JavaAttribute timestampAttr = (JavaAttribute) attributeMap
+                        .get("timestamp");
+                assertNotNull(timestampAttr);
+
+                JavaAttribute stateAttr = (JavaAttribute) attributeMap
+                        .get("state");
+                assertNotNull(stateAttr);
+
+                ListAttribute innerStream = (ListAttribute) attributeMap
+                        .get("inner-stream-list");
+                assertNotNull(innerStream);
+                assertThat(innerStream.getAttributeYangName(),
+                        is("inner-stream-list"));
+                assertThat(innerStream.getLowerCaseCammelCase(),
+                        is("innerStreamList"));
+                assertThat(innerStream.getUpperCaseCammelCase(),
+                        is("InnerStreamList"));
+                assertThat(innerStream.getOpenType(), is(ArrayType.class));
+
+            }
+
+        }
+    }
+
+}
diff --git a/opendaylight/config/yang-jmx-generator/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/PackageTranslatorTest.java b/opendaylight/config/yang-jmx-generator/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/PackageTranslatorTest.java
new file mode 100644 (file)
index 0000000..031acfa
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * 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;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
+import java.net.URI;
+import java.util.Map;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.model.api.Module;
+
+import com.google.common.collect.Maps;
+
+public class PackageTranslatorTest {
+    public static final String EXPECTED_PACKAGE_PREFIX = "org.opendaylight.controller.config";
+
+    @Test
+    public void test() throws Exception {
+        Map<String, String> map = Maps.newHashMap();
+        map.put(ConfigConstants.CONFIG_NAMESPACE, EXPECTED_PACKAGE_PREFIX);
+        PackageTranslator tested = new PackageTranslator(map);
+        Module module = mock(Module.class);
+        doReturn(new URI(ConfigConstants.CONFIG_NAMESPACE + ":threads:api"))
+                .when(module).getNamespace();
+        assertEquals(EXPECTED_PACKAGE_PREFIX + ".threads.api",
+                tested.getPackageName(module));
+    }
+
+}
diff --git a/opendaylight/config/yang-jmx-generator/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/RuntimeBeanEntryTest.java b/opendaylight/config/yang-jmx-generator/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/RuntimeBeanEntryTest.java
new file mode 100644 (file)
index 0000000..597cf4c
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ * 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;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.doReturn;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+
+import javax.management.openmbean.SimpleType;
+
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.JavaAttribute;
+import org.opendaylight.yangtools.sal.binding.yang.types.TypeProviderImpl;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+
+public class RuntimeBeanEntryTest extends AbstractYangTest {
+
+    public static final String PACKAGE_NAME = "packages.sis";
+    public static final String THREADFACTORY_NAMING_MXB_NAME = "threadfactory-naming";
+    public static final String THREAD_RUNTIME_BEAN_JAVA_NAME = "ThreadRuntimeMXBean";
+    public static final String THREAD_RUNTIME_BEAN_JAVA_PREFIX = "Thread";
+    public static final String THREAD_RUNTIME_BEAN_YANG_NAME = "thread";
+    public static final String SLEEP_RPC_NAME = "sleep";
+    public static final String SLEEP_RPC_OUTPUT = "ThreadState";
+    public static final String SLEEP_RPC_INPUT_NAME = "millis";
+    public static final String SLEEP_RPC_INPUT_TYPE = "Long";
+
+    @Test
+    public void createRuntimeBean() {
+        ChoiceCaseNode caseNode = Mockito.mock(ChoiceCaseNode.class);
+        doReturn(new HashSet<LeafSchemaNode>()).when(caseNode).getChildNodes();
+        doReturn(new ArrayList<UnknownSchemaNode>()).when(caseNode)
+                .getUnknownSchemaNodes();
+        Map<String, RuntimeBeanEntry> runtimeBeans = RuntimeBeanEntry
+                .extractClassNameToRuntimeBeanMap(PACKAGE_NAME, caseNode, "test-name", new TypeProviderWrapper(new
+                        TypeProviderImpl(context)), "test", jmxImplModule);
+        assertThat(runtimeBeans.size(), is(1));
+        RuntimeBeanEntry runtimeMXBean = runtimeBeans.get("testRuntimeMXBean");
+        assertThat(runtimeMXBean.isRoot(), is(true));
+        assertThat(runtimeMXBean.getYangName(), is("test-name"));
+    }
+
+    @Test
+    public void runtimeBeanRPCTest() {
+        // create service interfaces
+        Map<QName, ServiceInterfaceEntry> modulesToSIEs = ServiceInterfaceEntry
+                .create(threadsModule, "packages.sis");
+        assertNotNull(modulesToSIEs);
+
+        // create MXBeans map
+        Map<String, ModuleMXBeanEntry> namesToMBEs = ModuleMXBeanEntry.create(
+                threadsJavaModule, modulesToSIEs, context,
+                new TypeProviderWrapper(new TypeProviderImpl(context)),
+                PACKAGE_NAME);
+        assertThat(namesToMBEs.isEmpty(), is(false));
+
+        // get threadfactory-naming bean
+        ModuleMXBeanEntry threadfactoryNamingMXBean = namesToMBEs
+                .get(THREADFACTORY_NAMING_MXB_NAME);
+        assertNotNull(threadfactoryNamingMXBean);
+
+        // get runtime beans
+        Collection<RuntimeBeanEntry> runtimeBeanEntries = threadfactoryNamingMXBean
+                .getRuntimeBeans();
+        assertThat(runtimeBeanEntries.isEmpty(), is(false));
+
+        // get root runtime bean
+        RuntimeBeanEntry threadfactoryRuntimeBeanEntry = getRuntimeBeanEntryByJavaName(
+                runtimeBeanEntries, "NamingThreadFactoryRuntimeMXBean");
+        assertNotNull(threadfactoryRuntimeBeanEntry);
+        assertThat(threadfactoryRuntimeBeanEntry.isRoot(), is(true));
+
+        // get thread runtime bean
+        RuntimeBeanEntry runtimeBeanEntry = getRuntimeBeanEntryByJavaName(
+                runtimeBeanEntries, THREAD_RUNTIME_BEAN_JAVA_NAME);
+        assertNotNull(runtimeBeanEntry);
+
+        // test thread runtime bean properties
+        assertThat(runtimeBeanEntry.getJavaNamePrefix(),
+                is(THREAD_RUNTIME_BEAN_JAVA_PREFIX));
+        assertThat(runtimeBeanEntry.getPackageName(), is(PACKAGE_NAME));
+        assertThat(runtimeBeanEntry.getFullyQualifiedName(runtimeBeanEntry
+                .getJavaNameOfRuntimeMXBean()), is(PACKAGE_NAME + "."
+                + THREAD_RUNTIME_BEAN_JAVA_NAME));
+        assertThat(runtimeBeanEntry.getYangName(),
+                is(THREAD_RUNTIME_BEAN_YANG_NAME));
+
+        // get thread runtime bean rpcs
+        List<RuntimeBeanEntry.Rpc> rpcs = new ArrayList<RuntimeBeanEntry.Rpc>(
+                runtimeBeanEntry.getRpcs());
+        assertThat(rpcs.size(), is(2));
+
+        // get sleep rpc and test it
+        RuntimeBeanEntry.Rpc rpc = getRpcByName(rpcs, SLEEP_RPC_NAME);
+        assertNotNull(rpc);
+        assertThat(rpc.getYangName(), is(SLEEP_RPC_NAME));
+        assertThat(rpc.getReturnType().endsWith(SLEEP_RPC_OUTPUT), is(true));
+
+        // get sleep rpc input attribute and test it
+        List<JavaAttribute> attributes = rpc.getParameters();
+        assertThat(attributes.size(), is(1));
+        JavaAttribute attribute = attributes.get(0);
+        assertThat(attribute.getAttributeYangName(), is(SLEEP_RPC_INPUT_NAME));
+        assertThat(attribute.getType().getName(), is(SLEEP_RPC_INPUT_TYPE));
+        assertThat(attribute.getLowerCaseCammelCase(), is(SLEEP_RPC_INPUT_NAME));
+        assertThat(attribute.getUpperCaseCammelCase(), is("Millis"));
+        assertNull(attribute.getNullableDefault());
+        assertNull(attribute.getNullableDescription());
+        assertThat(attribute.getOpenType(), is(SimpleType.class));
+    }
+
+    private RuntimeBeanEntry getRuntimeBeanEntryByJavaName(
+            final Collection<RuntimeBeanEntry> runtimeBeanEntries,
+            String javaName) {
+        if (runtimeBeanEntries != null && !runtimeBeanEntries.isEmpty()) {
+            for (RuntimeBeanEntry runtimeBeanEntry : runtimeBeanEntries) {
+                if (runtimeBeanEntry.getJavaNameOfRuntimeMXBean().equals(
+                        javaName)) {
+                    return runtimeBeanEntry;
+                }
+            }
+        }
+        return null;
+    }
+
+    private RuntimeBeanEntry.Rpc getRpcByName(
+            final List<RuntimeBeanEntry.Rpc> rpcs, String name) {
+        if (rpcs != null && !rpcs.isEmpty()) {
+            for (RuntimeBeanEntry.Rpc rpc : rpcs) {
+                if (rpc.getName().equals(name)) {
+                    return rpc;
+                }
+            }
+        }
+        return null;
+    }
+
+}
diff --git a/opendaylight/config/yang-jmx-generator/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/RuntimeRegistratorTest.java b/opendaylight/config/yang-jmx-generator/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/RuntimeRegistratorTest.java
new file mode 100644 (file)
index 0000000..6dd6444
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * 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;
+
+import static org.apache.commons.lang3.StringUtils.capitalize;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
+import java.net.URI;
+import java.util.Collections;
+import java.util.List;
+
+import org.junit.Test;
+import org.opendaylight.controller.config.yangjmxgenerator.RuntimeBeanEntry.Rpc;
+import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc;
+import org.opendaylight.yangtools.sal.binding.model.api.Type;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+
+import com.google.common.base.Optional;
+
+public class RuntimeRegistratorTest {
+    // TODO add more tests
+    protected RuntimeBeanEntry prepareRootRB(List<RuntimeBeanEntry> children) {
+
+        DataSchemaNode dataSchemaNodeForReporting = mock(DataSchemaNode.class);
+        doReturn("DataSchemaNode").when(dataSchemaNodeForReporting).toString();
+        return new RuntimeBeanEntry("pa.cka.ge", dataSchemaNodeForReporting,
+                "module-name", "ModuleName", true, Optional.<String> absent(),
+                Collections.<AttributeIfc> emptyList(), children,
+                Collections.<Rpc> emptySet());
+    }
+
+    protected RuntimeBeanEntry prepareChildRB(List<RuntimeBeanEntry> children,
+            String prefix) {
+        DataSchemaNode dataSchemaNodeForReporting = mock(DataSchemaNode.class);
+        doReturn("DataSchemaNode").when(dataSchemaNodeForReporting).toString();
+        return new RuntimeBeanEntry("pa.cka.ge", dataSchemaNodeForReporting,
+                prefix + "child-name", capitalize(prefix) + "ChildName", false,
+                Optional.<String> absent(),
+                Collections.<AttributeIfc> emptyList(), children,
+                Collections.<Rpc> emptySet());
+    }
+
+    @Test
+    public void testHierarchy() {
+        LeafSchemaNode leaf = mock(LeafSchemaNode.class);
+        doReturn(new QName(URI.create("urn:x"), "leaf-local-name")).when(leaf)
+                .getQName();
+        doReturn(Collections.emptyList()).when(leaf).getUnknownSchemaNodes();
+        doReturn(null).when(leaf).getDefault();
+        doReturn(null).when(leaf).getDescription();
+
+        TypeProviderWrapper typeProviderWrapper = mock(TypeProviderWrapper.class);
+        Type mockedType = mock(Type.class);
+        doReturn(mockedType).when(typeProviderWrapper).getType(leaf);
+        doReturn("java.lang.String").when(mockedType).getFullyQualifiedName();
+
+    }
+
+}
diff --git a/opendaylight/config/yang-jmx-generator/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/SchemaContextTest.java b/opendaylight/config/yang-jmx-generator/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/SchemaContextTest.java
new file mode 100644 (file)
index 0000000..24c0257
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * 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;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.opendaylight.controller.config.yangjmxgenerator.ConfigConstants.MODULE_TYPE_Q_NAME;
+import static org.opendaylight.controller.config.yangjmxgenerator.ConfigConstants.SERVICE_TYPE_Q_NAME;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableMap;
+
+public class SchemaContextTest extends AbstractYangTest {
+
+    IdentitySchemaNode findIdentityByQName(Module module, QName qName) {
+        Map<QName, IdentitySchemaNode> mapIdentitiesByQNames = mapIdentitiesByQNames(module);
+        IdentitySchemaNode found = mapIdentitiesByQNames.get(qName);
+        assertNotNull(found);
+        return found;
+    }
+
+    @Test
+    public void testReadingIdentities_threadsModule() {
+
+        IdentitySchemaNode serviceType = findIdentityByQName(configModule,
+                SERVICE_TYPE_Q_NAME);
+
+        Map<String /* identity name */, Optional<QName>> expectedIdentitiesToBases = ImmutableMap
+                .of("eventbus", Optional.<QName>absent(), "threadfactory", Optional.<QName>absent(), "threadpool",
+                        Optional.<QName>absent(), "scheduled-threadpool", Optional.<QName>absent());
+
+        assertThat(threadsModule.getIdentities().size(),
+                is(expectedIdentitiesToBases.size()));
+        assertAllIdentitiesAreExpected(threadsModule, expectedIdentitiesToBases);
+
+        IdentitySchemaNode eventBusSchemaNode = null;
+        for (IdentitySchemaNode id : threadsModule.getIdentities()) {
+            String localName = id.getQName().getLocalName();
+
+            if (localName.equals("eventbus")) {
+                eventBusSchemaNode = id;
+            }
+            // all except scheduled-threadpool should have base set to
+            // serviceType
+            if (localName.equals("scheduled-threadpool") == false) {
+                assertEquals(serviceType, id.getBaseIdentity());
+            }
+        }
+        assertNotNull(eventBusSchemaNode);
+        // check unknown schma nodes
+        List<UnknownSchemaNode> unknownSchemaNodes = eventBusSchemaNode
+                .getUnknownSchemaNodes();
+        assertEquals(1, unknownSchemaNodes.size());
+        UnknownSchemaNode usn = unknownSchemaNodes.get(0);
+        assertEquals("com.google.common.eventbus.EventBus", usn.getQName()
+                .getLocalName());
+        assertEquals(ConfigConstants.JAVA_CLASS_EXTENSION_QNAME,
+                usn.getNodeType());
+    }
+
+    private void assertAllIdentitiesAreExpected(
+            Module module,
+            Map<String /* identity name */, Optional<QName>> expectedIdentitiesToBases) {
+        Map<String /* identity name */, Optional<QName>> copyOfExpectedNames = new HashMap<>(
+                expectedIdentitiesToBases);
+        for (IdentitySchemaNode id : module.getIdentities()) {
+            String localName = id.getQName().getLocalName();
+            assertTrue("Unexpected identity " + localName,
+                    copyOfExpectedNames.containsKey(localName));
+            Optional<QName> maybeExpectedBaseQName = copyOfExpectedNames
+                    .remove(localName);
+            if (maybeExpectedBaseQName.isPresent()) {
+                assertEquals("Unexpected base identity of " + localName,
+                        maybeExpectedBaseQName.get(), id.getBaseIdentity()
+                                .getQName());
+            }
+        }
+        assertEquals("Expected identities not found " + copyOfExpectedNames,
+                Collections.EMPTY_MAP, copyOfExpectedNames);
+    }
+
+    @Test
+    public void testReadingIdentities_threadsJavaModule() {
+        Map<String /* identity name */, Optional<QName>> expectedIdentitiesToBases = ImmutableMap
+                .of("eventbus", Optional.of(MODULE_TYPE_Q_NAME), "async-eventbus", Optional.of(MODULE_TYPE_Q_NAME),
+                        "threadfactory-naming", Optional.of(MODULE_TYPE_Q_NAME), "threadpool-dynamic",
+                        Optional.of(MODULE_TYPE_Q_NAME), "thread-rpc-context", Optional.<QName>absent());
+        assertAllIdentitiesAreExpected(threadsJavaModule,
+                expectedIdentitiesToBases);
+    }
+
+}
diff --git a/opendaylight/config/yang-jmx-generator/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/ServiceInterfaceEntryTest.java b/opendaylight/config/yang-jmx-generator/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/ServiceInterfaceEntryTest.java
new file mode 100644 (file)
index 0000000..0da61b4
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * 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;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import org.hamcrest.CoreMatchers;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+
+import com.google.common.collect.Sets;
+
+public class ServiceInterfaceEntryTest extends AbstractYangTest {
+    public static final String PACKAGE_NAME = "packages.sis";
+    public static final List<String> expectedSIEFileNames = toFileNames("[EventBusServiceInterface"
+            + ".java, "
+            + "ScheduledThreadPoolServiceInterface"
+            + ".java, ThreadFactoryServiceInterface.java, ThreadPoolServiceInterface.java]");
+
+    private static final URI THREADS_NAMESPACE;
+    private static final Date THREADS_REVISION_DATE;
+    static {
+        try {
+            THREADS_NAMESPACE = new URI(ConfigConstants.CONFIG_NAMESPACE
+                    + ":threads");
+        } catch (URISyntaxException e) {
+            throw new Error(e);
+        }
+        SimpleDateFormat revisionFormat = new SimpleDateFormat("yyyy-MM-dd");
+        try {
+            THREADS_REVISION_DATE = revisionFormat.parse("2013-04-09");
+        } catch (ParseException e) {
+            throw new Error(e);
+        }
+    }
+
+    public static final QName EVENTBUS_QNAME = new QName(THREADS_NAMESPACE,
+            THREADS_REVISION_DATE, "eventbus");
+    public static final QName THREADFACTORY_QNAME = new QName(
+            THREADS_NAMESPACE, THREADS_REVISION_DATE, "threadfactory");
+    public static final QName THREADPOOL_QNAME = new QName(THREADS_NAMESPACE,
+            THREADS_REVISION_DATE, "threadpool");
+    public static final QName SCHEDULED_THREADPOOL_QNAME = new QName(
+            THREADS_NAMESPACE, THREADS_REVISION_DATE, "scheduled-threadpool");
+    public static final QName SCHEDULED_EXECUTOR_SERVICE_QNAME = new QName(
+            THREADS_NAMESPACE, THREADS_REVISION_DATE,
+            "scheduled-executor-service");
+    public static final String SCHEDULED_THREADPOOL_INTERFACE_NAME = "ScheduledThreadPoolServiceInterface";
+
+    public static List<String> toFileNames(String fileNameString) {
+        assertThat(fileNameString.startsWith("["), CoreMatchers.is(true));
+        assertThat(fileNameString.endsWith("]"), CoreMatchers.is(true));
+        fileNameString = fileNameString.substring(1,
+                fileNameString.length() - 1);
+        return Arrays.asList(fileNameString.split(", "));
+    }
+
+    @Test
+    public void testCreateFromIdentities() {
+        // each identity has to have a base that leads to service-type
+        Map<QName, ServiceInterfaceEntry> namesToSIEntries = ServiceInterfaceEntry
+                .create(threadsModule, PACKAGE_NAME);
+        // expected eventbus, threadfactory, threadpool,
+        // scheduled-threadpool,thread-rpc-context
+        assertThat(namesToSIEntries.size(), is(expectedSIEFileNames.size()));
+
+        Set<QName> withNoBaseType = Sets.newHashSet(EVENTBUS_QNAME,
+                THREADFACTORY_QNAME, THREADPOOL_QNAME,
+                SCHEDULED_EXECUTOR_SERVICE_QNAME);
+        HashSet<QName> withBaseType = new HashSet<>();
+        for (Entry<QName, ServiceInterfaceEntry> entry : namesToSIEntries
+                .entrySet()) {
+            QName qName = entry.getKey();
+            if (withNoBaseType.contains(qName)) {
+                ServiceInterfaceEntry sie = namesToSIEntries.get(qName);
+                assertNotNull(qName + " not found", sie);
+                assertThat(qName + " should have empty base type", sie
+                        .getBase().isPresent(), is(false));
+                assertThat(sie.getQName(), is(qName));
+            } else {
+                withBaseType.add(qName);
+            }
+        }
+        // scheduled-threadpool has super type threadpool
+        assertThat(withBaseType,
+                is(Sets.newHashSet(SCHEDULED_THREADPOOL_QNAME)));
+        assertThat(withBaseType.contains(SCHEDULED_THREADPOOL_QNAME), is(true));
+        ServiceInterfaceEntry scheduled = namesToSIEntries
+                .get(SCHEDULED_THREADPOOL_QNAME);
+        assertNotNull(scheduled);
+        assertThat(scheduled.getQName(), is(SCHEDULED_THREADPOOL_QNAME));
+        ServiceInterfaceEntry threadPool = namesToSIEntries
+                .get(THREADPOOL_QNAME);
+        assertNotNull(threadPool);
+        assertThat("scheduled-threadpool should extend threadpool", scheduled
+                .getBase().get(), is(threadPool));
+
+        assertThat(scheduled.getExportedOsgiClassName(),
+                is(PackageTranslatorTest.EXPECTED_PACKAGE_PREFIX
+                        + ".threadpool.ScheduledThreadPool"));
+        assertThat(threadPool.getExportedOsgiClassName(),
+                is(PackageTranslatorTest.EXPECTED_PACKAGE_PREFIX
+                        + ".threadpool.ThreadPool"));
+
+        String expectedDescription = "An extension of the simple pool of threads able to schedule "
+                + "work to be executed at some point in time.";
+        assertThat(trimInnerSpacesOrNull(scheduled.getNullableDescription()),
+                is(expectedDescription));
+        assertThat(scheduled.getPackageName(), is(PACKAGE_NAME));
+        assertThat(scheduled.getTypeName(),
+                is(SCHEDULED_THREADPOOL_INTERFACE_NAME));
+        assertThat(scheduled.getFullyQualifiedName(), is(PACKAGE_NAME + "."
+                + SCHEDULED_THREADPOOL_INTERFACE_NAME));
+    }
+
+    static String trimInnerSpacesOrNull(String input) {
+        if (input == null)
+            return null;
+        return input.replaceAll("\\s{2,}", " ");
+    }
+}
diff --git a/opendaylight/config/yang-jmx-generator/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/util/YangModelSearchUtils.java b/opendaylight/config/yang-jmx-generator/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/util/YangModelSearchUtils.java
new file mode 100644 (file)
index 0000000..11c7d04
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * 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.util;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.opendaylight.yangtools.yang.model.api.Module;
+
+import com.google.common.base.Preconditions;
+
+public class YangModelSearchUtils {
+
+    public static Map<String, Module> mapModulesByNames(
+            Collection<Module> modules) {
+        Map<String, Module> result = new HashMap<>();
+        for (Module m : modules) {
+            String moduleName = m.getName();
+            Preconditions.checkArgument(
+                    result.containsKey(moduleName) == false,
+                    "Two modules have same name " + moduleName);
+            result.put(moduleName, m);
+        }
+        return result;
+    }
+}
diff --git a/opendaylight/config/yang-jmx-generator/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/unknownextension/UnknownExtensionTest.java b/opendaylight/config/yang-jmx-generator/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/unknownextension/UnknownExtensionTest.java
new file mode 100644 (file)
index 0000000..91e92f7
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * 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.unknownextension;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.InputStream;
+import java.util.List;
+import java.util.Set;
+
+import org.junit.Test;
+import org.opendaylight.controller.config.yangjmxgenerator.ConfigConstants;
+import org.opendaylight.controller.config.yangjmxgenerator.ServiceInterfaceEntryTest;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.util.YangModelSearchUtils;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
+
+import com.google.common.collect.Lists;
+
+public class UnknownExtensionTest extends ServiceInterfaceEntryTest {
+
+    @Test
+    public void testStopOnUnknownLanguageExtension() throws Exception {
+        List<InputStream> yangISs = Lists.newArrayList(getClass()
+                .getResourceAsStream("test-ifcWithUnknownExtension.yang"));
+        yangISs.addAll(getConfigApiYangInputStreams());
+        try {
+            YangParserImpl parser = new YangParserImpl();
+            Set<Module> modulesToBuild = parser
+                    .parseYangModelsFromStreams(yangISs);
+            context = parser.resolveSchemaContext(modulesToBuild);
+            namesToModules = YangModelSearchUtils.mapModulesByNames(context
+                    .getModules());
+            configModule = namesToModules.get(ConfigConstants.CONFIG_MODULE);
+            threadsModule = namesToModules
+                    .get(ConfigConstants.CONFIG_THREADS_MODULE);
+            try {
+                super.testCreateFromIdentities();
+                fail();
+            } catch (IllegalStateException e) {
+                assertTrue(
+                        e.getMessage(),
+                        e.getMessage().startsWith(
+                                "Unexpected unknown schema node."));
+            }
+        } finally {
+            for (InputStream is : yangISs) {
+                is.close();
+            }
+        }
+    }
+
+}
diff --git a/opendaylight/config/yang-jmx-generator/src/test/resources/config-bgp-listener-impl.yang b/opendaylight/config/yang-jmx-generator/src/test/resources/config-bgp-listener-impl.yang
new file mode 100644 (file)
index 0000000..fca83fd
--- /dev/null
@@ -0,0 +1,43 @@
+module config-bgp-listener-impl {
+    yang-version 1;
+    namespace "urn:opendaylight:params:xml:ns:yang:controller:config:bgp:listener:impl";
+    prefix "bgpl-impl";
+
+    import ietf-inet-types { prefix inet; }
+    import config { prefix config; revision-date 2013-04-05; }
+
+    description
+        "This module contains the base YANG definitions for NS-OS
+         BGP listener implementation.";
+
+    revision "2013-04-09" {
+        description
+            "Initial revision";
+        reference "NS-OS System Design, version 1.2.";
+    }
+
+    identity bgp-listener-impl {
+        base config:module-type;
+    }
+
+    augment "/config:modules/config:module/config:state" {
+        case bgp-listener-impl {
+            when "/config:modules/config:module/config:type = 'bgp-listener-impl'";
+            list peers {
+                config:inner-state-bean;
+                leaf port {
+                    type inet:port-number;
+                    default 179;
+                }
+                leaf core-size {
+                    type uint32;
+                }
+            }
+
+           leaf as-number {
+                mandatory true;
+                type inet:as-number;
+            }
+        }
+    }
+}
diff --git a/opendaylight/config/yang-jmx-generator/src/test/resources/config-jmx-it-impl.yang b/opendaylight/config/yang-jmx-generator/src/test/resources/config-jmx-it-impl.yang
new file mode 100644 (file)
index 0000000..97078e0
--- /dev/null
@@ -0,0 +1,217 @@
+module config-jmx-it-impl {
+    yang-version 1;
+    namespace "urn:opendaylight:params:xml:ns:yang:controller:config:jmx:generator:it:impl";
+    prefix "it-impl";
+
+    import config-jmx-it { prefix jmxIt; revision-date 2013-06-13;}
+    import config { prefix config; revision-date 2013-04-05; }
+    import ietf-inet-types { prefix inet; revision-date 2010-09-24;}
+    import config-threads { prefix th; revision-date 2013-04-09; }
+
+
+
+    description
+        "Testing IMPL";
+
+    revision "2013-04-03" {
+        description
+            "Initial revision";
+    }
+
+    identity impl {
+        base config:module-type;
+        config:provided-service jmxIt:testing;
+        config:java-name-prefix TestImpl;
+    }
+
+    identity impl-netconf {
+        base config:module-type;
+        config:provided-service jmxIt:testing;
+        config:java-name-prefix NetconfTestImpl;
+    }
+
+    augment "/config:modules/config:module/config:configuration" {
+        case impl {
+            when "/config:modules/config:module/config:type = 'impl'";
+
+           container dto-a1 {
+                leaf simple-arg {
+                    type uint32;
+                }
+
+                leaf port {
+                    type inet:port-number;
+                }
+
+            }
+
+            leaf as-number {
+                mandatory true;
+                type inet:as-number;
+            }
+
+
+            leaf simpleInt {
+                type uint32;
+                default 99L;
+            }
+
+            container dto_b {
+                leaf simple-int1 {
+                    type uint32;
+                }
+
+                leaf simple-int2 {
+                    type uint32;
+                }
+            }
+
+        }
+    }
+
+    augment "/config:modules/config:module/config:state" {
+        case impl {
+            when "/config:modules/config:module/config:type = 'impl'";
+            // root runtime bean
+            leaf created-sessions {
+                type uint32;
+            }
+        }
+    }
+
+    augment "/config:modules/config:module/config:configuration" {
+        case impl-netconf {
+            when "/config:modules/config:module/config:type = 'impl-netconf'";
+
+           container dto-a {
+                leaf simple-arg {
+                    type uint32;
+                }
+
+                container dto-a-inner {
+                    leaf simple-arg {
+                        type uint32;
+                    }
+
+                    container dto-a-inner-inner {
+                        leaf simple-arg {
+                            type uint32;
+                        }
+                    }
+                }
+            }
+
+            leaf simpleInt {
+                type uint32;
+            }
+
+            leaf simpleBoolean {
+                type boolean;
+                default false;
+            }
+
+            leaf simple-long {
+                type int64  ;
+            }
+
+            leaf simple-long-2 {
+                type uint32;
+            }
+
+            leaf simple-BigInteger {
+                type uint64;
+            }
+
+            leaf simple-byte {
+                type int8;
+            }
+
+            leaf simple-short {
+                type uint8;
+            }
+
+            leaf simple-test {
+                type uint16;
+                default 99;
+            }
+
+            leaf-list simple-list {
+                type uint16;
+            }
+
+            container dto_c {
+                leaf simple-int1 {
+                    type uint32;
+                }
+
+                leaf simple-int2 {
+                    type uint32;
+                }
+
+                leaf simple-int3 {
+                    type uint16;
+                }
+
+                leaf-list simple-list {
+                    type uint16;
+                }
+
+                list complex-dto-bInner {
+                    leaf-list simple-list {
+                        type uint16;
+                    }
+                    leaf simple-int3 {
+                        type uint16;
+                    }
+
+                    container deep {
+                        leaf simple-int3 {
+                            type uint16;
+                        }
+                    }
+                }
+            }
+
+            list complex-list {
+                list simple-list {
+                    leaf simple-int3 {
+                        type uint16;
+                    }
+                }
+            }
+
+            list peers {
+                    config:java-name-prefix Peer;
+                    leaf port {
+                        type string;
+                    }
+                    leaf core-size {
+                        type uint32;
+                    }
+                    leaf simple-int3 {
+                        type uint16;
+                    }
+            }
+
+            container thread-factory {
+                uses config:service-ref {
+                    refine type {
+                        mandatory false;
+                        config:required-identity th:threadfactory;
+                    }
+                }
+            }
+        }
+    }
+
+    augment "/config:modules/config:module/config:state" {
+        case impl-netconf {
+            when "/config:modules/config:module/config:type = 'impl-netconf'";
+            // root runtime bean
+            leaf created-sessions {
+                type uint32;
+            }
+
+        }
+    }
+}
diff --git a/opendaylight/config/yang-jmx-generator/src/test/resources/config-jmx-it.yang b/opendaylight/config/yang-jmx-generator/src/test/resources/config-jmx-it.yang
new file mode 100644 (file)
index 0000000..e2b45b8
--- /dev/null
@@ -0,0 +1,26 @@
+// vi: set smarttab et sw=4 tabstop=4:
+module config-jmx-it {
+    yang-version 1;
+    namespace "urn:opendaylight:params:xml:ns:yang:controller:config:jmx:generator:it";
+    prefix "jmxIt";
+
+    import config { prefix config; revision-date 2013-04-05; }
+
+
+
+    description
+        "Testing API";
+
+    revision "2013-06-13" {
+        description
+            "Initial revision";
+    }
+
+    identity testing {
+        description
+            "Test api";
+
+        base "config:service-type";
+        config:java-class "java.lang.AutoCloseable";
+    }
+}
diff --git a/opendaylight/config/yang-jmx-generator/src/test/resources/duplicates/config-test-duplicate-attribute-in-list.yang b/opendaylight/config/yang-jmx-generator/src/test/resources/duplicates/config-test-duplicate-attribute-in-list.yang
new file mode 100644 (file)
index 0000000..4901b41
--- /dev/null
@@ -0,0 +1,84 @@
+// vi: set smarttab et sw=4 tabstop=4:
+module config-test-duplicate-attribute-in-list {
+    yang-version 1;
+    namespace "urn:opendaylight:params:xml:ns:yang:controller:jmx:generator:it:duplicate";
+    prefix "it-duplicate";
+
+    import config { prefix config; revision-date 2013-04-05; }
+    import ietf-inet-types { prefix inet; revision-date 2010-09-24;}
+
+    description
+        "Testing IMPL";
+
+    revision "2013-04-03" {
+        description
+            "Initial revision";
+    }
+
+    identity implementation {
+        base config:module-type;
+        config:java-name-prefix TestImpl;
+    }
+
+    identity netconf {
+        base config:module-type;
+        config:java-name-prefix NetconfTestImpl;
+    }
+
+    augment "/config:modules/config:module/config:configuration" {
+        case implementation {
+            when "/config:modules/config:module/config:type = 'implementation'";
+
+           container dto-a {
+                leaf simple-arg {
+                    type uint32;
+                }
+
+                leaf port {
+                    type inet:port-number;
+                }
+
+            }
+        }
+    }
+
+    augment "/config:modules/config:module/config:state" {
+        case implementation {
+            when "/config:modules/config:module/config:type = 'implementation'";
+            // root runtime bean
+            leaf created-sessions {
+                type uint32;
+            }
+        }
+    }
+
+    augment "/config:modules/config:module/config:configuration" {
+        case netconf {
+            when "/config:modules/config:module/config:type = 'netconf'";
+
+           list dtos {
+                   config:java-name-prefix dto-a;
+                       leaf port {
+                           type string;
+                       }
+                       leaf core-size {
+                           type uint32;
+                       }
+                       leaf simple-int3 {
+                           type uint16;
+                       }
+           }
+        }
+    }
+
+    augment "/config:modules/config:module/config:state" {
+        case netconf {
+            when "/config:modules/config:module/config:type = 'netconf'";
+            // root runtime bean
+            leaf created-sessions {
+                type uint32;
+            }
+
+        }
+    }
+}
diff --git a/opendaylight/config/yang-jmx-generator/src/test/resources/duplicates/config-test-duplicate-attribute-runtime-bean.yang b/opendaylight/config/yang-jmx-generator/src/test/resources/duplicates/config-test-duplicate-attribute-runtime-bean.yang
new file mode 100644 (file)
index 0000000..44e6dd0
--- /dev/null
@@ -0,0 +1,63 @@
+// vi: set smarttab et sw=4 tabstop=4:
+module config-test-duplicate-attribute-runtime-bean {
+    yang-version 1;
+    namespace "urn:opendaylight:params:xml:ns:yang:controller:jmx:duplicate:runtime";
+    prefix "th-java";
+
+    import config { prefix config; revision-date 2013-04-05; }
+    import rpc-context { prefix rpcx; revision-date 2013-06-17; }
+    import ietf-inet-types { prefix inet; revision-date 2010-09-24;}
+
+
+    description
+        "This module contains the base YANG definitions for NS-OS
+         thread services pure Java implementation.";
+
+    revision "2013-04-05" {
+        description
+            "Updated to work with new anchors.";
+    }
+
+    revision "2013-04-03" {
+        description
+            "Initial revision.";
+    }
+
+   identity async-eventbus {
+        base config:module-type;
+        config:java-name-prefix AsyncEventBus;
+    }
+
+    augment "/config:modules/config:module/config:configuration" {
+        case async-eventbus {
+            when "/config:modules/config:module/config:type = 'async-eventbus'";
+            list dtos {
+                config:java-name-prefix dto-a;
+                leaf port {
+                    type string;
+                }
+                leaf core-size {
+                    type uint32;
+                }
+                leaf simple-int3 {
+                    type uint16;
+                }
+            }
+        }
+    }
+
+    augment "/config:modules/config:module/config:state" {
+        case async-eventbus {
+            when "/config:modules/config:module/config:type = 'async-eventbus'";
+            container dto-a {
+                leaf simple-arg {
+                    type uint32;
+                }
+
+                leaf port {
+                    type inet:port-number;
+                }
+            }
+        }
+    }
+}
diff --git a/opendaylight/config/yang-jmx-generator/src/test/resources/duplicates/config-test-duplicate-attribute.yang b/opendaylight/config/yang-jmx-generator/src/test/resources/duplicates/config-test-duplicate-attribute.yang
new file mode 100644 (file)
index 0000000..bf08102
--- /dev/null
@@ -0,0 +1,81 @@
+// vi: set smarttab et sw=4 tabstop=4:
+module config-test-duplicate-attribute {
+    yang-version 1;
+    namespace "urn:opendaylight:params:xml:ns:yang:controller:jmx:generator:it:duplicate";
+    prefix "it-duplicate";
+
+    import config { prefix config; revision-date 2013-04-05; }
+    import ietf-inet-types { prefix inet; revision-date 2010-09-24;}
+
+    description
+        "Testing IMPL";
+
+    revision "2013-04-03" {
+        description
+            "Initial revision";
+    }
+
+    identity implementation {
+        base config:module-type;
+        config:java-name-prefix TestImpl;
+    }
+
+    identity netconf {
+        base config:module-type;
+        config:java-name-prefix NetconfTestImpl;
+    }
+
+    augment "/config:modules/config:module/config:configuration" {
+        case implementation {
+            when "/config:modules/config:module/config:type = 'implementation'";
+
+           container dto-a {
+                leaf simple-arg {
+                    type uint32;
+                }
+
+                leaf port {
+                    type inet:port-number;
+                }
+
+            }
+        }
+    }
+
+    augment "/config:modules/config:module/config:state" {
+        case implementation {
+            when "/config:modules/config:module/config:type = 'implementation'";
+            // root runtime bean
+            leaf created-sessions {
+                type uint32;
+            }
+        }
+    }
+
+    augment "/config:modules/config:module/config:configuration" {
+        case netconf {
+            when "/config:modules/config:module/config:type = 'netconf'";
+
+           container dto-a {
+                leaf simple-arg {
+                       type uint32;
+                   }
+
+                   leaf port {
+                       type inet:port-number;
+                   }
+            }
+        }
+    }
+
+    augment "/config:modules/config:module/config:state" {
+        case netconf {
+            when "/config:modules/config:module/config:type = 'netconf'";
+            // root runtime bean
+            leaf created-sessions {
+                type uint32;
+            }
+
+        }
+    }
+}
diff --git a/opendaylight/config/yang-jmx-generator/src/test/resources/duplicates/config-test-generated-attributes-name-conflict.yang b/opendaylight/config/yang-jmx-generator/src/test/resources/duplicates/config-test-generated-attributes-name-conflict.yang
new file mode 100644 (file)
index 0000000..b437b64
--- /dev/null
@@ -0,0 +1,69 @@
+// vi: set smarttab et sw=4 tabstop=4:
+module config-test-generated-attributes-name-conflict {
+     yang-version 1;
+        namespace "urn:opendaylight:params:xml:ns:yang:controller:jmx:duplicate:runtime:bean";
+        prefix "th-java";
+
+        import config { prefix config; revision-date 2013-04-05; }
+        import ietf-inet-types { prefix inet; revision-date 2010-09-24;}
+
+        description
+            "This module contains the base YANG definitions for NS-OS
+             thread services pure Java implementation.";
+
+        revision "2013-04-05" {
+            description
+                "Updated to work with new anchors.";
+        }
+
+        revision "2013-04-03" {
+            description
+                "Initial revision";
+        }
+
+       identity async-eventbus {
+            base config:module-type;
+            config:java-name-prefix AsyncEventBus;
+        }
+
+        augment "/config:modules/config:module/config:configuration" {
+            case async-eventbus {
+                when "/config:modules/config:module/config:type = 'async-eventbus'";
+                    leaf port {
+                        type string;
+                    }
+
+                    list state-b {
+                      leaf port {
+                          type string;
+                      }
+                    }
+
+                    container stateB {
+                      leaf port {
+                          type string;
+                      }
+                    }
+                }
+            }
+
+        augment "/config:modules/config:module/config:state" {
+            case async-eventbus {
+                when "/config:modules/config:module/config:type = 'async-eventbus'";
+
+                list state-a {
+                   config:inner-state-bean;
+
+                   leaf port {
+                       type string;
+                   }
+
+                   list state-b {
+                      leaf port {
+                          type string;
+                      }
+                   }
+                }
+            }
+        }
+}
diff --git a/opendaylight/config/yang-jmx-generator/src/test/resources/duplicates/config-test-runtime-bean-list-name-conflict.yang b/opendaylight/config/yang-jmx-generator/src/test/resources/duplicates/config-test-runtime-bean-list-name-conflict.yang
new file mode 100644 (file)
index 0000000..c39692d
--- /dev/null
@@ -0,0 +1,57 @@
+// vi: set smarttab et sw=4 tabstop=4:
+module config-test-runtime-bean-list-name-conflict {
+     yang-version 1;
+        namespace "urn:opendaylight:params:xml:ns:yang:controller:jmx:duplicate:runtime:bean";
+        prefix "th-java";
+
+        import config { prefix config; revision-date 2013-04-05; }
+        import ietf-inet-types { prefix inet; revision-date 2010-09-24;}
+
+        description
+            "This module contains the base YANG definitions for NS-OS
+             thread services pure Java implementation.";
+
+        revision "2013-04-05" {
+            description
+                "Updated to work with new anchors.";
+        }
+
+        revision "2013-04-03" {
+            description
+                "Initial revision";
+        }
+
+       identity async-eventbus {
+            base config:module-type;
+            config:java-name-prefix AsyncEventBus;
+        }
+
+        augment "/config:modules/config:module/config:configuration" {
+            case async-eventbus {
+                when "/config:modules/config:module/config:type = 'async-eventbus'";
+                    leaf port {
+                        type string;
+                    }
+                }
+            }
+
+        augment "/config:modules/config:module/config:state" {
+            case async-eventbus {
+                when "/config:modules/config:module/config:type = 'async-eventbus'";
+
+                list state-a-runtime-mX-bean {
+                   leaf port {
+                       type string;
+                   }
+                }
+
+                list state-a {
+                   config:inner-state-bean;
+
+                   leaf port {
+                       type string;
+                   }
+                }
+            }
+        }
+}
diff --git a/opendaylight/config/yang-jmx-generator/src/test/resources/duplicates/config-test-runtime-bean-list-name-conflict2.yang b/opendaylight/config/yang-jmx-generator/src/test/resources/duplicates/config-test-runtime-bean-list-name-conflict2.yang
new file mode 100644 (file)
index 0000000..1acba73
--- /dev/null
@@ -0,0 +1,57 @@
+// vi: set smarttab et sw=4 tabstop=4:
+module config-test-runtime-bean-list-name-conflict2 {
+     yang-version 1;
+        namespace "urn:opendaylight:params:xml:ns:yang:controller:jmx:duplicate:runtime:bean";
+        prefix "th-java";
+
+        import config { prefix config; revision-date 2013-04-05; }
+        import ietf-inet-types { prefix inet; revision-date 2010-09-24;}
+
+        description
+            "This module contains the base YANG definitions of
+             thread services pure Java implementation.";
+
+        revision "2013-04-05" {
+            description
+                "Updated to work with new anchors.";
+        }
+
+        revision "2013-04-03" {
+            description
+                "Initial revision";
+        }
+
+       identity async-eventbus {
+            base config:module-type;
+            config:java-name-prefix AsyncEventBus;
+        }
+
+        augment "/config:modules/config:module/config:configuration" {
+            case async-eventbus {
+                when "/config:modules/config:module/config:type = 'async-eventbus'";
+                    leaf port {
+                        type string;
+                    }
+
+                    list state-a-runtime-mX-bean {
+                       leaf port {
+                           type string;
+                       }
+                    }
+                }
+            }
+
+        augment "/config:modules/config:module/config:state" {
+            case async-eventbus {
+                when "/config:modules/config:module/config:type = 'async-eventbus'";
+
+                list state-a {
+                   config:inner-state-bean;
+
+                   leaf port {
+                       type string;
+                   }
+                }
+            }
+        }
+}
diff --git a/opendaylight/config/yang-jmx-generator/src/test/resources/duplicates/config-test-runtime-bean-name-conflict.yang b/opendaylight/config/yang-jmx-generator/src/test/resources/duplicates/config-test-runtime-bean-name-conflict.yang
new file mode 100644 (file)
index 0000000..b9f2140
--- /dev/null
@@ -0,0 +1,59 @@
+// vi: set smarttab et sw=4 tabstop=4:
+module config-test-runtime-bean-name-conflict {
+     yang-version 1;
+        namespace "urn:opendaylight:params:xml:ns:yang:controller:jmx:duplicate:runtime:bean";
+        prefix "th-java";
+
+        import config { prefix config; revision-date 2013-04-05; }
+        import ietf-inet-types { prefix inet; revision-date 2010-09-24;}
+
+        description
+            "This module contains the base YANG definitions for
+             thread services pure Java implementation.";
+
+        revision "2013-04-05" {
+            description
+                "Updated to work with new anchors.";
+        }
+
+        revision "2013-04-03" {
+            description
+                "Initial revision";
+        }
+
+       identity async-eventbus {
+            base config:module-type;
+            config:java-name-prefix AsyncEventBus;
+        }
+
+        augment "/config:modules/config:module/config:configuration" {
+            case async-eventbus {
+                when "/config:modules/config:module/config:type = 'async-eventbus'";
+                    leaf port {
+                        type string;
+                    }
+                }
+            }
+
+        augment "/config:modules/config:module/config:state" {
+            case async-eventbus {
+                when "/config:modules/config:module/config:type = 'async-eventbus'";
+
+                list state-a {
+                   config:inner-state-bean;
+
+                   leaf port {
+                       type string;
+                   }
+
+                   list state-a {
+                       config:inner-state-bean;
+
+                      leaf port {
+                          type string;
+                      }
+                   }
+                }
+            }
+        }
+}
diff --git a/opendaylight/config/yang-jmx-generator/src/test/resources/duplicates/config-test-runtime-bean-name-conflict2.yang b/opendaylight/config/yang-jmx-generator/src/test/resources/duplicates/config-test-runtime-bean-name-conflict2.yang
new file mode 100644 (file)
index 0000000..9f90480
--- /dev/null
@@ -0,0 +1,77 @@
+// vi: set smarttab et sw=4 tabstop=4:
+module config-test-runtime-bean-name-conflict2 {
+     yang-version 1;
+        namespace "urn:opendaylight:params:xml:ns:yang:controller:jmx:duplicate:runtime:bean";
+        prefix "th-java";
+
+        import config { prefix config; revision-date 2013-04-05; }
+        import ietf-inet-types { prefix inet; revision-date 2010-09-24;}
+
+
+        description
+            "This module contains the base YANG definitions for
+             thread services pure Java implementation.";
+
+        revision "2013-04-05" {
+            description
+                "Initial";
+        }
+
+       identity async-eventbus {
+            base config:module-type;
+            config:java-name-prefix AsyncEventBus;
+        }
+
+        augment "/config:modules/config:module/config:configuration" {
+            case async-eventbus {
+                when "/config:modules/config:module/config:type = 'async-eventbus'";
+                    leaf port {
+                        type string;
+                    }
+                }
+            }
+
+        augment "/config:modules/config:module/config:state" {
+            case async-eventbus {
+                when "/config:modules/config:module/config:type = 'async-eventbus'";
+
+                list state-a {
+                   config:inner-state-bean;
+
+                   leaf port {
+                       type string;
+                   }
+                }
+            }
+        }
+
+
+        identity async-eventbus-b {
+            base config:module-type;
+            config:java-name-prefix AsyncEventBusB;
+        }
+
+        augment "/config:modules/config:module/config:configuration" {
+            case async-eventbus-b {
+                when "/config:modules/config:module/config:type = 'async-eventbus-b'";
+                    leaf port {
+                        type string;
+                    }
+                }
+            }
+
+        augment "/config:modules/config:module/config:state" {
+            case async-eventbus-b {
+                when "/config:modules/config:module/config:type = 'async-eventbus-b'";
+
+                list state-a {
+                   config:inner-state-bean;
+
+                   leaf port {
+                       type string;
+                   }
+
+                }
+            }
+        }
+}
diff --git a/opendaylight/config/yang-jmx-generator/src/test/resources/ietf-inet-types.yang b/opendaylight/config/yang-jmx-generator/src/test/resources/ietf-inet-types.yang
new file mode 100644 (file)
index 0000000..c3ada6c
--- /dev/null
@@ -0,0 +1,418 @@
+module ietf-inet-types {
+
+  namespace "urn:ietf:params:xml:ns:yang:ietf-inet-types";
+  prefix "inet";
+
+  organization
+   "IETF NETMOD (NETCONF Data Modeling Language) Working Group";
+
+  contact
+   "WG Web:   <http://tools.ietf.org/wg/netmod/>
+    WG List:  <mailto:netmod@ietf.org>
+
+    WG Chair: David Partain
+              <mailto:david.partain@ericsson.com>
+
+    WG Chair: David Kessens
+              <mailto:david.kessens@nsn.com>
+
+    Editor:   Juergen Schoenwaelder
+              <mailto:j.schoenwaelder@jacobs-university.de>";
+
+  description
+   "This module contains a collection of generally useful derived
+    YANG data types for Internet addresses and related things.
+
+    Copyright (c) 2010 IETF Trust and the persons identified as
+    authors of the code.  All rights reserved.
+
+    Redistribution and use in source and binary forms, with or without
+    modification, is permitted pursuant to, and subject to the license
+    terms contained in, the Simplified BSD License set forth in Section
+    4.c of the IETF Trust's Legal Provisions Relating to IETF Documents
+    (http://trustee.ietf.org/license-info).
+
+    This version of this YANG module is part of RFC 6021; see
+    the RFC itself for full legal notices.";
+
+  revision 2010-09-24 {
+    description
+     "Initial revision.";
+    reference
+     "RFC 6021: Common YANG Data Types";
+  }
+
+  /*** collection of protocol field related types ***/
+
+  typedef ip-version {
+    type enumeration {
+      enum unknown {
+        value "0";
+        description
+         "An unknown or unspecified version of the Internet protocol.";
+      }
+      enum ipv4 {
+        value "1";
+        description
+         "The IPv4 protocol as defined in RFC 791.";
+      }
+      enum ipv6 {
+        value "2";
+        description
+         "The IPv6 protocol as defined in RFC 2460.";
+      }
+    }
+    description
+     "This value represents the version of the IP protocol.
+
+      In the value set and its semantics, this type is equivalent
+      to the InetVersion textual convention of the SMIv2.";
+    reference
+     "RFC  791: Internet Protocol
+      RFC 2460: Internet Protocol, Version 6 (IPv6) Specification
+      RFC 4001: Textual Conventions for Internet Network Addresses";
+  }
+
+  typedef dscp {
+    type uint8 {
+      range "0..63";
+    }
+    description
+     "The dscp type represents a Differentiated Services Code-Point
+      that may be used for marking packets in a traffic stream.
+
+      In the value set and its semantics, this type is equivalent
+      to the Dscp textual convention of the SMIv2.";
+    reference
+     "RFC 3289: Management Information Base for the Differentiated
+                Services Architecture
+      RFC 2474: Definition of the Differentiated Services Field
+                (DS Field) in the IPv4 and IPv6 Headers
+      RFC 2780: IANA Allocation Guidelines For Values In
+                the Internet Protocol and Related Headers";
+  }
+
+  typedef ipv6-flow-label {
+    type uint32 {
+      range "0..1048575";
+    }
+    description
+     "The flow-label type represents flow identifier or Flow Label
+      in an IPv6 packet header that may be used to discriminate
+      traffic flows.
+
+      In the value set and its semantics, this type is equivalent
+      to the IPv6FlowLabel textual convention of the SMIv2.";
+    reference
+     "RFC 3595: Textual Conventions for IPv6 Flow Label
+      RFC 2460: Internet Protocol, Version 6 (IPv6) Specification";
+  }
+
+  typedef port-number {
+    type uint16 {
+      range "0..65535";
+    }
+    description
+     "The port-number type represents a 16-bit port number of an
+      Internet transport layer protocol such as UDP, TCP, DCCP, or
+      SCTP.  Port numbers are assigned by IANA.  A current list of
+      all assignments is available from <http://www.iana.org/>.
+
+      Note that the port number value zero is reserved by IANA.  In
+      situations where the value zero does not make sense, it can
+      be excluded by subtyping the port-number type.
+
+      In the value set and its semantics, this type is equivalent
+      to the InetPortNumber textual convention of the SMIv2.";
+    reference
+     "RFC  768: User Datagram Protocol
+      RFC  793: Transmission Control Protocol
+      RFC 4960: Stream Control Transmission Protocol
+      RFC 4340: Datagram Congestion Control Protocol (DCCP)
+      RFC 4001: Textual Conventions for Internet Network Addresses";
+  }
+
+  /*** collection of autonomous system related types ***/
+
+  typedef as-number {
+    type uint32;
+    description
+     "The as-number type represents autonomous system numbers
+      which identify an Autonomous System (AS).  An AS is a set
+      of routers under a single technical administration, using
+      an interior gateway protocol and common metrics to route
+      packets within the AS, and using an exterior gateway
+      protocol to route packets to other ASs'.  IANA maintains
+      the AS number space and has delegated large parts to the
+      regional registries.
+
+      Autonomous system numbers were originally limited to 16
+      bits.  BGP extensions have enlarged the autonomous system
+      number space to 32 bits.  This type therefore uses an uint32
+      base type without a range restriction in order to support
+      a larger autonomous system number space.
+
+      In the value set and its semantics, this type is equivalent
+      to the InetAutonomousSystemNumber textual convention of
+      the SMIv2.";
+    reference
+     "RFC 1930: Guidelines for creation, selection, and registration
+                of an Autonomous System (AS)
+      RFC 4271: A Border Gateway Protocol 4 (BGP-4)
+      RFC 4893: BGP Support for Four-octet AS Number Space
+      RFC 4001: Textual Conventions for Internet Network Addresses";
+  }
+
+  /*** collection of IP address and hostname related types ***/
+
+  typedef ip-address {
+    type union {
+      type inet:ipv4-address;
+      type inet:ipv6-address;
+    }
+    description
+     "The ip-address type represents an IP address and is IP
+      version neutral.  The format of the textual representations
+      implies the IP version.";
+  }
+
+  typedef ipv4-address {
+    type string {
+      pattern
+        '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}'
+      +  '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])'
+      + '(%[\p{N}\p{L}]+)?';
+    }
+    description
+      "The ipv4-address type represents an IPv4 address in
+       dotted-quad notation.  The IPv4 address may include a zone
+       index, separated by a % sign.
+
+       The zone index is used to disambiguate identical address
+       values.  For link-local addresses, the zone index will
+       typically be the interface index number or the name of an
+       interface.  If the zone index is not present, the default
+       zone of the device will be used.
+
+       The canonical format for the zone index is the numerical
+       format";
+  }
+
+  typedef ipv6-address {
+    type string {
+      pattern '((:|[0-9a-fA-F]{0,4}):)([0-9a-fA-F]{0,4}:){0,5}'
+            + '((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|'
+            + '(((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}'
+            + '(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])))'
+            + '(%[\p{N}\p{L}]+)?';
+      pattern '(([^:]+:){6}(([^:]+:[^:]+)|(.*\..*)))|'
+            + '((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?)'
+            + '(%.+)?';
+    }
+    description
+     "The ipv6-address type represents an IPv6 address in full,
+      mixed, shortened, and shortened-mixed notation.  The IPv6
+      address may include a zone index, separated by a % sign.
+
+      The zone index is used to disambiguate identical address
+      values.  For link-local addresses, the zone index will
+      typically be the interface index number or the name of an
+      interface.  If the zone index is not present, the default
+      zone of the device will be used.
+
+      The canonical format of IPv6 addresses uses the compressed
+      format described in RFC 4291, Section 2.2, item 2 with the
+      following additional rules: the :: substitution must be
+      applied to the longest sequence of all-zero 16-bit chunks
+      in an IPv6 address.  If there is a tie, the first sequence
+      of all-zero 16-bit chunks is replaced by ::.  Single
+      all-zero 16-bit chunks are not compressed.  The canonical
+      format uses lowercase characters and leading zeros are
+      not allowed.  The canonical format for the zone index is
+      the numerical format as described in RFC 4007, Section
+      11.2.";
+    reference
+     "RFC 4291: IP Version 6 Addressing Architecture
+      RFC 4007: IPv6 Scoped Address Architecture
+      RFC 5952: A Recommendation for IPv6 Address Text Representation";
+  }
+
+  typedef ip-prefix {
+    type union {
+      type inet:ipv4-prefix;
+      type inet:ipv6-prefix;
+    }
+    description
+     "The ip-prefix type represents an IP prefix and is IP
+      version neutral.  The format of the textual representations
+      implies the IP version.";
+  }
+
+  typedef ipv4-prefix {
+    type string {
+      pattern
+         '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}'
+       +  '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])'
+       + '/(([0-9])|([1-2][0-9])|(3[0-2]))';
+    }
+    description
+     "The ipv4-prefix type represents an IPv4 address prefix.
+      The prefix length is given by the number following the
+      slash character and must be less than or equal to 32.
+
+      A prefix length value of n corresponds to an IP address
+      mask that has n contiguous 1-bits from the most
+      significant bit (MSB) and all other bits set to 0.
+
+      The canonical format of an IPv4 prefix has all bits of
+      the IPv4 address set to zero that are not part of the
+      IPv4 prefix.";
+  }
+
+  typedef ipv6-prefix {
+    type string {
+      pattern '((:|[0-9a-fA-F]{0,4}):)([0-9a-fA-F]{0,4}:){0,5}'
+            + '((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|'
+            + '(((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}'
+            + '(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])))'
+            + '(/(([0-9])|([0-9]{2})|(1[0-1][0-9])|(12[0-8])))';
+      pattern '(([^:]+:){6}(([^:]+:[^:]+)|(.*\..*)))|'
+            + '((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?)'
+            + '(/.+)';
+    }
+    description
+     "The ipv6-prefix type represents an IPv6 address prefix.
+      The prefix length is given by the number following the
+      slash character and must be less than or equal 128.
+
+      A prefix length value of n corresponds to an IP address
+      mask that has n contiguous 1-bits from the most
+      significant bit (MSB) and all other bits set to 0.
+
+      The IPv6 address should have all bits that do not belong
+      to the prefix set to zero.
+
+      The canonical format of an IPv6 prefix has all bits of
+      the IPv6 address set to zero that are not part of the
+      IPv6 prefix.  Furthermore, IPv6 address is represented
+      in the compressed format described in RFC 4291, Section
+      2.2, item 2 with the following additional rules: the ::
+      substitution must be applied to the longest sequence of
+      all-zero 16-bit chunks in an IPv6 address.  If there is
+      a tie, the first sequence of all-zero 16-bit chunks is
+      replaced by ::.  Single all-zero 16-bit chunks are not
+      compressed.  The canonical format uses lowercase
+      characters and leading zeros are not allowed.";
+    reference
+     "RFC 4291: IP Version 6 Addressing Architecture";
+  }
+
+  /*** collection of domain name and URI types ***/
+
+  typedef domain-name {
+    type string {
+      pattern '((([a-zA-Z0-9_]([a-zA-Z0-9\-_]){0,61})?[a-zA-Z0-9]\.)*'
+           +  '([a-zA-Z0-9_]([a-zA-Z0-9\-_]){0,61})?[a-zA-Z0-9]\.?)'
+           +  '|\.';
+      length "1..253";
+    }
+    description
+     "The domain-name type represents a DNS domain name.  The
+      name SHOULD be fully qualified whenever possible.
+
+      Internet domain names are only loosely specified.  Section
+      3.5 of RFC 1034 recommends a syntax (modified in Section
+      2.1 of RFC 1123).  The pattern above is intended to allow
+      for current practice in domain name use, and some possible
+      future expansion.  It is designed to hold various types of
+      domain names, including names used for A or AAAA records
+      (host names) and other records, such as SRV records.  Note
+      that Internet host names have a stricter syntax (described
+      in RFC 952) than the DNS recommendations in RFCs 1034 and
+      1123, and that systems that want to store host names in
+      schema nodes using the domain-name type are recommended to
+      adhere to this stricter standard to ensure interoperability.
+
+      The encoding of DNS names in the DNS protocol is limited
+      to 255 characters.  Since the encoding consists of labels
+      prefixed by a length bytes and there is a trailing NULL
+      byte, only 253 characters can appear in the textual dotted
+      notation.
+
+      The description clause of schema nodes using the domain-name
+      type MUST describe when and how these names are resolved to
+      IP addresses.  Note that the resolution of a domain-name value
+      may require to query multiple DNS records (e.g., A for IPv4
+      and AAAA for IPv6).  The order of the resolution process and
+      which DNS record takes precedence can either be defined
+      explicitely or it may depend on the configuration of the
+      resolver.
+
+      Domain-name values use the US-ASCII encoding.  Their canonical
+      format uses lowercase US-ASCII characters.  Internationalized
+      domain names MUST be encoded in punycode as described in RFC
+      3492";
+    reference
+     "RFC  952: DoD Internet Host Table Specification
+      RFC 1034: Domain Names - Concepts and Facilities
+      RFC 1123: Requirements for Internet Hosts -- Application
+                and Support
+      RFC 2782: A DNS RR for specifying the location of services
+                (DNS SRV)
+      RFC 3492: Punycode: A Bootstring encoding of Unicode for
+                Internationalized Domain Names in Applications
+                (IDNA)
+      RFC 5891: Internationalizing Domain Names in Applications
+                (IDNA): Protocol";
+  }
+
+  typedef host {
+    type union {
+      type inet:ip-address;
+      type inet:domain-name;
+    }
+    description
+     "The host type represents either an IP address or a DNS
+      domain name.";
+  }
+
+  typedef uri {
+    type string;
+    description
+     "The uri type represents a Uniform Resource Identifier
+      (URI) as defined by STD 66.
+
+      Objects using the uri type MUST be in US-ASCII encoding,
+      and MUST be normalized as described by RFC 3986 Sections
+      6.2.1, 6.2.2.1, and 6.2.2.2.  All unnecessary
+      percent-encoding is removed, and all case-insensitive
+      characters are set to lowercase except for hexadecimal
+      digits, which are normalized to uppercase as described in
+      Section 6.2.2.1.
+
+      The purpose of this normalization is to help provide
+      unique URIs.  Note that this normalization is not
+      sufficient to provide uniqueness.  Two URIs that are
+      textually distinct after this normalization may still be
+      equivalent.
+
+      Objects using the uri type may restrict the schemes that
+      they permit.  For example, 'data:' and 'urn:' schemes
+      might not be appropriate.
+
+      A zero-length URI is not a valid URI.  This can be used to
+      express 'URI absent' where required.
+
+      In the value set and its semantics, this type is equivalent
+      to the Uri SMIv2 textual convention defined in RFC 5017.";
+    reference
+     "RFC 3986: Uniform Resource Identifier (URI): Generic Syntax
+      RFC 3305: Report from the Joint W3C/IETF URI Planning Interest
+                Group: Uniform Resource Identifiers (URIs), URLs,
+                and Uniform Resource Names (URNs): Clarifications
+                and Recommendations
+      RFC 5017: MIB Textual Conventions for Uniform Resource
+                Identifiers (URIs)";
+  }
+
+}
diff --git a/opendaylight/config/yang-jmx-generator/src/test/resources/org/opendaylight/controller/config/yangjmxgenerator/unknownextension/test-ifcWithUnknownExtension.yang b/opendaylight/config/yang-jmx-generator/src/test/resources/org/opendaylight/controller/config/yangjmxgenerator/unknownextension/test-ifcWithUnknownExtension.yang
new file mode 100644 (file)
index 0000000..0aae571
--- /dev/null
@@ -0,0 +1,32 @@
+// vi: set smarttab et sw=4 tabstop=4:
+module config-threads {
+    yang-version 1;
+    namespace "urn:opendaylight:params:xml:ns:yang:controller:threads";
+    prefix "th";
+
+    import config { prefix config; revision-date 2013-04-05; }
+
+    revision "2013-05-02" {
+        description
+            "Add test";
+    }
+
+    extension java-class2 {
+        description
+            "YANG language extension carrying the fully-qualified name of
+             a Java class. Code generation tools use the provided reference
+             to tie a specific construct to its Java representation.";
+
+        argument "name";
+    }
+
+    identity eventbus {
+        description
+            "Service representing an event bus. The service acts as message
+             router between event producers and event consumers";
+
+        base "config:service-type";
+        config:java-class2 "com.google.common.eventbus.EventBus";
+    }
+
+}
diff --git a/opendaylight/config/yang-jmx-generator/src/test/resources/test-config-files.yang b/opendaylight/config/yang-jmx-generator/src/test/resources/test-config-files.yang
new file mode 100644 (file)
index 0000000..1db0279
--- /dev/null
@@ -0,0 +1,71 @@
+module test-config-files {
+    yang-version 1;
+    namespace "urn:opendaylight:params:xml:ns:yang:controller:config:test:files";
+    prefix "it-duplicate";
+
+    import config { prefix config; revision-date 2013-04-05; }
+    import ietf-inet-types { prefix inet; revision-date 2010-09-24;}
+
+
+    description
+        "Testing IMPL";
+
+    revision "2013-04-03" {
+        description
+            "Initial revision";
+    }
+
+    identity implementation {
+        base config:module-type;
+        config:java-name-prefix TestFileImpl;
+    }
+
+    identity netconf {
+        base config:module-type;
+        config:java-name-prefix NetconfTestFileImpl;
+    }
+
+    augment "/config:modules/config:module/config:configuration" {
+        case implementation {
+            when "/config:modules/config:module/config:type = 'implementation'";
+
+           container dto-a {
+                leaf simple-arg {
+                    type uint32;
+                }
+
+                leaf port {
+                    type inet:port-number;
+                }
+
+            }
+        }
+    }
+
+    augment "/config:modules/config:module/config:state" {
+        case implementation {
+            when "/config:modules/config:module/config:type = 'implementation'";
+            // root runtime bean
+            leaf created-sessions {
+                type uint32;
+            }
+        }
+    }
+
+    augment "/config:modules/config:module/config:configuration" {
+        case netconf {
+            when "/config:modules/config:module/config:type = 'netconf'";
+        }
+    }
+
+    augment "/config:modules/config:module/config:state" {
+        case netconf {
+            when "/config:modules/config:module/config:type = 'netconf'";
+            // root runtime bean
+            leaf created-sessions {
+                type uint32;
+            }
+
+        }
+    }
+}
diff --git a/opendaylight/config/yang-jmx-generator/src/test/resources/test-config-files1.yang b/opendaylight/config/yang-jmx-generator/src/test/resources/test-config-files1.yang
new file mode 100644 (file)
index 0000000..0a152e7
--- /dev/null
@@ -0,0 +1,71 @@
+module test-config-files1 {
+    yang-version 1;
+    namespace "urn:opendaylight:params:xml:ns:yang:controller:config:test:files1";
+    prefix "it-duplicate";
+
+    import config { prefix config; revision-date 2013-04-05; }
+    import ietf-inet-types { prefix inet; revision-date 2010-09-24;}
+
+
+    description
+        "Testing IMPL";
+
+    revision "2013-04-03" {
+        description
+            "Initial revision";
+    }
+
+    identity implementation1 {
+        base config:module-type;
+        config:java-name-prefix TestFiles1Impl;
+    }
+
+    identity netconf1 {
+        base config:module-type;
+        config:java-name-prefix NetconfTestFiles1Impl;
+    }
+
+    augment "/config:modules/config:module/config:configuration" {
+        case implementation1 {
+            when "/config:modules/config:module/config:type = 'implementation1'";
+
+           container dto-a {
+                leaf simple-arg {
+                    type uint32;
+                }
+
+                leaf port {
+                    type inet:port-number;
+                }
+
+            }
+        }
+    }
+
+    augment "/config:modules/config:module/config:state" {
+        case implementation1 {
+            when "/config:modules/config:module/config:type = 'implementation1'";
+            // root runtime bean
+            leaf created-sessions {
+                type uint32;
+            }
+        }
+    }
+
+    augment "/config:modules/config:module/config:configuration" {
+        case netconf1 {
+            when "/config:modules/config:module/config:type = 'netconf1'";
+        }
+    }
+
+    augment "/config:modules/config:module/config:state" {
+        case netconf1 {
+            when "/config:modules/config:module/config:type = 'netconf1'";
+            // root runtime bean
+            leaf created-sessions {
+                type uint32;
+            }
+
+        }
+    }
+}
diff --git a/opendaylight/config/yang-jmx-generator/src/test/resources/test-config-threads-java.yang b/opendaylight/config/yang-jmx-generator/src/test/resources/test-config-threads-java.yang
new file mode 100644 (file)
index 0000000..2972cec
--- /dev/null
@@ -0,0 +1,244 @@
+module config-threads-java {
+    yang-version 1;
+    namespace "urn:opendaylight:params:xml:ns:yang:controller:config:threads:java";
+    prefix "th-java";
+
+    import config-threads { prefix th2; revision-date 2013-04-09; }
+    import config { prefix config; revision-date 2013-04-05; }
+    import rpc-context { prefix rpcx; revision-date 2013-06-17; }
+
+    description
+        "This module contains the base YANG definitions for NS-OS
+         thread services pure Java implementation.";
+
+    revision "2013-04-05" {
+        description
+            "Updated to work with new anchors.";
+    }
+
+    revision "2013-04-03" {
+        description
+            "Initial revision";
+    }
+
+   identity thread-rpc-context;
+
+    identity eventbus {
+        base config:module-type;
+        config:provided-service "th2:eventbus";
+        config:java-name-prefix EventBus;
+    }
+
+    identity async-eventbus {
+        base config:module-type;
+        config:provided-service "th2:eventbus";
+        config:java-name-prefix AsyncEventBus;
+    }
+
+    identity threadfactory-naming {
+        base config:module-type;
+        config:provided-service "th2:threadfactory";
+        config:java-name-prefix NamingThreadFactory;
+    }
+
+    identity threadpool-dynamic {
+        base config:module-type;
+        description "threadpool-dynamic description";
+        config:provided-service "th2:threadpool";
+        config:provided-service "th2:scheduled-threadpool";
+        config:java-name-prefix DynamicThreadPool;
+    }
+
+    augment "/config:modules/config:module/config:configuration" {
+        case eventbus {
+            when "/config:modules/config:module/config:type = 'eventbus'";
+            // No real configuration
+        }
+    }
+
+    augment "/config:modules/config:module/config:configuration" {
+        case async-eventbus {
+            when "/config:modules/config:module/config:type = 'async-eventbus'";
+            container threadpool {
+                uses config:service-ref {
+                    refine type {
+                        config:required-identity th2:threadpool;
+                    }
+                }
+            }
+        }
+    }
+    augment "/config:modules/config:module/config:state" {
+        case async-eventbus {
+            when "/config:modules/config:module/config:type = 'async-eventbus'";
+            // simulate not having root runtime bean
+            list event {
+                config:inner-state-bean;
+                key "name";
+                leaf name {
+                    type string;
+                }
+            }
+        }
+    }
+
+    typedef thread-state {
+        type enumeration {
+            enum "STARTED";
+            enum "STOPPED";
+        }
+        description "Enum type holding state in which a thread can be.";
+    }
+
+    augment "/config:modules/config:module/config:configuration" {
+        case threadfactory-naming {
+            when "/config:modules/config:module/config:type = 'threadfactory-naming'";
+            leaf name-prefix {
+                description "String that will be prefixed to each created thread. Suffix will be constructed from
+                    underscore (_) and auto-incremented index number.";
+                type string;
+            }
+        }
+    }
+
+    augment "/config:modules/config:module/config:state" {
+        case threadfactory-naming {
+            when "/config:modules/config:module/config:type = 'threadfactory-naming'";
+            list thread {
+                config:inner-state-bean;
+                key "name";
+                leaf name {
+                    type string;
+                }
+                // add rpc context
+                rpcx:rpc-context-instance "thread-rpc-context";
+
+                list stream {
+                    config:inner-state-bean;
+                    config:java-name-prefix ThreadStream;
+                    // no key â€“ key will be generated by incrementing a counter
+                    leaf timestamp {
+                        type string;
+                    }
+                    leaf state {
+                        type thread-state;
+                    }
+
+                    container peer {
+                        leaf port {
+                            type uint32;
+                            default 179;
+                        }
+                        leaf core-size {
+                            type uint32;
+                        }
+                    }
+
+                    list inner-stream-list {
+                        leaf timestamp {
+                            type string;
+                        }
+                    }
+
+                }
+            }
+
+            list stream {
+                config:inner-state-bean;
+
+                leaf timestamp {
+                    type string;
+                }
+            }
+
+            // root runtime bean
+            leaf created-sessions {
+                type uint32;
+            }
+        }
+    }
+
+
+    rpc dump-stack {
+        config:java-name-prefix dumpStack;
+        input {
+            uses rpcx:rpc-context-ref {
+                refine context-instance {
+                    rpcx:rpc-context-instance thread-rpc-context;
+                }
+            }
+        }
+    }
+
+    rpc sleep {
+        input {
+            uses rpcx:rpc-context-ref {
+                refine context-instance {
+                    rpcx:rpc-context-instance thread-rpc-context;
+                }
+            }
+            leaf millis {
+                type uint32;
+            }
+        }
+        output {
+            leaf result {
+                type thread-state;
+            }
+        }
+    }
+
+
+    augment "/config:modules/config:module/config:configuration" {
+        case threadpool-dynamic {
+            when "/config:modules/config:module/config:type = 'threadpool-dynamic'";
+            leaf core-size {
+                type uint32;
+            }
+
+            leaf keep-alive {
+                type uint32;
+                units seconds;
+                default 10;
+            }
+
+            leaf maximum-size {
+                type uint32;
+                description "maximum-size description";
+            }
+
+            leaf binary {
+                type binary;
+            }
+
+            container threadfactory {
+                description "threadfactory description";
+                uses config:service-ref {
+                    refine type {
+                        mandatory true;
+                        config:required-identity th2:threadfactory;
+                    }
+                }
+            }
+
+            leaf-list users  {
+                type string;
+            }
+
+             leaf-list users-numbers {
+                type uint32;
+                description "numbers of users description";
+            }
+        }
+    }
+
+    augment "/config:modules/config:module/config:state" {
+        case threadpool-dynamic {
+            when "/config:modules/config:module/config:type = 'threadpool-dynamic'";
+            // root runtime bean
+            leaf created-sessions {
+                type uint32;
+            }
+        }
+    }
+}
diff --git a/opendaylight/config/yang-jmx-generator/src/test/resources/test-config-threads.yang b/opendaylight/config/yang-jmx-generator/src/test/resources/test-config-threads.yang
new file mode 100644 (file)
index 0000000..ff4c426
--- /dev/null
@@ -0,0 +1,63 @@
+module config-threads {
+    yang-version 1;
+    namespace "urn:opendaylight:params:xml:ns:yang:controller:config:threads";
+    prefix "th";
+
+    import config { prefix config; revision-date 2013-04-05; }
+
+    description
+        "This module contains the base YANG definitions for NS-OS
+         thread-related services.";
+
+    revision "2013-04-09" {
+        description
+            "Added eventbus service.";
+    }
+
+    revision "2013-04-05" {
+        description
+            "Updated with YANG extension for Java class specification.";
+    }
+
+    revision "2013-04-03" {
+        description
+            "Initial revision";
+    }
+
+    identity eventbus {
+        description
+            "Service representing an event bus. The service acts as message
+             router between event producers and event consumers";
+
+        base "config:service-type";
+        config:java-class "com.google.common.eventbus.EventBus";
+    }
+
+    identity threadfactory {
+        description
+            "Service representing a ThreadFactory instance. It is directly
+             useful in Java world, where various library pieces need to create
+             threads and you may want to inject a customized thread
+             implementation.";
+
+        base "config:service-type";
+        config:java-class "java.util.concurrent.ThreadFactory";
+    }
+
+    identity threadpool {
+        description
+            "A simple pool of threads able to execute work.";
+
+        base "config:service-type";
+        config:java-class "org.opendaylight.controller.config.threadpool.ThreadPool";
+    }
+
+    identity scheduled-threadpool {
+        description
+            "An extension of the simple pool of threads able to schedule
+             work to be executed at some point in time.";
+
+        base "threadpool";
+        config:java-class "org.opendaylight.controller.config.threadpool.ScheduledThreadPool";
+    }
+}
diff --git a/opendaylight/config/yang-store-api/pom.xml b/opendaylight/config/yang-store-api/pom.xml
new file mode 100644 (file)
index 0000000..a92baa9
--- /dev/null
@@ -0,0 +1,51 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <artifactId>config-subsystem</artifactId>
+        <groupId>org.opendaylight</groupId>
+        <version>0.2.1-SNAPSHOT</version>
+        <relativePath>..</relativePath>
+    </parent>
+    <artifactId>yang-store-api</artifactId>
+    <name>${project.artifactId}</name>
+    <packaging>bundle</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.opendaylight</groupId>
+            <artifactId>yang-jmx-generator</artifactId>
+            <version>0.2.1-SNAPSHOT</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+
+                        <Private-Package>
+                        </Private-Package>
+
+                        <Import-Package>
+                            org.opendaylight.controller.config.yangjmxgenerator,
+                            org.opendaylight.yangtools.yang.model.api
+                        </Import-Package>
+                        <Export-Package>
+                            org.opendaylight.controller.config.yang.store.api
+                        </Export-Package>
+                    </instructions>
+                </configuration>
+            </plugin>
+            <!-- test jar -->
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jar-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/opendaylight/config/yang-store-api/src/main/java/org/opendaylight/controller/config/yang/store/api/YangStoreException.java b/opendaylight/config/yang-store-api/src/main/java/org/opendaylight/controller/config/yang/store/api/YangStoreException.java
new file mode 100644 (file)
index 0000000..68d62f8
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * 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.yang.store.api;
+
+public class YangStoreException extends Exception {
+
+    private static final long serialVersionUID = 2841238836278528836L;
+
+    public YangStoreException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+}
diff --git a/opendaylight/config/yang-store-api/src/main/java/org/opendaylight/controller/config/yang/store/api/YangStoreService.java b/opendaylight/config/yang-store-api/src/main/java/org/opendaylight/controller/config/yang/store/api/YangStoreService.java
new file mode 100644 (file)
index 0000000..15619a8
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * 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.yang.store.api;
+
+/**
+ * Yang store OSGi service
+ */
+public interface YangStoreService {
+
+    /**
+     * Module entry objects mapped to module names and namespaces.
+     *
+     * @return actual view of what is available in OSGi service registry.
+     */
+    YangStoreSnapshot getYangStoreSnapshot() throws YangStoreException;
+
+}
diff --git a/opendaylight/config/yang-store-api/src/main/java/org/opendaylight/controller/config/yang/store/api/YangStoreSnapshot.java b/opendaylight/config/yang-store-api/src/main/java/org/opendaylight/controller/config/yang/store/api/YangStoreSnapshot.java
new file mode 100644 (file)
index 0000000..40daf40
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * 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.yang.store.api;
+
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry;
+import org.opendaylight.yangtools.yang.model.api.Module;
+
+public interface YangStoreSnapshot extends AutoCloseable {
+
+    Map<String, Map<String, ModuleMXBeanEntry>> getModuleMXBeanEntryMap();
+
+    Map<String, Entry<Module, String>> getModuleMap();
+
+    int countModuleMXBeanEntries();
+
+    @Override
+    void close();
+}
diff --git a/opendaylight/config/yang-store-impl/.gitignore b/opendaylight/config/yang-store-impl/.gitignore
new file mode 100644 (file)
index 0000000..fc1d35e
--- /dev/null
@@ -0,0 +1,3 @@
+target
+.classpath
+.settings
diff --git a/opendaylight/config/yang-store-impl/pom.xml b/opendaylight/config/yang-store-impl/pom.xml
new file mode 100644 (file)
index 0000000..ead52e0
--- /dev/null
@@ -0,0 +1,138 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <artifactId>config-subsystem</artifactId>
+        <groupId>org.opendaylight</groupId>
+        <version>0.2.1-SNAPSHOT</version>
+        <relativePath>..</relativePath>
+    </parent>
+    <artifactId>yang-store-impl</artifactId>
+    <name>${project.artifactId}</name>
+    <packaging>bundle</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>yang-store-api</artifactId>
+            <version>0.2.1-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>yang-jmx-generator</artifactId>
+            <version>0.2.1-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>binding-generator-impl</artifactId>
+            <version>${opendaylight.binding.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>commons-io</groupId>
+            <artifactId>commons-io</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.bgpcep</groupId>
+            <artifactId>mockito-configuration</artifactId>
+            <version>0.2.0-SNAPSHOT</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Bundle-Activator>org.opendaylight.controller.config.yang.store.impl.YangStoreActivator
+                        </Bundle-Activator>
+                        <Private-Package>
+                            org.opendaylight.controller.config.yang.store.impl,
+                            <!-- OPENDAYLIGHT -->
+                            org.opendaylight.yangtools.sal.binding.yang.types,
+                            org.opendaylight.yangtools.yang.parser.impl,
+                            org.opendaylight.yangtools.antlrv4.code.gen,
+                            org.opendaylight.yangtools.yang.model.parser.api,
+
+                            org.opendaylight.yangtools.yang.model.util,
+
+                            org.opendaylight.yangtools.yang.parser.builder.api,
+                            org.opendaylight.yangtools.yang.parser.builder.impl,
+
+                            org.opendaylight.yangtools.yang.parser.impl,
+                            org.opendaylight.yangtools.yang.parser.util,
+
+                            org.opendaylight.yangtools.yang.validator,
+                            <!-- ANTLR -->
+
+                            org.antlr.v4.runtime,
+                            org.antlr.v4.runtime.tree,
+                            org.antlr.v4.runtime.atn,
+                            org.antlr.v4.runtime.dfa,
+                            org.antlr.v4.runtime.misc,
+                            org.antlr.v4.runtime.tree.gui,
+                            org.abego.treelayout,
+                            org.abego.treelayout.util,
+                            org.abego.treelayout.internal.util,
+                            org.abego.treelayout.internal.util.java.lang,
+                            org.abego.treelayout.internal.util.java.lang.string,
+                            org.abego.treelayout.internal.util.java.util,
+
+                            <!-- xtext -->
+                            org.eclipse.xtext.xbase.lib.*,
+                            org.apache.commons.lang.*,
+                        </Private-Package>
+
+                        <Import-Package>
+                            org.opendaylight.controller.config.yang.store.api,
+                            org.opendaylight.controller.config.yangjmxgenerator,
+                            com.google.common.base,
+                            com.google.common.collect,
+                            com.google.common.primitives,
+                            org.apache.commons.io,
+                            org.osgi.framework,
+                            org.osgi.util.tracker,
+                            org.slf4j,
+                            javax.*,
+                            <!-- OPENDAYLIGHT PROVIDED BY YANG-JMX-GENERATOR -->
+                            org.opendaylight.yangtools.yang.common,
+                            org.opendaylight.yangtools.yang.model.api,
+                            org.opendaylight.yangtools.yang.model.api.type,
+
+                            org.opendaylight.yangtools.yang.binding,
+
+                            org.opendaylight.yangtools.binding.generator.util,
+                            org.opendaylight.yangtools.sal.binding.generator.spi,
+                            org.opendaylight.yangtools.sal.binding.model.api,
+
+                            org.opendaylight.yangtools.binding.generator.util.generated.type.builder,
+                            org.opendaylight.yangtools.sal.binding.model.api.type.builder,
+                        </Import-Package>
+                        <Export-Package>
+                        </Export-Package>
+                    </instructions>
+                </configuration>
+            </plugin>
+            <!-- test jar -->
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jar-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/opendaylight/config/yang-store-impl/src/main/java/org/opendaylight/controller/config/yang/store/impl/ExtenderYangTracker.java b/opendaylight/config/yang-store-impl/src/main/java/org/opendaylight/controller/config/yang/store/impl/ExtenderYangTracker.java
new file mode 100644 (file)
index 0000000..e3be734
--- /dev/null
@@ -0,0 +1,171 @@
+/*
+ * 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.yang.store.impl;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.Set;
+
+import org.opendaylight.controller.config.yang.store.api.YangStoreException;
+import org.opendaylight.controller.config.yang.store.api.YangStoreService;
+import org.opendaylight.controller.config.yang.store.api.YangStoreSnapshot;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
+import org.osgi.util.tracker.BundleTracker;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.Sets;
+
+public class ExtenderYangTracker extends BundleTracker<Object> implements
+        YangStoreService {
+
+    private static final Logger logger = LoggerFactory
+            .getLogger(ExtenderYangTracker.class);
+
+    private final Multimap<Bundle, URL> bundlesToYangURLs = HashMultimap
+            .create();
+    private final YangStoreCache cache = new YangStoreCache();
+    private final MbeParser mbeParser;
+
+    public ExtenderYangTracker(BundleContext context) {
+        this(context, new MbeParser());
+
+    }
+
+    @VisibleForTesting
+    ExtenderYangTracker(BundleContext context, MbeParser mbeParser) {
+        super(context, Bundle.ACTIVE, null);
+        this.mbeParser = mbeParser;
+        logger.trace("Registered as extender with context {}", context);
+    }
+
+    @Override
+    public Object addingBundle(Bundle bundle, BundleEvent event) {
+
+        // Ignore system bundle
+        //
+        // system bundle has config-api on classpath &&
+        // config-api contains yang files =>
+        // system bundle contains yang files from that bundle
+        if (bundle.getBundleId() == 0)
+            return bundle;
+
+        Enumeration<URL> yangURLs = bundle.findEntries("META-INF/yang",
+                "*.yang", false);
+
+        if (yangURLs == null)
+            return bundle;
+
+        synchronized (this) {
+            while (yangURLs.hasMoreElements()) {
+                URL yang = yangURLs.nextElement();
+                logger.debug("Bundle {} found yang file {}", bundle, yang);
+                bundlesToYangURLs.put(bundle, yang);
+            }
+        }
+
+        return bundle;
+    }
+
+    @Override
+    public void removedBundle(Bundle bundle, BundleEvent event, Object object) {
+        synchronized (this) {
+            Collection<URL> urls = bundlesToYangURLs.removeAll(bundle);
+            logger.debug(
+                    "Removed following yang URLs {} because of removed bundle {}",
+                    urls, bundle);
+        }
+    }
+
+    @Override
+    public synchronized YangStoreSnapshot getYangStoreSnapshot()
+            throws YangStoreException {
+        Optional<YangStoreSnapshot> yangStoreOpt = cache
+                .getCachedYangStore(bundlesToYangURLs);
+        if (yangStoreOpt.isPresent()) {
+            logger.debug("Returning cached yang store {}", yangStoreOpt.get());
+            return yangStoreOpt.get();
+        }
+
+        try {
+            YangStoreSnapshot yangStoreSnapshot = mbeParser
+                    .parseYangFiles(fromUrlsToInputStreams());
+            logger.debug(
+                    "{} module entries parsed successfully from {} yang files",
+                    yangStoreSnapshot.countModuleMXBeanEntries(),
+                    bundlesToYangURLs.values().size());
+            cache.cacheYangStore(bundlesToYangURLs, yangStoreSnapshot);
+
+            return yangStoreSnapshot;
+        } catch (RuntimeException e) {
+            logger.warn(
+                    "Unable to parse yang files, yang files that were picked up so far: {}",
+                    bundlesToYangURLs, e);
+            throw new YangStoreException("Unable to parse yang files", e);
+        }
+    }
+
+    private Collection<InputStream> fromUrlsToInputStreams() {
+        return Collections2.transform(bundlesToYangURLs.values(),
+                new Function<URL, InputStream>() {
+
+                    @Override
+                    public InputStream apply(URL url) {
+                        try {
+                            return url.openStream();
+                        } catch (IOException e) {
+                            logger.warn("Unable to open stream from {}", url);
+                            throw new IllegalStateException(
+                                    "Unable to open stream from " + url, e);
+                        }
+                    }
+                });
+    }
+
+    private static final class YangStoreCache {
+
+        Set<URL> cachedUrls;
+        YangStoreSnapshot cachedYangStoreSnapshot;
+
+        Optional<YangStoreSnapshot> getCachedYangStore(
+                Multimap<Bundle, URL> bundlesToYangURLs) {
+            Set<URL> urls = setFromMultimapValues(bundlesToYangURLs);
+            if (cachedUrls != null && cachedUrls.equals(urls)) {
+                Preconditions.checkState(cachedYangStoreSnapshot != null);
+                return Optional.of(cachedYangStoreSnapshot);
+            }
+            return Optional.absent();
+        }
+
+        private static Set<URL> setFromMultimapValues(
+                Multimap<Bundle, URL> bundlesToYangURLs) {
+            Set<URL> urls = Sets.newHashSet(bundlesToYangURLs.values());
+            Preconditions.checkState(bundlesToYangURLs.size() == urls.size());
+            return urls;
+        }
+
+        void cacheYangStore(Multimap<Bundle, URL> urls,
+                YangStoreSnapshot yangStoreSnapshot) {
+            this.cachedUrls = setFromMultimapValues(urls);
+            this.cachedYangStoreSnapshot = yangStoreSnapshot;
+        }
+
+    }
+}
diff --git a/opendaylight/config/yang-store-impl/src/main/java/org/opendaylight/controller/config/yang/store/impl/MbeParser.java b/opendaylight/config/yang-store-impl/src/main/java/org/opendaylight/controller/config/yang/store/impl/MbeParser.java
new file mode 100644 (file)
index 0000000..fc895eb
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * 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.yang.store.impl;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.apache.commons.io.IOUtils;
+import org.opendaylight.controller.config.yang.store.api.YangStoreException;
+import org.opendaylight.controller.config.yang.store.api.YangStoreSnapshot;
+import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry;
+import org.opendaylight.controller.config.yangjmxgenerator.PackageTranslator;
+import org.opendaylight.controller.config.yangjmxgenerator.ServiceInterfaceEntry;
+import org.opendaylight.controller.config.yangjmxgenerator.TypeProviderWrapper;
+import org.opendaylight.yangtools.sal.binding.yang.types.TypeProviderImpl;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+
+public class MbeParser {
+
+    public YangStoreSnapshot parseYangFiles(
+            Collection<? extends InputStream> allInput)
+            throws YangStoreException {
+        YangParserImpl parser = new YangParserImpl();
+
+        List<InputStream> bufferedInputStreams = new ArrayList<>();
+        for (InputStream is : allInput) {
+            String content;
+            try {
+                content = IOUtils.toString(is);
+            } catch (IOException e) {
+                throw new YangStoreException("Can not get yang as String from "
+                        + is, e);
+            }
+            InputStream buf = new ByteArrayInputStream(content.getBytes());
+            bufferedInputStreams.add(buf);
+        }
+
+        Map<InputStream, Module> allYangModules = parser
+                .parseYangModelsFromStreamsMapped(bufferedInputStreams);
+
+        SchemaContext resolveSchemaContext = parser.resolveSchemaContext(Sets
+                .newHashSet(allYangModules.values()));
+
+        // JMX generator
+
+        Map<String, String> namespaceToPackageMapping = Maps.newHashMap();
+        PackageTranslator packageTranslator = new PackageTranslator(
+                namespaceToPackageMapping);
+
+        Map<QName, ServiceInterfaceEntry> qNamesToSIEs = new HashMap<>();
+
+        // create SIE structure qNamesToSIEs
+        for (Module module : resolveSchemaContext.getModules()) {
+            String packageName = packageTranslator.getPackageName(module);
+            Map<QName, ServiceInterfaceEntry> namesToSIEntries = ServiceInterfaceEntry
+                    .create(module, packageName);
+
+            for (Entry<QName, ServiceInterfaceEntry> sieEntry : namesToSIEntries
+                    .entrySet()) {
+
+                // merge value into qNamesToSIEs
+                if (qNamesToSIEs.containsKey(sieEntry.getKey()) == false) {
+                    qNamesToSIEs.put(sieEntry.getKey(), sieEntry.getValue());
+                } else {
+                    throw new IllegalStateException(
+                            "Cannot add two SIE with same qname "
+                                    + sieEntry.getValue());
+                }
+            }
+        }
+
+        Map<String, Map<String, ModuleMXBeanEntry>> retVal = Maps.newHashMap();
+        Map<String, Entry<Module, String>> modulesMap = new HashMap<>();
+
+        for (Entry<InputStream, Module> moduleEntry : allYangModules.entrySet()) {
+            String packageName = packageTranslator.getPackageName(moduleEntry
+                    .getValue());
+            TypeProviderWrapper typeProviderWrapper = new TypeProviderWrapper(
+                    new TypeProviderImpl(resolveSchemaContext));
+            String yangAsString;
+            try {
+                moduleEntry.getKey().reset();
+                yangAsString = IOUtils.toString(moduleEntry.getKey());
+            } catch (IOException e) {
+                throw new IllegalStateException(e);
+            }
+            modulesMap.put(moduleEntry.getValue().getName(),
+                    Maps.immutableEntry(moduleEntry.getValue(), yangAsString));
+            Map<String /* MB identity local name */, ModuleMXBeanEntry> namesToMBEs = ModuleMXBeanEntry
+                    .create(moduleEntry.getValue(), qNamesToSIEs, resolveSchemaContext, typeProviderWrapper,
+                            packageName);
+            retVal.put(moduleEntry.getValue().getNamespace().toString(),
+                    namesToMBEs);
+        }
+
+        return new YangStoreSnapshotImpl(retVal, modulesMap);
+    }
+
+    public Map<Module, String> parseYangFilesToString(
+            Collection<? extends InputStream> allYangs) {
+        YangParserImpl parser = new YangParserImpl();
+
+        Map<InputStream, Module> allYangModules = parser
+                .parseYangModelsFromStreamsMapped(Lists.newArrayList(allYangs));
+        Map<Module, String> retVal = new HashMap<>();
+
+        for (Entry<InputStream, Module> entry : allYangModules.entrySet()) {
+            try {
+                retVal.put(entry.getValue(), IOUtils.toString(entry.getKey()));
+            } catch (IOException e) {
+                throw new IllegalStateException(
+                        "Can not create string from yang file.");
+            }
+        }
+        return retVal;
+    }
+
+}
diff --git a/opendaylight/config/yang-store-impl/src/main/java/org/opendaylight/controller/config/yang/store/impl/YangStoreActivator.java b/opendaylight/config/yang-store-impl/src/main/java/org/opendaylight/controller/config/yang/store/impl/YangStoreActivator.java
new file mode 100644 (file)
index 0000000..2331fd1
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * 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.yang.store.impl;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import org.opendaylight.controller.config.yang.store.api.YangStoreService;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class YangStoreActivator implements BundleActivator {
+
+    private ExtenderYangTracker extender;
+    private ServiceRegistration<YangStoreService> registration;
+    private static final Logger logger = LoggerFactory
+            .getLogger(YangStoreActivator.class);
+
+    @Override
+    public void start(BundleContext context) throws Exception {
+        extender = new ExtenderYangTracker(context);
+        extender.open();
+
+        Dictionary<String, ?> properties = new Hashtable<>();
+        registration = context.registerService(YangStoreService.class,
+                extender, properties);
+    }
+
+    @Override
+    public void stop(BundleContext context) throws Exception {
+        try {
+            extender.close();
+        } catch (Exception e) {
+            logger.warn("Exception while closing extender", e);
+        }
+
+        if (registration != null)
+            try {
+                registration.unregister();
+            } catch (Exception e) {
+                logger.warn("Exception while unregistring yang store service",
+                        e);
+            }
+    }
+}
diff --git a/opendaylight/config/yang-store-impl/src/main/java/org/opendaylight/controller/config/yang/store/impl/YangStoreSnapshotImpl.java b/opendaylight/config/yang-store-impl/src/main/java/org/opendaylight/controller/config/yang/store/impl/YangStoreSnapshotImpl.java
new file mode 100644 (file)
index 0000000..d5169ea
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * 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.yang.store.impl;
+
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.opendaylight.controller.config.yang.store.api.YangStoreSnapshot;
+import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry;
+import org.opendaylight.yangtools.yang.model.api.Module;
+
+public class YangStoreSnapshotImpl implements YangStoreSnapshot {
+
+    private final Map<String /* Namespace from yang file */, Map<String /*
+                                                                         * Name
+                                                                         * of
+                                                                         * module
+                                                                         * entry
+                                                                         * from
+                                                                         * yang
+                                                                         * file
+                                                                         */, ModuleMXBeanEntry>> moduleMXBeanEntryMap;
+
+    private final Map<String, Entry<Module, String>> moduleMap;
+
+    public YangStoreSnapshotImpl(
+            Map<String, Map<String, ModuleMXBeanEntry>> moduleMXBeanEntryMap,
+            Map<String, Entry<Module, String>> moduleMap) {
+        this.moduleMXBeanEntryMap = moduleMXBeanEntryMap;
+        this.moduleMap = moduleMap;
+    }
+
+    @Override
+    public Map<String, Map<String, ModuleMXBeanEntry>> getModuleMXBeanEntryMap() {
+        return moduleMXBeanEntryMap;
+    }
+
+    @Override
+    public Map<String, Entry<Module, String>> getModuleMap() {
+        return moduleMap;
+    }
+
+    @Override
+    public int countModuleMXBeanEntries() {
+        int i = 0;
+        for (Map<String, ModuleMXBeanEntry> value : moduleMXBeanEntryMap
+                .values()) {
+            i += value.keySet().size();
+        }
+        return i;
+    }
+
+    @Override
+    public void close() {
+        // TODO: reference counting
+    }
+
+}
diff --git a/opendaylight/config/yang-store-impl/src/test/java/org/opendaylight/controller/config/yang/store/impl/ExtenderYangTrackerTest.java b/opendaylight/config/yang-store-impl/src/test/java/org/opendaylight/controller/config/yang/store/impl/ExtenderYangTrackerTest.java
new file mode 100644 (file)
index 0000000..e40d734
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * 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.yang.store.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Matchers.anyCollectionOf;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.controller.config.yang.store.api.YangStoreException;
+import org.opendaylight.controller.config.yang.store.api.YangStoreSnapshot;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+
+import com.google.common.collect.Lists;
+
+public class ExtenderYangTrackerTest {
+
+    @Mock
+    private BundleContext context;
+    private ExtenderYangTracker tested;
+    @Mock
+    private MbeParser parser;
+    @Mock
+    private YangStoreSnapshot yangStoreSnapshot;
+
+    @Before
+    public void setUp() throws YangStoreException {
+        MockitoAnnotations.initMocks(this);
+        doReturn("context").when(context).toString();
+        tested = new ExtenderYangTracker(context, parser);
+        doReturn(yangStoreSnapshot).when(parser).parseYangFiles(
+                anyCollectionOf(InputStream.class));
+        doReturn(22).when(yangStoreSnapshot).countModuleMXBeanEntries();
+        doReturn("mock yang store").when(yangStoreSnapshot).toString();
+    }
+
+    @Test
+    public void testCache() throws MalformedURLException, YangStoreException,
+            InterruptedException {
+        Bundle bundle = getMockedBundle(5, false);
+        tested.addingBundle(bundle, null);
+        bundle = getMockedBundle(2, false);
+        tested.addingBundle(bundle, null);
+        bundle = getMockedBundle(10, false);
+        tested.addingBundle(bundle, null);
+        YangStoreSnapshot returnedStore;
+
+        returnedStore = tested.getYangStoreSnapshot();
+
+        assertEquals(yangStoreSnapshot, returnedStore);
+
+        tested.removedBundle(bundle, null, null);
+        tested.getYangStoreSnapshot();
+
+        bundle = getMockedBundle(10, false);
+        tested.addingBundle(bundle, null);
+
+        tested.getYangStoreSnapshot();
+
+        verify(parser, times(3)).parseYangFiles(
+                anyCollectionOf(InputStream.class));
+
+        returnedStore = tested.getYangStoreSnapshot();
+
+        verifyNoMoreInteractions(parser);
+        assertEquals(yangStoreSnapshot, returnedStore);
+    }
+
+    int bundleCounter = 1;
+
+    private Bundle getMockedBundle(int sizeOfUrls, boolean system)
+            throws MalformedURLException {
+        Bundle mock = mock(Bundle.class);
+
+        List<URL> urls = Lists.newArrayList();
+        for (int i = 0; i < sizeOfUrls; i++) {
+            urls.add(new URL("http://127.0." + bundleCounter++ + "." + i));
+        }
+        Enumeration<URL> abc = new TestEnumeration(urls);
+
+        doReturn(abc).when(mock).findEntries("META-INF/yang", "*.yang", false);
+        if (system)
+            doReturn(0L).when(mock).getBundleId();
+        else
+            doReturn(1L).when(mock).getBundleId();
+
+        doReturn("mockedBundle").when(mock).toString();
+
+        return mock;
+    }
+
+    private static final class TestEnumeration implements Enumeration<URL> {
+
+        private final List<URL> urls;
+        int currentPos = 0;
+
+        public TestEnumeration(List<URL> urls) {
+            this.urls = urls;
+        }
+
+        @Override
+        public boolean hasMoreElements() {
+            try {
+                urls.get(currentPos);
+            } catch (IndexOutOfBoundsException e) {
+                return false;
+            }
+            return true;
+        }
+
+        @Override
+        public URL nextElement() {
+            URL url = urls.get(currentPos++);
+            return url;
+        }
+
+    }
+}
diff --git a/opendaylight/config/yang-store-impl/src/test/java/org/opendaylight/controller/config/yang/store/impl/HardcodedYangStoreService.java b/opendaylight/config/yang-store-impl/src/test/java/org/opendaylight/controller/config/yang/store/impl/HardcodedYangStoreService.java
new file mode 100644 (file)
index 0000000..96833bd
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * 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.yang.store.impl;
+
+import static org.junit.Assert.assertNotNull;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+
+import org.apache.commons.io.IOUtils;
+import org.opendaylight.controller.config.yang.store.api.YangStoreException;
+import org.opendaylight.controller.config.yang.store.api.YangStoreService;
+import org.opendaylight.controller.config.yang.store.api.YangStoreSnapshot;
+
+public class HardcodedYangStoreService implements YangStoreService {
+
+    private final Collection<ByteArrayInputStream> byteArrayInputStreams;
+
+    public HardcodedYangStoreService(
+            Collection<? extends InputStream> inputStreams)
+            throws YangStoreException, IOException {
+        byteArrayInputStreams = new ArrayList<>();
+        for (InputStream inputStream : inputStreams) {
+            assertNotNull(inputStream);
+            byte[] content = IOUtils.toByteArray(inputStream);
+            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+                    content);
+            byteArrayInputStreams.add(byteArrayInputStream);
+        }
+    }
+
+    @Override
+    public YangStoreSnapshot getYangStoreSnapshot() throws YangStoreException {
+        for (InputStream inputStream : byteArrayInputStreams) {
+            try {
+                inputStream.reset();
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+        }
+        return new MbeParser().parseYangFiles(byteArrayInputStreams);
+    }
+}
diff --git a/opendaylight/config/yang-test/README.txt b/opendaylight/config/yang-test/README.txt
new file mode 100644 (file)
index 0000000..c26df02
--- /dev/null
@@ -0,0 +1,9 @@
+Test code generator, namely generating abstract classes to target/generated-sources/config folder.
+Currently files generated to src are modified - getInstance() method must be implemented, so the body is replaced
+manually with following snippet:
+               return new AutoCloseable() {
+                       @Override
+                       public void close() throws Exception {
+                       }
+               };
+TODO: clean src/main/java directory and replace generated body during build.
diff --git a/opendaylight/config/yang-test/pom.xml b/opendaylight/config/yang-test/pom.xml
new file mode 100644 (file)
index 0000000..9ee414e
--- /dev/null
@@ -0,0 +1,41 @@
+<project
+        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
+        xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.opendaylight</groupId>
+        <artifactId>config-subsystem</artifactId>
+        <version>0.2.1-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>yang-test</artifactId>
+
+    <description>Artifact that contains only generated code from yang files. Suitable for testing.
+    </description>
+
+    <dependencies>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>config-api</artifactId>
+            <version>0.2.1-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+        </dependency>
+    </dependencies>
+
+    <name>${project.artifactId}</name>
+    <prerequisites>
+        <maven>3.0.4</maven>
+    </prerequisites>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yang-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/opendaylight/config/yang-test/src/main/java/org/opendaylight/controller/config/yang/test/impl/DepTestImplModule.java b/opendaylight/config/yang-test/src/main/java/org/opendaylight/controller/config/yang/test/impl/DepTestImplModule.java
new file mode 100644 (file)
index 0000000..4b37aa1
--- /dev/null
@@ -0,0 +1,46 @@
+/**
+ * Generated file
+
+ * Generated from: yang module name: config-test-impl  yang module local name: impl-dep
+ * Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+ * Generated at: Fri Sep 27 13:02:28 CEST 2013
+ *
+ * Do not modify this file unless it is present under src/main directory
+ */
+package org.opendaylight.controller.config.yang.test.impl;
+
+/**
+*
+*/
+public final class DepTestImplModule
+        extends
+        org.opendaylight.controller.config.yang.test.impl.AbstractDepTestImplModule {
+
+    public DepTestImplModule(
+            org.opendaylight.controller.config.api.ModuleIdentifier name,
+            org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+        super(name, dependencyResolver);
+    }
+
+    public DepTestImplModule(
+            org.opendaylight.controller.config.api.ModuleIdentifier name,
+            org.opendaylight.controller.config.api.DependencyResolver dependencyResolver,
+            DepTestImplModule oldModule, java.lang.AutoCloseable oldInstance) {
+        super(name, dependencyResolver, oldModule, oldInstance);
+    }
+
+    @Override
+    public void validate() {
+        super.validate();
+        // Add custom validation for module attributes here.
+    }
+
+    @Override
+    public java.lang.AutoCloseable createInstance() {
+        return new AutoCloseable() {
+            @Override
+            public void close() throws Exception {
+            }
+        };
+    }
+}
diff --git a/opendaylight/config/yang-test/src/main/java/org/opendaylight/controller/config/yang/test/impl/DepTestImplModuleFactory.java b/opendaylight/config/yang-test/src/main/java/org/opendaylight/controller/config/yang/test/impl/DepTestImplModuleFactory.java
new file mode 100644 (file)
index 0000000..b07cf40
--- /dev/null
@@ -0,0 +1,19 @@
+/**
+ * Generated file
+
+ * Generated from: yang module name: config-test-impl  yang module local name: impl-dep
+ * Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+ * Generated at: Fri Sep 27 13:02:28 CEST 2013
+ *
+ * Do not modify this file unless it is present under src/main directory
+ */
+package org.opendaylight.controller.config.yang.test.impl;
+
+/**
+*
+*/
+public class DepTestImplModuleFactory
+        extends
+        org.opendaylight.controller.config.yang.test.impl.AbstractDepTestImplModuleFactory {
+
+}
diff --git a/opendaylight/config/yang-test/src/main/java/org/opendaylight/controller/config/yang/test/impl/NetconfTestImplModule.java b/opendaylight/config/yang-test/src/main/java/org/opendaylight/controller/config/yang/test/impl/NetconfTestImplModule.java
new file mode 100644 (file)
index 0000000..1cfbd0e
--- /dev/null
@@ -0,0 +1,46 @@
+/**
+ * Generated file
+
+ * Generated from: yang module name: config-test-impl  yang module local name: impl-netconf
+ * Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+ * Generated at: Fri Sep 27 13:02:28 CEST 2013
+ *
+ * Do not modify this file unless it is present under src/main directory
+ */
+package org.opendaylight.controller.config.yang.test.impl;
+
+/**
+*
+*/
+public final class NetconfTestImplModule
+        extends
+        org.opendaylight.controller.config.yang.test.impl.AbstractNetconfTestImplModule {
+
+    public NetconfTestImplModule(
+            org.opendaylight.controller.config.api.ModuleIdentifier name,
+            org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+        super(name, dependencyResolver);
+    }
+
+    public NetconfTestImplModule(
+            org.opendaylight.controller.config.api.ModuleIdentifier name,
+            org.opendaylight.controller.config.api.DependencyResolver dependencyResolver,
+            NetconfTestImplModule oldModule, java.lang.AutoCloseable oldInstance) {
+        super(name, dependencyResolver, oldModule, oldInstance);
+    }
+
+    @Override
+    public void validate() {
+        super.validate();
+        // Add custom validation for module attributes here.
+    }
+
+    @Override
+    public java.lang.AutoCloseable createInstance() {
+        return new AutoCloseable() {
+            @Override
+            public void close() throws Exception {
+            }
+        };
+    }
+}
diff --git a/opendaylight/config/yang-test/src/main/java/org/opendaylight/controller/config/yang/test/impl/NetconfTestImplModuleFactory.java b/opendaylight/config/yang-test/src/main/java/org/opendaylight/controller/config/yang/test/impl/NetconfTestImplModuleFactory.java
new file mode 100644 (file)
index 0000000..e99c64d
--- /dev/null
@@ -0,0 +1,19 @@
+/**
+ * Generated file
+
+ * Generated from: yang module name: config-test-impl  yang module local name: impl-netconf
+ * Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+ * Generated at: Fri Sep 27 13:02:28 CEST 2013
+ *
+ * Do not modify this file unless it is present under src/main directory
+ */
+package org.opendaylight.controller.config.yang.test.impl;
+
+/**
+*
+*/
+public class NetconfTestImplModuleFactory
+        extends
+        org.opendaylight.controller.config.yang.test.impl.AbstractNetconfTestImplModuleFactory {
+
+}
diff --git a/opendaylight/config/yang-test/src/main/java/org/opendaylight/controller/config/yang/test/impl/TestImplModule.java b/opendaylight/config/yang-test/src/main/java/org/opendaylight/controller/config/yang/test/impl/TestImplModule.java
new file mode 100644 (file)
index 0000000..bc5f1bb
--- /dev/null
@@ -0,0 +1,46 @@
+/**
+ * Generated file
+
+ * Generated from: yang module name: config-test-impl  yang module local name: impl
+ * Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+ * Generated at: Fri Sep 27 13:02:28 CEST 2013
+ *
+ * Do not modify this file unless it is present under src/main directory
+ */
+package org.opendaylight.controller.config.yang.test.impl;
+
+/**
+*
+*/
+public final class TestImplModule
+        extends
+        org.opendaylight.controller.config.yang.test.impl.AbstractTestImplModule {
+
+    public TestImplModule(
+            org.opendaylight.controller.config.api.ModuleIdentifier name,
+            org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+        super(name, dependencyResolver);
+    }
+
+    public TestImplModule(
+            org.opendaylight.controller.config.api.ModuleIdentifier name,
+            org.opendaylight.controller.config.api.DependencyResolver dependencyResolver,
+            TestImplModule oldModule, java.lang.AutoCloseable oldInstance) {
+        super(name, dependencyResolver, oldModule, oldInstance);
+    }
+
+    @Override
+    public void validate() {
+        super.validate();
+        // Add custom validation for module attributes here.
+    }
+
+    @Override
+    public java.lang.AutoCloseable createInstance() {
+        return new AutoCloseable() {
+            @Override
+            public void close() throws Exception {
+            }
+        };
+    }
+}
diff --git a/opendaylight/config/yang-test/src/main/java/org/opendaylight/controller/config/yang/test/impl/TestImplModuleFactory.java b/opendaylight/config/yang-test/src/main/java/org/opendaylight/controller/config/yang/test/impl/TestImplModuleFactory.java
new file mode 100644 (file)
index 0000000..1e86c83
--- /dev/null
@@ -0,0 +1,19 @@
+/**
+ * Generated file
+
+ * Generated from: yang module name: config-test-impl  yang module local name: impl
+ * Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+ * Generated at: Fri Sep 27 13:02:28 CEST 2013
+ *
+ * Do not modify this file unless it is present under src/main directory
+ */
+package org.opendaylight.controller.config.yang.test.impl;
+
+/**
+*
+*/
+public class TestImplModuleFactory
+        extends
+        org.opendaylight.controller.config.yang.test.impl.AbstractTestImplModuleFactory {
+
+}
diff --git a/opendaylight/config/yang-test/src/main/yang/config-test-impl.yang b/opendaylight/config/yang-test/src/main/yang/config-test-impl.yang
new file mode 100644 (file)
index 0000000..6245e61
--- /dev/null
@@ -0,0 +1,381 @@
+module config-test-impl {
+    yang-version 1;
+    namespace "urn:opendaylight:params:xml:ns:yang:controller:test:impl";
+    prefix "it-impl";
+
+    import config-test { prefix test; revision-date 2013-06-13;}
+    import config { prefix config; revision-date 2013-04-05; }
+    import ietf-inet-types { prefix inet; revision-date 2010-09-24;}
+    import rpc-context { prefix rpcx; revision-date 2013-06-17; }
+
+
+    description
+        "Testing IMPL";
+
+    revision "2013-04-03" {
+        description
+            "Initial revision";
+    }
+
+    identity impl {
+        base config:module-type;
+        config:provided-service test:testing;
+        config:java-name-prefix TestImpl;
+    }
+
+    identity impl-dep {
+        base config:module-type;
+        config:provided-service test:testing;
+        config:java-name-prefix DepTestImpl;
+    }
+
+    identity impl-netconf {
+        base config:module-type;
+        config:provided-service test:testing;
+        config:java-name-prefix NetconfTestImpl;
+    }
+
+
+    augment "/config:modules/config:module/config:configuration" {
+        case impl {
+            when "/config:modules/config:module/config:type = 'impl'";
+
+           leaf-list allow-user  {
+                type string;
+                description "A list of user name patterns to allow";
+           }
+
+           container dto-a {
+                leaf simple-arg {
+                    type uint32;
+                }
+
+                leaf port {
+                    type inet:port-number;
+                }
+
+            }
+
+            leaf as-number {
+                mandatory true;
+                type inet:as-number;
+            }
+
+
+            leaf simpleInt {
+                type uint32;
+                default 99L;
+            }
+
+            container dto_b {
+                leaf simple-int1 {
+                    type uint32;
+                }
+
+                leaf simple-int2 {
+                    type uint32;
+                }
+            }
+
+        }
+    }
+
+    augment "/config:modules/config:module/config:state" {
+        case impl {
+            when "/config:modules/config:module/config:type = 'impl'";
+            // root runtime bean
+            leaf created-sessions {
+                type uint32;
+            }
+        }
+    }
+
+    augment "/config:modules/config:module/config:configuration" {
+        case impl-dep {
+            when "/config:modules/config:module/config:type = 'impl-dep'";
+        }
+    }
+
+    augment "/config:modules/config:module/config:configuration" {
+        case impl-netconf {
+            when "/config:modules/config:module/config:type = 'impl-netconf'";
+            leaf binaryLeaf {
+                type binary;
+            }
+
+            leaf type {
+                type string;
+            }
+
+           container dto-c {
+                leaf simple-arg {
+                    type uint32;
+                }
+
+                container dto-a-inner {
+                    leaf simple-arg {
+                        type uint32;
+                    }
+
+                    container dto-a-inner-inner {
+                        leaf simple-arg {
+                            type uint32;
+                        }
+                    }
+                }
+            }
+
+            leaf simpleInt {
+                type uint32;
+            }
+
+            leaf simpleBoolean {
+                type boolean;
+                default false;
+            }
+
+            leaf simple-long {
+                type int64  ;
+            }
+
+            leaf simple-long-2 {
+                type uint32;
+            }
+
+            leaf simple-BigInteger {
+                type uint64;
+            }
+
+            leaf simple-byte {
+                type int8;
+            }
+
+            leaf simple-short {
+                type uint8;
+            }
+
+            leaf simple-test {
+                type uint16;
+                default 99;
+            }
+
+            leaf-list simple-list {
+                type uint16;
+            }
+
+            container dto_d {
+                leaf simple-int1 {
+                    type uint32;
+                }
+
+                leaf simple-int2 {
+                    type uint32;
+                }
+
+                leaf simple-int3 {
+                    type uint16;
+                }
+
+                leaf-list simple-list {
+                    type uint16;
+                }
+
+                list complex-dto-bInner {
+                    leaf-list simple-list {
+                        type uint16;
+                    }
+                    leaf simple-int3 {
+                        type uint16;
+                    }
+
+                    container deep {
+                        leaf simple-int3 {
+                            type uint16;
+                        }
+                    }
+                }
+            }
+
+            list complex-list {
+                list simple-list {
+                    leaf simple-int3 {
+                        type uint16;
+                    }
+                }
+            }
+
+            list peers {
+                    leaf port {
+                        type string;
+                    }
+                    leaf core-size {
+                        type uint32;
+                    }
+                    leaf simple-int3 {
+                        type uint16;
+                    }
+            }
+
+            container testing-dep {
+                uses config:service-ref {
+                    refine type {
+                        mandatory true;
+                        config:required-identity test:testing;
+                    }
+                }
+            }
+        }
+    }
+
+    augment "/config:modules/config:module/config:state" {
+        case impl-netconf {
+            when "/config:modules/config:module/config:type = 'impl-netconf'";
+            // rpc
+            rpcx:rpc-context-instance "test-rpc";
+
+            // root runtime bean
+            leaf created-sessions {
+                type uint32;
+            }
+
+            container asdf {
+                leaf simpleInt {
+                    type uint16;
+                }
+
+                leaf simpleString {
+                    type string;
+                }
+            }
+
+
+            list inner-running-data-additional {
+                config:inner-state-bean;
+
+                // rpc
+                rpcx:rpc-context-instance "inner-test-rpc";
+
+                key "simpleString";
+
+                leaf simple-int3 {
+                        type uint16;
+                }
+
+                leaf simpleString {
+                    type string;
+                }
+
+                container deep4 {
+                    leaf boool {
+                        type boolean;
+                    }
+                }
+            }
+
+             list inner-running-data {
+                config:inner-state-bean;
+
+                key "simple-int3";
+
+                leaf simple-int3 {
+                        type uint16;
+                    }
+
+                    container deep2 {
+                    leaf boool {
+                        type boolean;
+                    }
+                 }
+
+                list inner-inner-running-data {
+                    config:inner-state-bean;
+
+                    rpcx:rpc-context-instance "inner-inner-test-rpc";
+
+                    key "simple-int3";
+
+                    leaf simple-int3 {
+                            type uint16;
+                    }
+
+                    list not-state-bean {
+                        leaf element {
+                            type string;
+                        }
+
+                        list not-state-bean-internal {
+                            // This should be ignored
+                            config:inner-state-bean;
+
+                            leaf element2 {
+                                type string;
+                            }
+                        }
+                    }
+
+                    container deep3 {
+                        leaf boool {
+                            type boolean;
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    identity test-rpc;
+    identity inner-test-rpc;
+    identity inner-inner-test-rpc;
+
+    rpc no-arg {
+        input {
+            uses rpcx:rpc-context-ref {
+                refine context-instance {
+                    rpcx:rpc-context-instance test-rpc;
+                }
+            }
+            leaf arg1 {
+                type string;
+            }
+        }
+
+        output {
+            leaf result {
+                type string;
+            }
+        }
+    }
+
+    rpc noArgInner {
+        input {
+            uses rpcx:rpc-context-ref {
+                refine context-instance {
+                    rpcx:rpc-context-instance inner-test-rpc;
+                }
+            }
+        }
+    }
+
+    rpc noArgInnerInner {
+        input {
+            uses rpcx:rpc-context-ref {
+                refine context-instance {
+                    rpcx:rpc-context-instance inner-inner-test-rpc;
+                }
+            }
+
+            leaf arg1 {
+                type uint16;
+            }
+
+            leaf arg2 {
+                type boolean;
+            }
+        }
+         output {
+            leaf result {
+                type boolean;
+            }
+        }
+    }
+}
diff --git a/opendaylight/config/yang-test/src/main/yang/config-test.yang b/opendaylight/config/yang-test/src/main/yang/config-test.yang
new file mode 100644 (file)
index 0000000..2daf405
--- /dev/null
@@ -0,0 +1,23 @@
+module config-test {
+    yang-version 1;
+    namespace "urn:opendaylight:params:xml:ns:yang:controller:test";
+    prefix "test";
+
+    import config { prefix config; revision-date 2013-04-05; }
+
+    description
+        "Testing API";
+
+    revision "2013-06-13" {
+        description
+            "Initial revision";
+    }
+
+    identity testing {
+        description
+            "Test api";
+
+        base "config:service-type";
+        config:java-class "java.lang.AutoCloseable";
+    }
+}
index ccdb26845bf2b2065cdad3dad19c2ce76ecbc6c5..eac5a705f95c625e0b81efc1e8ba8ee958e85970 100644 (file)
@@ -28,6 +28,7 @@
       <modules>
         <!-- MD-SAL bundles -->
         <module>../../md-sal</module>
+        <module>../../config</module>
       </modules>
     </profile>
   </profiles>