Bug 1022 - Add ability to lookup dependent Module's attribute. 10/7110/1
authorTomas Olvecky <tolvecky@cisco.com>
Fri, 16 May 2014 11:24:49 +0000 (13:24 +0200)
committerTomas Olvecky <tolvecky@cisco.com>
Fri, 16 May 2014 11:24:49 +0000 (13:24 +0200)
Add 2 methods to DependencyResolver to allow modules look up values
of dependent bean attributes.

Change-Id: I2ebee82cc1260a84a16f5e69b32c9234bf576e87
Signed-off-by: Tomas Olvecky <tolvecky@cisco.com>
opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/DependencyResolver.java
opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/JmxAttribute.java
opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/ConfigTransactionControllerImpl.java
opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/dependencyresolver/DependencyResolverImpl.java
opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/dependencyresolver/DependencyResolverManager.java
opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/impl/dependencyresolver/DependencyResolverManagerTest.java
opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/parallelapsp/TestingParallelAPSPModule.java
opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/testingservices/parallelapsp/test/DependentWiringTest.java

index b020000d3d742b93ec71ea602c30e902e759fb27..40ff7e17030bc254026574cd34a7a1ca48970819 100644 (file)
@@ -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<ModuleIdentifier> {
      * 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<? extends AbstractServiceInterface> expectedServiceInterface,
@@ -44,13 +45,11 @@ public interface DependencyResolver extends Identifiable<ModuleIdentifier> {
      * 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> T resolveInstance(Class<T> 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<ModuleIdentifier> {
      */
     <T extends BaseIdentity> Class<? extends T> resolveIdentity(IdentityAttributeRef identityRef, Class<T> expectedBaseClass);
 
+
+    /**
+     * Validate identity-ref config attribute.
+     */
     <T extends BaseIdentity> void validateIdentity(IdentityAttributeRef identityRef, Class<T> 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 <T> type of proxy for type safe return value
+     * @return instance of MXBean proxy
+     */
+    <T> T newMXBeanProxy(ObjectName objectName, Class<T> interfaceClass);
+
 }
index 244f22f58efdd5b1615806a2859fd50902b79172..649b1eb4676e6ab7dfc2ef81f8b13d2b74753e15 100644 (file)
@@ -21,6 +21,9 @@ public class JmxAttribute {
         this.attributeName = attributeName;
     }
 
+    /**
+     * Name of attribute in JMX.
+     */
     public String getAttributeName() {
         return attributeName;
     }
index 3e231201821619d36c95b0cc481938b5eda042f4..39eef8741ba840309b8293e19f14af437652e82e 100644 (file)
@@ -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;
index c229450c30138584e615e552843c1b0f10581dca..4f60a673f5f711ce17815061318f2641bbda0091 100644 (file)
@@ -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<ModuleIdentifier> 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> T newMXBeanProxy(ObjectName name, Class<T> interfaceClass) {
+        name = translateServiceRefIfPossible(name);
+        // add transaction name
+        name = ObjectNameUtil.withTransactionName(name, transactionName);
+        return JMX.newMXBeanProxy(mBeanServer, name, interfaceClass);
+    }
 }
index 0c1531728fb1b58488fe6623047c0fd804528824..2a1a908e7a18875c1baed6997fe9a6e5f6848519 100644 (file)
@@ -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<ModuleIdentifier, DependencyResolverImpl> 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;
index d5d3823ef0e84f5e0d9e0d678d38b2094544a92b..48d7de0e82f200f0dae9a4480d5b317b8e0fe47b 100644 (file)
@@ -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();
     }
index df6dce124339991a50c0016d30dd87cc76168e88..5c320ae2c159b6bdd132ad6fd1e5a15d4db33bd3 100644 (file)
@@ -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
index f42b9559c4bb7e5a562aead7b7a60c0bc692602f..c9810d052149ead1b358d14b42bda64d33c707c9 100644 (file)
@@ -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();
+
+    }
 }