7d0bee25333b84148bfce23e1c09380e169b2149
[controller.git] / opendaylight / config / config-manager / src / main / java / org / opendaylight / controller / config / manager / impl / jmx / InternalJMXRegistrator.java
1 /*
2  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.controller.config.manager.impl.jmx;
9
10 import com.google.common.base.Preconditions;
11 import java.util.ArrayList;
12 import java.util.Collections;
13 import java.util.HashSet;
14 import java.util.List;
15 import java.util.Set;
16 import javax.annotation.concurrent.GuardedBy;
17 import javax.management.InstanceAlreadyExistsException;
18 import javax.management.InstanceNotFoundException;
19 import javax.management.JMX;
20 import javax.management.MBeanRegistrationException;
21 import javax.management.MBeanServer;
22 import javax.management.NotCompliantMBeanException;
23 import javax.management.ObjectName;
24 import javax.management.QueryExp;
25 import org.slf4j.Logger;
26 import org.slf4j.LoggerFactory;
27
28 abstract class InternalJMXRegistrator implements AutoCloseable {
29     private static final class Root extends InternalJMXRegistrator {
30         private final MBeanServer mbeanServer;
31
32         Root(final MBeanServer mbeanServer) {
33             this.mbeanServer = Preconditions.checkNotNull(mbeanServer);
34         }
35
36         @Override
37         MBeanServer getMBeanServer() {
38             return mbeanServer;
39         }
40     }
41
42     private static final class Nested extends InternalJMXRegistrator {
43         private final InternalJMXRegistrator parent;
44
45         Nested(final InternalJMXRegistrator parent) {
46             this.parent = Preconditions.checkNotNull(parent);
47         }
48
49         @Override
50         MBeanServer getMBeanServer() {
51             return parent.getMBeanServer();
52         }
53
54         @Override
55         public void close() {
56             try {
57                 super.close();
58             } finally {
59                 parent.removeChild(this);
60             }
61         }
62     }
63
64     private static final Logger LOG = LoggerFactory.getLogger(InternalJMXRegistrator.class);
65     @GuardedBy("this")
66     private final Set<ObjectName> registeredObjectNames = new HashSet<>(1);
67     @GuardedBy("this")
68     private final List<Nested> children = new ArrayList<>();
69
70     public static InternalJMXRegistrator create(final MBeanServer configMBeanServer) {
71         return new Root(configMBeanServer);
72     }
73
74     public final synchronized InternalJMXRegistration registerMBean(final Object object, final ObjectName on)
75             throws InstanceAlreadyExistsException {
76         try {
77             getMBeanServer().registerMBean(object, on);
78         } catch (final NotCompliantMBeanException e) {
79             throw new IllegalArgumentException("Object does not comply to JMX", e);
80         } catch (final MBeanRegistrationException e) {
81             throw new IllegalStateException("Failed to register " + on, e);
82         }
83
84         registeredObjectNames.add(on);
85         return new InternalJMXRegistration(this, on);
86     }
87
88     final synchronized void unregisterMBean(final ObjectName on) {
89         // first check that on was registered using this instance
90         boolean removed = registeredObjectNames.remove(on);
91         Preconditions.checkState(removed, "Cannot unregister - ObjectName not found in 'registeredObjectNames': %s", on);
92
93         try {
94             getMBeanServer().unregisterMBean(on);
95         } catch (final InstanceNotFoundException e) {
96             LOG.warn("MBean {} not found on server", on, e);
97         } catch (final MBeanRegistrationException e) {
98             throw new IllegalStateException("Failed to unregister MBean " + on, e);
99         }
100     }
101
102     public final synchronized InternalJMXRegistrator createChild() {
103         final Nested child = new Nested(this);
104         children.add(child);
105         return child;
106     }
107
108     /**
109      * Allow close to be called multiple times.
110      */
111     @Override
112     public void close() {
113         internalClose();
114     }
115
116     public final <T> T newMBeanProxy(final ObjectName objectName, final Class<T> interfaceClass) {
117         return JMX.newMBeanProxy(getMBeanServer(), objectName, interfaceClass);
118     }
119
120     public final <T> T newMBeanProxy(final ObjectName objectName, final Class<T> interfaceClass,
121             final boolean notificationBroadcaster) {
122         return JMX.newMBeanProxy(getMBeanServer(), objectName, interfaceClass, notificationBroadcaster);
123     }
124
125     public final <T> T newMXBeanProxy(final ObjectName objectName, final Class<T> interfaceClass) {
126         return JMX.newMXBeanProxy(getMBeanServer(), objectName, interfaceClass);
127     }
128
129     public final <T> T newMXBeanProxy(final ObjectName objectName, final Class<T> interfaceClass,
130             final boolean notificationBroadcaster) {
131         return JMX.newMXBeanProxy(getMBeanServer(), objectName, interfaceClass, notificationBroadcaster);
132     }
133
134     public final Set<ObjectName> getRegisteredObjectNames() {
135         return Collections.unmodifiableSet(registeredObjectNames);
136     }
137
138     public final Set<ObjectName> queryNames(final ObjectName name, final QueryExp query) {
139         // keep only those that were registered using this instance
140         return getSameNames(getMBeanServer().queryNames(name, query));
141     }
142
143     abstract MBeanServer getMBeanServer();
144
145     synchronized void removeChild(final InternalJMXRegistrator child) {
146         children.remove(child);
147     }
148
149     private synchronized void internalClose() {
150         // close all children
151         for (InternalJMXRegistrator child : children) {
152             // This bypasses the call to removeChild(), preventing a potential deadlock when children are being closed
153             // concurrently
154             child.internalClose();
155         }
156         children.clear();
157
158         // close registered ONs
159         for (ObjectName on : registeredObjectNames) {
160             try {
161                 getMBeanServer().unregisterMBean(on);
162             } catch (final Exception e) {
163                 LOG.warn("Ignoring error while unregistering {}", on, e);
164             }
165         }
166         registeredObjectNames.clear();
167     }
168
169     private synchronized Set<ObjectName> getSameNames(final Set<ObjectName> superSet) {
170         final Set<ObjectName> result = new HashSet<>(superSet);
171         result.retainAll(registeredObjectNames);
172
173         for (InternalJMXRegistrator child : children) {
174             result.addAll(child.getSameNames(superSet));
175         }
176         return result;
177     }
178 }