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 f78fd47..d8c4cce 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