*/
package org.opendaylight.controller.config.manager.impl.jmx;
-import java.io.Closeable;
+import com.google.common.base.Preconditions;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public class InternalJMXRegistrator implements Closeable {
- private static final Logger LOG = LoggerFactory
- .getLogger(InternalJMXRegistrator.class);
- private final MBeanServer configMBeanServer;
+abstract class InternalJMXRegistrator implements AutoCloseable {
+ private static final class Root extends InternalJMXRegistrator {
+ private final MBeanServer mbeanServer;
- public InternalJMXRegistrator(MBeanServer configMBeanServer) {
- this.configMBeanServer = configMBeanServer;
+ Root(final MBeanServer mbeanServer) {
+ this.mbeanServer = Preconditions.checkNotNull(mbeanServer);
+ }
+
+ @Override
+ MBeanServer getMBeanServer() {
+ return mbeanServer;
+ }
}
- static class InternalJMXRegistration implements AutoCloseable {
- private final InternalJMXRegistrator internalJMXRegistrator;
- private final ObjectName on;
+ private static final class Nested extends InternalJMXRegistrator {
+ private final InternalJMXRegistrator parent;
- InternalJMXRegistration(InternalJMXRegistrator internalJMXRegistrator,
- ObjectName on) {
- this.internalJMXRegistrator = internalJMXRegistrator;
- this.on = on;
+ Nested(final InternalJMXRegistrator parent) {
+ this.parent = Preconditions.checkNotNull(parent);
+ }
+
+ @Override
+ MBeanServer getMBeanServer() {
+ return parent.getMBeanServer();
}
@Override
public void close() {
- internalJMXRegistrator.unregisterMBean(on);
+ try {
+ super.close();
+ } finally {
+ parent.removeChild(this);
+ }
}
}
+ private static final Logger LOG = LoggerFactory.getLogger(InternalJMXRegistrator.class);
@GuardedBy("this")
- private final Set<ObjectName> registeredObjectNames = new HashSet<>();
+ private final Set<ObjectName> registeredObjectNames = new HashSet<>(1);
@GuardedBy("this")
- private final List<InternalJMXRegistrator> children = new ArrayList<>();
+ private final List<Nested> children = new ArrayList<>();
+
+ public static InternalJMXRegistrator create(final MBeanServer configMBeanServer) {
+ return new Root(configMBeanServer);
+ }
- public synchronized InternalJMXRegistration registerMBean(Object object,
- ObjectName on) throws InstanceAlreadyExistsException {
+ public final synchronized InternalJMXRegistration registerMBean(final Object object, final ObjectName on)
+ throws InstanceAlreadyExistsException {
try {
- configMBeanServer.registerMBean(object, on);
- } catch (MBeanRegistrationException | NotCompliantMBeanException e) {
- throw new IllegalStateException(e);
+ getMBeanServer().registerMBean(object, on);
+ } catch (NotCompliantMBeanException e) {
+ throw new IllegalArgumentException("Object does not comply to JMX", e);
+ } catch (MBeanRegistrationException e) {
+ throw new IllegalStateException("Failed to register " + on, e);
}
+
registeredObjectNames.add(on);
return new InternalJMXRegistration(this, on);
}
- private synchronized void unregisterMBean(ObjectName on) {
+ final synchronized void unregisterMBean(final 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);
- }
+ Preconditions.checkState(removed, "Cannot unregister - ObjectName not found in 'registeredObjectNames': {}", on);
+
try {
- configMBeanServer.unregisterMBean(on);
- } catch (InstanceNotFoundException | MBeanRegistrationException e) {
- throw new IllegalStateException(e);
+ getMBeanServer().unregisterMBean(on);
+ } catch (InstanceNotFoundException e) {
+ LOG.warn("MBean {} not found on server", on, e);
+ } catch (MBeanRegistrationException e) {
+ throw new IllegalStateException("Failed to unregister MBean " + on, e);
}
}
- public synchronized InternalJMXRegistrator createChild() {
- InternalJMXRegistrator child = new InternalJMXRegistrator(configMBeanServer);
+ public final synchronized InternalJMXRegistrator createChild() {
+ final Nested child = new Nested(this);
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) {
- LOG.warn("Ignoring error while unregistering {}", on, e);
- }
- }
- registeredObjectNames.clear();
+ public void close() {
+ internalClose();
}
- public <T> T newMBeanProxy(ObjectName objectName, Class<T> interfaceClass) {
- return JMX.newMBeanProxy(configMBeanServer, objectName, interfaceClass);
+ public final <T> T newMBeanProxy(final ObjectName objectName, final Class<T> interfaceClass) {
+ return JMX.newMBeanProxy(getMBeanServer(), objectName, interfaceClass);
}
- public <T> T newMBeanProxy(ObjectName objectName, Class<T> interfaceClass,
- boolean notificationBroadcaster) {
- return JMX.newMBeanProxy(configMBeanServer, objectName, interfaceClass,
- notificationBroadcaster);
+ public final <T> T newMBeanProxy(final ObjectName objectName, final Class<T> interfaceClass,
+ final boolean notificationBroadcaster) {
+ return JMX.newMBeanProxy(getMBeanServer(), objectName, interfaceClass, notificationBroadcaster);
}
- public <T> T newMXBeanProxy(ObjectName objectName, Class<T> interfaceClass) {
- return JMX
- .newMXBeanProxy(configMBeanServer, objectName, interfaceClass);
+ public final <T> T newMXBeanProxy(final ObjectName objectName, final Class<T> interfaceClass) {
+ return JMX.newMXBeanProxy(getMBeanServer(), objectName, interfaceClass);
}
- public <T> T newMXBeanProxy(ObjectName objectName, Class<T> interfaceClass,
- boolean notificationBroadcaster) {
- return JMX.newMXBeanProxy(configMBeanServer, objectName,
- interfaceClass, notificationBroadcaster);
+ public final <T> T newMXBeanProxy(final ObjectName objectName, final Class<T> interfaceClass,
+ final boolean notificationBroadcaster) {
+ return JMX.newMXBeanProxy(getMBeanServer(), objectName, interfaceClass, notificationBroadcaster);
}
- public Set<ObjectName> getRegisteredObjectNames() {
+ public final Set<ObjectName> getRegisteredObjectNames() {
return Collections.unmodifiableSet(registeredObjectNames);
}
- public Set<ObjectName> queryNames(ObjectName name, QueryExp query) {
- Set<ObjectName> result = configMBeanServer.queryNames(name, query);
+ public final Set<ObjectName> queryNames(final ObjectName name, final QueryExp query) {
// keep only those that were registered using this instance
- return getSameNames(result);
+ return getSameNames(getMBeanServer().queryNames(name, query));
+ }
+
+ abstract MBeanServer getMBeanServer();
+
+ synchronized void removeChild(final InternalJMXRegistrator child) {
+ children.remove(child);
}
- private synchronized Set<ObjectName> getSameNames(Set<ObjectName> superSet) {
- Set<ObjectName> result = new HashSet<>(superSet);
+ private synchronized void internalClose() {
+ // close all children
+ for (InternalJMXRegistrator child : children) {
+ // This bypasses the call to removeChild(), preventing a potential deadlock when children are being closed
+ // concurrently
+ child.internalClose();
+ }
+ children.clear();
+
+ // close registered ONs
+ for (ObjectName on : registeredObjectNames) {
+ try {
+ getMBeanServer().unregisterMBean(on);
+ } catch (Exception e) {
+ LOG.warn("Ignoring error while unregistering {}", on, e);
+ }
+ }
+ registeredObjectNames.clear();
+ }
+
+ private synchronized Set<ObjectName> getSameNames(final Set<ObjectName> superSet) {
+ final Set<ObjectName> result = new HashSet<>(superSet);
result.retainAll(registeredObjectNames);
+
for (InternalJMXRegistrator child : children) {
result.addAll(child.getSameNames(superSet));
}
return result;
}
-
}