From 2b78a2bcd978808b98c85a2c89160bca4674cffa Mon Sep 17 00:00:00 2001 From: Tomas Olvecky Date: Fri, 16 May 2014 13:24:49 +0200 Subject: [PATCH] Bug 1022 - Add ability to lookup dependent Module's attribute. Add 2 methods to DependencyResolver to allow modules look up values of dependent bean attributes. Change-Id: I2ebee82cc1260a84a16f5e69b32c9234bf576e87 Signed-off-by: Tomas Olvecky --- .../config/api/DependencyResolver.java | 57 ++++++++++++----- .../controller/config/api/JmxAttribute.java | 3 + .../impl/ConfigTransactionControllerImpl.java | 5 +- .../DependencyResolverImpl.java | 62 ++++++++++++++----- .../DependencyResolverManager.java | 12 +++- .../DependencyResolverManagerTest.java | 6 +- .../TestingParallelAPSPModule.java | 27 +++++--- .../test/DependentWiringTest.java | 31 +++++++--- 8 files changed, 148 insertions(+), 55 deletions(-) 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 index b020000d3d..40ff7e1703 100644 --- 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 @@ -7,12 +7,15 @@ */ package org.opendaylight.controller.config.api; +import javax.management.AttributeNotFoundException; +import javax.management.InstanceNotFoundException; +import javax.management.MBeanException; +import javax.management.ObjectName; +import javax.management.ReflectionException; import org.opendaylight.controller.config.api.annotations.AbstractServiceInterface; import org.opendaylight.yangtools.concepts.Identifiable; import org.opendaylight.yangtools.yang.binding.BaseIdentity; -import javax.management.ObjectName; - /** * Each new {@link org.opendaylight.controller.config.spi.Module} can receive * resolver from {@link org.opendaylight.controller.config.spi.ModuleFactory} @@ -26,15 +29,13 @@ public interface DependencyResolver extends Identifiable { * 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. + * @param expectedServiceInterface MBean/MXBean interface which will back the proxy object. + * @param objectName ObjectName of dependent module without transaction name + * (platformON). + * @param jmxAttribute for reporting + * @throws IllegalArgumentException when module is not found + * @throws IllegalStateException if module does not export this + * service interface. */ void validateDependency( Class expectedServiceInterface, @@ -44,13 +45,11 @@ public interface DependencyResolver extends Identifiable { * 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 + * {@link org.opendaylight.controller.config.spi.Module#getInstance()} + * @throws IllegalArgumentException when module is not found */ T resolveInstance(Class expectedType, ObjectName objectName, - JmxAttribute jmxAttribute); - - // TODO finish javadoc + JmxAttribute jmxAttribute); /** * To be used during commit phase to resolve identity-ref config attributes. @@ -59,6 +58,32 @@ public interface DependencyResolver extends Identifiable { */ Class resolveIdentity(IdentityAttributeRef identityRef, Class expectedBaseClass); + + /** + * Validate identity-ref config attribute. + */ void validateIdentity(IdentityAttributeRef identityRef, Class expectedBaseClass, JmxAttribute jmxAttribute); + /** + * Can be used during validation or commit phase to get attribute value of dependent module. + * + * @param name either direct ObjectName of a Module (type=Module) or service reference (type=ServiceReference) of dependent Module + * @param attribute String identifying attribute name in JMX. Note that attributes start with upper case. See {@link org.opendaylight.controller.config.api.JmxAttribute#getAttributeName()} + */ + Object getAttribute(ObjectName name, String attribute) + throws MBeanException, AttributeNotFoundException, + InstanceNotFoundException, ReflectionException; + + + /** + * Helper method around {@link javax.management.JMX#newMXBeanProxy(javax.management.MBeanServerConnection, javax.management.ObjectName, Class)} }. + * Returns MXBean proxy for dependent module. Can be used during validation or commit phase to inspect dependent module's attributes. + * + * @param objectName either direct ObjectName of a Module (type=Module) or service reference (type=ServiceReference) of dependent Module + * @param interfaceClass MXBean interface to be used as a proxy + * @param type of proxy for type safe return value + * @return instance of MXBean proxy + */ + T newMXBeanProxy(ObjectName objectName, Class interfaceClass); + } 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 index 244f22f58e..649b1eb467 100644 --- 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 @@ -21,6 +21,9 @@ public class JmxAttribute { this.attributeName = attributeName; } + /** + * Name of attribute in JMX. + */ public String getAttributeName() { return attributeName; } 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 index 3e23120182..39eef8741b 100644 --- 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 @@ -7,6 +7,7 @@ */ package org.opendaylight.controller.config.manager.impl; +import static com.google.common.base.Preconditions.checkNotNull; import static java.lang.String.format; import java.util.ArrayList; @@ -43,8 +44,6 @@ import org.opendaylight.yangtools.yang.data.impl.codec.CodecRegistry; import org.osgi.framework.BundleContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; - -import static com.google.common.base.Preconditions.checkNotNull; /** * This is a JMX bean representing current transaction. It contains * transaction identifier, unique version and parent version for @@ -96,7 +95,7 @@ class ConfigTransactionControllerImpl implements this.factoriesHolder = new HierarchicalConfigMBeanFactoriesHolder(currentlyRegisteredFactories); this.transactionStatus = new TransactionStatus(); this.dependencyResolverManager = new DependencyResolverManager(txLookupRegistry.getTransactionIdentifier(), - transactionStatus, writableSRRegistry, codecRegistry); + transactionStatus, writableSRRegistry, codecRegistry, transactionsMBeanServer); this.transactionsMBeanServer = transactionsMBeanServer; this.configMBeanServer = configMBeanServer; this.blankTransaction = blankTransaction; 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 index c229450c30..4f60a673f5 100644 --- 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 @@ -7,6 +7,19 @@ */ 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.AttributeNotFoundException; +import javax.management.InstanceNotFoundException; +import javax.management.JMX; +import javax.management.MBeanException; +import javax.management.MBeanServer; +import javax.management.ObjectName; +import javax.management.ReflectionException; import org.opendaylight.controller.config.api.DependencyResolver; import org.opendaylight.controller.config.api.IdentityAttributeRef; import org.opendaylight.controller.config.api.JmxAttribute; @@ -25,14 +38,6 @@ import org.opendaylight.yangtools.yang.data.impl.codec.IdentityCodec; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.annotation.concurrent.GuardedBy; -import javax.management.ObjectName; -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.Set; - -import static java.lang.String.format; - /** * Protect {@link org.opendaylight.controller.config.spi.Module#getInstance()} * by creating proxy that would throw exception if those methods are called @@ -49,15 +54,20 @@ final class DependencyResolverImpl implements DependencyResolver, private final Set dependencies = new HashSet<>(); private final ServiceReferenceReadableRegistry readableRegistry; private final CodecRegistry codecRegistry; + private final String transactionName; + private final MBeanServer mBeanServer; DependencyResolverImpl(ModuleIdentifier currentModule, TransactionStatus transactionStatus, ModulesHolder modulesHolder, - ServiceReferenceReadableRegistry readableRegistry, CodecRegistry codecRegistry) { + ServiceReferenceReadableRegistry readableRegistry, CodecRegistry codecRegistry, + String transactionName, MBeanServer mBeanServer) { this.codecRegistry = codecRegistry; this.name = currentModule; this.transactionStatus = transactionStatus; this.modulesHolder = modulesHolder; this.readableRegistry = readableRegistry; + this.transactionName = transactionName; + this.mBeanServer = mBeanServer; } /** @@ -80,7 +90,8 @@ final class DependencyResolverImpl implements DependencyResolver, JmxAttributeValidationException.checkNotNull(dependentReadOnlyON, "is null, expected dependency implementing " - + expectedServiceInterface, jmxAttribute); + + expectedServiceInterface, jmxAttribute + ); // check that objectName belongs to this transaction - this should be @@ -91,8 +102,10 @@ final class DependencyResolverImpl implements DependencyResolver, JmxAttributeValidationException.checkCondition( hasTransaction == false, format("ObjectName should not contain " - + "transaction name. %s set to %s. ", jmxAttribute, - dependentReadOnlyON), jmxAttribute); + + "transaction name. %s set to %s. ", jmxAttribute, + dependentReadOnlyON + ), jmxAttribute + ); dependentReadOnlyON = translateServiceRefIfPossible(dependentReadOnlyON); @@ -110,7 +123,8 @@ final class DependencyResolverImpl implements DependencyResolver, + "attribute %s", foundFactory.getImplementationName(), foundFactory, expectedServiceInterface, dependentReadOnlyON, - jmxAttribute); + jmxAttribute + ); throw new JmxAttributeValidationException(message, jmxAttribute); } synchronized (this) { @@ -157,7 +171,8 @@ final class DependencyResolverImpl implements DependencyResolver, String message = format( "Error while %s resolving instance %s. getInstance() returned null. " + "Expected type %s , attribute %s", name, - dependentModuleIdentifier, expectedType, jmxAttribute); + dependentModuleIdentifier, expectedType, jmxAttribute + ); throw new JmxAttributeValidationException(message, jmxAttribute); } try { @@ -166,7 +181,8 @@ final class DependencyResolverImpl implements DependencyResolver, String message = format( "Instance cannot be cast to expected type. Instance class is %s , " + "expected type %s , attribute %s", - instance.getClass(), expectedType, jmxAttribute); + instance.getClass(), expectedType, jmxAttribute + ); throw new JmxAttributeValidationException(message, e, jmxAttribute); } } @@ -258,4 +274,20 @@ final class DependencyResolverImpl implements DependencyResolver, return name; } + @Override + public Object getAttribute(ObjectName name, String attribute) + throws MBeanException, AttributeNotFoundException, InstanceNotFoundException, ReflectionException { + name = translateServiceRefIfPossible(name); + // add transaction name + name = ObjectNameUtil.withTransactionName(name, transactionName); + return mBeanServer.getAttribute(name, attribute); + } + + @Override + public T newMXBeanProxy(ObjectName name, Class interfaceClass) { + name = translateServiceRefIfPossible(name); + // add transaction name + name = ObjectNameUtil.withTransactionName(name, transactionName); + return JMX.newMXBeanProxy(mBeanServer, name, interfaceClass); + } } 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 index 0c1531728f..2a1a908e7a 100644 --- 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 @@ -20,6 +20,7 @@ import java.util.List; import java.util.Map; import javax.annotation.concurrent.GuardedBy; import javax.management.InstanceAlreadyExistsException; +import javax.management.MBeanServer; import org.opendaylight.controller.config.api.DependencyResolver; import org.opendaylight.controller.config.api.DependencyResolverFactory; import org.opendaylight.controller.config.api.JmxAttribute; @@ -45,19 +46,25 @@ import org.osgi.framework.BundleContext; public class DependencyResolverManager implements DependencyResolverFactory, AutoCloseable { @GuardedBy("this") private final Map moduleIdentifiersToDependencyResolverMap = new HashMap<>(); + private final TransactionIdentifier transactionIdentifier; private final ModulesHolder modulesHolder; private final TransactionStatus transactionStatus; private final ServiceReferenceReadableRegistry readableRegistry; private final CodecRegistry codecRegistry; private final DeadlockMonitor deadlockMonitor; + private final MBeanServer mBeanServer; public DependencyResolverManager(TransactionIdentifier transactionIdentifier, - TransactionStatus transactionStatus, ServiceReferenceReadableRegistry readableRegistry, CodecRegistry codecRegistry) { + TransactionStatus transactionStatus, + ServiceReferenceReadableRegistry readableRegistry, CodecRegistry codecRegistry, + MBeanServer mBeanServer) { + this.transactionIdentifier = transactionIdentifier; this.modulesHolder = new ModulesHolder(transactionIdentifier); this.transactionStatus = transactionStatus; this.readableRegistry = readableRegistry; this.codecRegistry = codecRegistry; this.deadlockMonitor = new DeadlockMonitor(transactionIdentifier); + this.mBeanServer = mBeanServer; } @Override @@ -69,7 +76,8 @@ public class DependencyResolverManager implements DependencyResolverFactory, Aut DependencyResolverImpl dependencyResolver = moduleIdentifiersToDependencyResolverMap.get(name); if (dependencyResolver == null) { transactionStatus.checkNotCommitted(); - dependencyResolver = new DependencyResolverImpl(name, transactionStatus, modulesHolder, readableRegistry, codecRegistry); + dependencyResolver = new DependencyResolverImpl(name, transactionStatus, modulesHolder, readableRegistry, + codecRegistry, transactionIdentifier.getName(), mBeanServer); moduleIdentifiersToDependencyResolverMap.put(name, dependencyResolver); } return dependencyResolver; diff --git a/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/impl/dependencyresolver/DependencyResolverManagerTest.java b/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/impl/dependencyresolver/DependencyResolverManagerTest.java index d5d3823ef0..48d7de0e82 100644 --- a/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/impl/dependencyresolver/DependencyResolverManagerTest.java +++ b/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/impl/dependencyresolver/DependencyResolverManagerTest.java @@ -21,6 +21,7 @@ import org.opendaylight.controller.config.api.JmxAttribute; import org.opendaylight.controller.config.api.ModuleIdentifier; import org.opendaylight.controller.config.api.ServiceReferenceReadableRegistry; import org.opendaylight.controller.config.api.jmx.ObjectNameUtil; +import org.opendaylight.controller.config.manager.impl.AbstractLockedPlatformMBeanServerTest; import org.opendaylight.controller.config.manager.impl.ModuleInternalInfo; import org.opendaylight.controller.config.manager.impl.TransactionIdentifier; import org.opendaylight.controller.config.manager.impl.TransactionStatus; @@ -29,7 +30,7 @@ import org.opendaylight.controller.config.spi.Module; import org.opendaylight.controller.config.spi.ModuleFactory; import org.osgi.framework.BundleContext; -public class DependencyResolverManagerTest { +public class DependencyResolverManagerTest extends AbstractLockedPlatformMBeanServerTest { final ModuleIdentifier apspName = new ModuleIdentifier("apsp", "apsp"); // depends // on: @@ -45,7 +46,8 @@ public class DependencyResolverManagerTest { public void setUp() { transactionStatus = mock(TransactionStatus.class); ServiceReferenceReadableRegistry mockedRegistry = mock(ServiceReferenceReadableRegistry.class); - tested = new DependencyResolverManager(new TransactionIdentifier("txName"), transactionStatus, mockedRegistry, null); + tested = new DependencyResolverManager(new TransactionIdentifier("txName"), transactionStatus, mockedRegistry, + null, platformMBeanServer); doNothing().when(transactionStatus).checkCommitStarted(); doNothing().when(transactionStatus).checkNotCommitted(); } diff --git a/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/parallelapsp/TestingParallelAPSPModule.java b/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/parallelapsp/TestingParallelAPSPModule.java index df6dce1243..5c320ae2c1 100644 --- a/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/parallelapsp/TestingParallelAPSPModule.java +++ b/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/parallelapsp/TestingParallelAPSPModule.java @@ -7,25 +7,25 @@ */ package org.opendaylight.controller.config.manager.testingservices.parallelapsp; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; + import com.google.common.base.Strings; +import java.io.Closeable; +import javax.annotation.Nullable; +import javax.annotation.concurrent.NotThreadSafe; +import javax.management.ObjectName; import org.opendaylight.controller.config.api.DependencyResolver; import org.opendaylight.controller.config.api.JmxAttribute; import org.opendaylight.controller.config.api.ModuleIdentifier; import org.opendaylight.controller.config.api.annotations.RequireInterface; import org.opendaylight.controller.config.manager.testingservices.seviceinterface.TestingThreadPoolServiceInterface; +import org.opendaylight.controller.config.manager.testingservices.threadpool.TestingThreadPoolConfigMXBean; import org.opendaylight.controller.config.manager.testingservices.threadpool.TestingThreadPoolIfc; import org.opendaylight.controller.config.spi.Module; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.annotation.Nullable; -import javax.annotation.concurrent.NotThreadSafe; -import javax.management.ObjectName; -import java.io.Closeable; - -import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.base.Preconditions.checkState; - /** * Represents service that has dependency to thread pool. */ @@ -102,6 +102,17 @@ public class TestingParallelAPSPModule implements Module, checkState("Commit was not triggered".equals(e.getMessage()), e.getMessage()); } + + // test retrieving dependent module's attribute + int threadCount; + try { + threadCount = (Integer)dependencyResolver.getAttribute(threadPoolON, "ThreadCount"); + } catch (Exception e) { + throw new IllegalStateException(e); + } + checkState(threadCount > 0); + TestingThreadPoolConfigMXBean proxy = dependencyResolver.newMXBeanProxy(threadPoolON, TestingThreadPoolConfigMXBean.class); + checkState(threadCount == proxy.getThreadCount()); } @Override diff --git a/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/parallelapsp/test/DependentWiringTest.java b/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/parallelapsp/test/DependentWiringTest.java index f42b9559c4..c9810d0521 100644 --- a/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/parallelapsp/test/DependentWiringTest.java +++ b/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/parallelapsp/test/DependentWiringTest.java @@ -7,6 +7,14 @@ */ package org.opendaylight.controller.config.manager.testingservices.parallelapsp.test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.junit.internal.matchers.StringContains.containsString; + +import java.util.Map; +import javax.management.ObjectName; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -17,20 +25,12 @@ import org.opendaylight.controller.config.manager.impl.factoriesresolver.Hardcod import org.opendaylight.controller.config.manager.testingservices.parallelapsp.TestingParallelAPSPConfigMXBean; import org.opendaylight.controller.config.manager.testingservices.parallelapsp.TestingParallelAPSPImpl; import org.opendaylight.controller.config.manager.testingservices.parallelapsp.TestingParallelAPSPModuleFactory; +import org.opendaylight.controller.config.manager.testingservices.seviceinterface.TestingThreadPoolServiceInterface; import org.opendaylight.controller.config.manager.testingservices.threadpool.TestingFixedThreadPool; import org.opendaylight.controller.config.manager.testingservices.threadpool.TestingFixedThreadPoolConfigMXBean; import org.opendaylight.controller.config.manager.testingservices.threadpool.TestingFixedThreadPoolModuleFactory; import org.opendaylight.controller.config.util.ConfigTransactionJMXClient; -import javax.management.ObjectName; -import java.util.Map; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.junit.internal.matchers.StringContains.containsString; - public class DependentWiringTest extends AbstractParallelAPSPTest { private final String fixed1 = "fixed1"; private final String apsp1 = "apsp-parallel"; @@ -132,4 +132,17 @@ public class DependentWiringTest extends AbstractParallelAPSPTest { parallelAPSPRuntimeProxy.getMaxNumberOfThreads()); } + + @Test + public void testUsingServiceReferences() throws Exception { + ConfigTransactionJMXClient transaction = configRegistryClient.createTransaction(); + ObjectName threadPoolON = createFixed1(transaction, 10); + transaction.lookupConfigBean(getThreadPoolImplementationName(), fixed1); + String refName = "ref"; + ObjectName serviceReferenceON = transaction.saveServiceReference(TestingThreadPoolServiceInterface.QNAME, refName, + threadPoolON); + createParallelAPSP(transaction, serviceReferenceON); + transaction.commit(); + + } } -- 2.36.6