f4b326ea19bda835075947f625bd631a1dcdabfe
[controller.git] / opendaylight / config / config-manager / src / main / java / org / opendaylight / controller / config / manager / impl / jmx / InternalJMXRegistrator.java
1 /*
2  * Copyright (c) 2013, 2017 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",
92                 on);
93
94         try {
95             getMBeanServer().unregisterMBean(on);
96         } catch (final InstanceNotFoundException e) {
97             LOG.warn("MBean {} not found on server", on, e);
98         } catch (final MBeanRegistrationException e) {
99             throw new IllegalStateException("Failed to unregister MBean " + on, e);
100         }
101     }
102
103     public final synchronized InternalJMXRegistrator createChild() {
104         final Nested child = new Nested(this);
105         children.add(child);
106         return child;
107     }
108
109     /**
110      * Allow close to be called multiple times.
111      */
112     @Override
113     public void close() {
114         internalClose();
115     }
116
117     public final <T> T newMBeanProxy(final ObjectName objectName, final Class<T> interfaceClass) {
118         return JMX.newMBeanProxy(getMBeanServer(), objectName, interfaceClass);
119     }
120
121     public final <T> T newMBeanProxy(final ObjectName objectName, final Class<T> interfaceClass,
122             final boolean notificationBroadcaster) {
123         return JMX.newMBeanProxy(getMBeanServer(), objectName, interfaceClass, notificationBroadcaster);
124     }
125
126     public final <T> T newMXBeanProxy(final ObjectName objectName, final Class<T> interfaceClass) {
127         return JMX.newMXBeanProxy(getMBeanServer(), objectName, interfaceClass);
128     }
129
130     public final <T> T newMXBeanProxy(final ObjectName objectName, final Class<T> interfaceClass,
131             final boolean notificationBroadcaster) {
132         return JMX.newMXBeanProxy(getMBeanServer(), objectName, interfaceClass, notificationBroadcaster);
133     }
134
135     public final Set<ObjectName> getRegisteredObjectNames() {
136         return Collections.unmodifiableSet(registeredObjectNames);
137     }
138
139     public final Set<ObjectName> queryNames(final ObjectName name, final QueryExp query) {
140         // keep only those that were registered using this instance
141         return getSameNames(getMBeanServer().queryNames(name, query));
142     }
143
144     abstract MBeanServer getMBeanServer();
145
146     synchronized void removeChild(final InternalJMXRegistrator child) {
147         children.remove(child);
148     }
149
150     private synchronized void internalClose() {
151         // close all children
152         for (InternalJMXRegistrator child : children) {
153             // This bypasses the call to removeChild(), preventing a potential deadlock when children are being closed
154             // concurrently
155             child.internalClose();
156         }
157         children.clear();
158
159         // close registered ONs
160         for (ObjectName on : registeredObjectNames) {
161             try {
162                 getMBeanServer().unregisterMBean(on);
163             } catch (final InstanceNotFoundException | MBeanRegistrationException e) {
164                 LOG.warn("Ignoring error while unregistering {}", on, e);
165             }
166         }
167         registeredObjectNames.clear();
168     }
169
170     private synchronized Set<ObjectName> getSameNames(final Set<ObjectName> superSet) {
171         final Set<ObjectName> result = new HashSet<>(superSet);
172         result.retainAll(registeredObjectNames);
173
174         for (InternalJMXRegistrator child : children) {
175             result.addAll(child.getSameNames(superSet));
176         }
177         return result;
178     }
179 }