/** * * Copyright * 2009-2015 Jayway Products AB * 2016-2017 Föreningen Sambruk * * Licensed under AGPL, Version 3.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.gnu.org/licenses/agpl.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package se.streamsource.infrastructure.jmx; import org.slf4j.LoggerFactory; import javax.management.MBeanServer; import javax.management.MBeanServerNotification; import javax.management.MalformedObjectNameException; import javax.management.Notification; import javax.management.NotificationListener; import javax.management.ObjectName; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * Tracker that simplifies keeping track of MBeans in an MBeanServer * whose names matches a particular pattern. Callbacks are called * when MBeans are added/removed if their names match the given pattern. * * Example usage: * new MBeanTracker(new ObjectName("*:*,name=Circuit breaker"), server).registerCallback(updateStatus).start(); */ public class MBeanTracker { private boolean started = false; private ObjectName pattern; private MBeanServer server; private List<TrackerCallback> callbacks = new ArrayList<TrackerCallback>(); private List<ObjectName> tracked = Collections.synchronizedList(new ArrayList<ObjectName>()); private RegistrationListener registrationListener; public MBeanTracker(ObjectName pattern, MBeanServer server) { this.pattern = pattern; this.server = server; } public MBeanTracker registerCallback(TrackerCallback callback) { List<TrackerCallback> newList = new ArrayList<TrackerCallback>(callbacks); newList.add(callback); callbacks = newList; return this; } public MBeanTracker unregisterCallback(TrackerCallback callback) { List<TrackerCallback> newList = new ArrayList<TrackerCallback>(callbacks); newList.remove(callback); callbacks = newList; return this; } public Iterable<ObjectName> getTracked() { return tracked; } public void start() { if (!started) { try { registrationListener = new RegistrationListener(); server.addNotificationListener(new ObjectName("JMImplementation:type=MBeanServerDelegate"), registrationListener, null, null); for (ObjectName objectName : server.queryNames(pattern, null)) { for (TrackerCallback callback : callbacks) { try { tracked.add(objectName); callback.addedMBean(objectName, server); } catch (Throwable throwable) { LoggerFactory.getLogger(MBeanTracker.class).error("Tracker callback threw exception", throwable); } } } started = true; } catch (Exception e) { // Could not start } } } public void stop() { if (started) { try { server.removeNotificationListener(new ObjectName("JMImplementation:type=MBeanServerDelegate"), registrationListener); for (ObjectName objectName : server.queryNames(pattern, null)) { for (TrackerCallback callback : callbacks) { try { callback.removedMBean(objectName, server); tracked.remove(objectName); } catch (Throwable throwable) { LoggerFactory.getLogger(MBeanTracker.class).error("Tracker callback threw exception", throwable); } } } started = false; } catch (Exception e) { // Could not start } } } public interface TrackerCallback { void addedMBean(ObjectName newMBean, MBeanServer server) throws Throwable; void removedMBean(ObjectName removedMBean, MBeanServer server) throws Throwable; } class RegistrationListener implements NotificationListener { public void handleNotification(Notification notification, Object o) { if (notification instanceof MBeanServerNotification) { MBeanServerNotification serverNotification = (MBeanServerNotification) notification; if (pattern.apply(serverNotification.getMBeanName())) { if (serverNotification.getType().equals(MBeanServerNotification.REGISTRATION_NOTIFICATION)) { for (TrackerCallback callback : callbacks) { try { tracked.add(serverNotification.getMBeanName()); callback.addedMBean(serverNotification.getMBeanName(), server); } catch (Throwable throwable) { LoggerFactory.getLogger(MBeanTracker.class).error("Tracker callback threw exception", throwable); } } } else if (serverNotification.getType().equals(MBeanServerNotification.UNREGISTRATION_NOTIFICATION)) { for (TrackerCallback callback : callbacks) { try { callback.removedMBean(serverNotification.getMBeanName(), server); tracked.remove(serverNotification.getMBeanName()); } catch (Throwable throwable) { LoggerFactory.getLogger(MBeanTracker.class).error("Tracker callback threw exception", throwable); } } } } } } } }