| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more contributor license |
| * agreements. See the NOTICE file distributed with this work for additional information regarding |
| * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 |
| * |
| * 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 org.apache.geode.admin.jmx.internal; |
| |
| import java.net.URL; |
| import java.util.Date; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import javax.management.InstanceNotFoundException; |
| import javax.management.JMException; |
| import javax.management.JMRuntimeException; |
| import javax.management.ListenerNotFoundException; |
| import javax.management.MBeanRegistrationException; |
| import javax.management.MBeanServer; |
| import javax.management.MBeanServerFactory; |
| import javax.management.MBeanServerNotification; |
| import javax.management.MalformedObjectNameException; |
| import javax.management.Notification; |
| import javax.management.NotificationListener; |
| import javax.management.ObjectName; |
| import javax.management.timer.TimerMBean; |
| |
| import org.apache.commons.modeler.ManagedBean; |
| import org.apache.commons.modeler.Registry; |
| import org.apache.logging.log4j.Level; |
| import org.apache.logging.log4j.Logger; |
| |
| import org.apache.geode.SystemFailure; |
| import org.apache.geode.admin.RuntimeAdminException; |
| import org.apache.geode.annotations.internal.MakeNotStatic; |
| import org.apache.geode.internal.ClassPathLoader; |
| import org.apache.geode.logging.internal.log4j.api.LogService; |
| |
| /** |
| * Common support for MBeans and {@link ManagedResource}s. Static loading of this class creates the |
| * MBeanServer and Modeler Registry. |
| * |
| * @since GemFire 3.5 |
| * |
| */ |
| public class MBeanUtil { |
| |
| private static final Logger logger = LogService.getLogger(); |
| |
| /** The default MBeanServer domain name is "GemFire" */ |
| private static final String DEFAULT_DOMAIN = "GemFire"; |
| |
| /** MBean Name for refreshTimer */ |
| private static final String REFRESH_TIMER_NAME = DEFAULT_DOMAIN + ":type=RefreshTimer"; |
| |
| /* indicates whether the mbeanServer, registry & refreshTimer are started */ |
| @MakeNotStatic |
| private static boolean isStarted; |
| |
| /** The Commons-Modeler configuration registry for our managed beans */ |
| @MakeNotStatic |
| private static Registry registry; |
| |
| /** The <code>MBeanServer</code> for this application */ |
| @MakeNotStatic |
| private static MBeanServer mbeanServer; |
| |
| /** MBean name of the Timer which handles refresh notifications */ |
| @MakeNotStatic |
| private static ObjectName refreshTimerObjectName; |
| |
| /** Actual TimerMBean responsible for refresh notifications */ |
| @MakeNotStatic |
| private static TimerMBean refreshTimer; |
| |
| /** |
| * Map of ObjectNames to current timerNotificationIds |
| * <p> |
| * map: key=ObjectName, value=map: key=RefreshNotificationType, value=timerNotificationId |
| */ |
| @MakeNotStatic |
| private static final Map<NotificationListener, Map<RefreshNotificationType, Integer>> refreshClients = |
| new HashMap<NotificationListener, Map<RefreshNotificationType, Integer>>(); |
| |
| /** key=ObjectName, value=ManagedResource */ |
| @MakeNotStatic |
| private static final Map<ObjectName, ManagedResource> managedResources = |
| new HashMap<ObjectName, ManagedResource>(); |
| |
| static { |
| try { |
| refreshTimerObjectName = ObjectName.getInstance(REFRESH_TIMER_NAME); |
| } catch (Exception e) { |
| logStackTrace(Level.ERROR, e); |
| } |
| } |
| |
| /** |
| * Initializes Mbean Server, Registry, Refresh Timer & registers Server Notification Listener. |
| * |
| * @return reference to the mbeanServer |
| */ |
| static MBeanServer start() { |
| if (!isStarted) { |
| mbeanServer = createMBeanServer(); |
| registry = createRegistry(); |
| |
| registerServerNotificationListener(); |
| createRefreshTimer(); |
| isStarted = true; |
| } |
| |
| return mbeanServer; |
| } |
| |
| /** |
| * Stops Registry, Refresh Timer. Releases Mbean Server after. |
| */ |
| static void stop() { |
| if (isStarted) { |
| stopRefreshTimer(); |
| |
| registry.stop(); |
| registry = null; |
| releaseMBeanServer();// makes mbeanServer null |
| isStarted = false; |
| } |
| } |
| |
| /** |
| * Create and configure (if necessary) and return the <code>MBeanServer</code> with which we will |
| * be registering our <code>ModelMBean</code> implementations. |
| * |
| * @see javax.management.MBeanServer |
| */ |
| static synchronized MBeanServer createMBeanServer() { |
| if (mbeanServer == null) { |
| mbeanServer = MBeanServerFactory.createMBeanServer(DEFAULT_DOMAIN); |
| } |
| return mbeanServer; |
| } |
| |
| /** |
| * Create and configure (if necessary) and return the Commons-Modeler registry of managed object |
| * descriptions. |
| * |
| * @see org.apache.commons.modeler.Registry |
| */ |
| static synchronized Registry createRegistry() { |
| if (registry == null) { |
| try { |
| registry = Registry.getRegistry(null, null); |
| if (mbeanServer == null) { |
| throw new IllegalStateException( |
| "MBean Server is not initialized yet."); |
| } |
| registry.setMBeanServer(mbeanServer); |
| |
| String mbeansResource = getOSPath("/org/apache/geode/admin/jmx/mbeans-descriptors.xml"); |
| |
| URL url = ClassPathLoader.getLatest().getResource(MBeanUtil.class, mbeansResource); |
| raiseOnFailure(url != null, String.format("Failed to find %s", |
| new Object[] {mbeansResource})); |
| registry.loadMetadata(url); |
| |
| // simple test to make sure the xml was actually loaded and is valid... |
| String[] test = registry.findManagedBeans(); |
| raiseOnFailure(test != null && test.length > 0, |
| String.format("Failed to load metadata from %s", |
| new Object[] {mbeansResource})); |
| } catch (Exception e) { |
| logStackTrace(Level.WARN, e); |
| throw new RuntimeAdminException( |
| "Failed to get MBean Registry", e); |
| } |
| } |
| return registry; |
| } |
| |
| /** |
| * Creates and registers a <code>ModelMBean</code> for the specified <code>ManagedResource</code>. |
| * State changing callbacks into the <code>ManagedResource</code> will also be made. |
| * |
| * @param resource the ManagedResource to create a managing MBean for |
| * |
| * @return The object name of the newly-created MBean |
| * |
| * @see ManagedResource#setModelMBean |
| */ |
| static ObjectName createMBean(ManagedResource resource) { |
| return createMBean(resource, lookupManagedBean(resource)); |
| } |
| |
| /** |
| * Creates and registers a <code>ModelMBean</code> for the specified <code>ManagedResource</code>. |
| * State changing callbacks into the <code>ManagedResource</code> will also be made. |
| * |
| * @param resource the ManagedResource to create a managing MBean for |
| * @param managed the ManagedBean definition to create the MBean with |
| * @see ManagedResource#setModelMBean |
| */ |
| static ObjectName createMBean(ManagedResource resource, ManagedBean managed) { |
| |
| try { |
| DynamicManagedBean mb = new DynamicManagedBean(managed); |
| resource.setModelMBean(mb.createMBean(resource)); |
| |
| // create the ObjectName and register the MBean... |
| final ObjectName objName; |
| try { |
| objName = ObjectName.getInstance(resource.getMBeanName()); |
| } catch (MalformedObjectNameException e) { |
| throw new MalformedObjectNameException(String.format("%s in '%s'", |
| new Object[] {e.getMessage(), resource.getMBeanName()})); |
| } |
| |
| synchronized (MBeanUtil.class) { |
| // Only register a bean once. Otherwise, you risk race |
| // conditions with things like the RMI connector accessing it. |
| |
| if (mbeanServer != null && !mbeanServer.isRegistered(objName)) { |
| mbeanServer.registerMBean(resource.getModelMBean(), objName); |
| synchronized (managedResources) { |
| managedResources.put(objName, resource); |
| } |
| } |
| } |
| return objName; |
| } catch (java.lang.Exception e) { |
| throw new RuntimeAdminException( |
| String.format("Failed to create MBean representation for resource %s.", |
| new Object[] {resource.getMBeanName()}), |
| e); |
| } |
| } |
| |
| /** |
| * Ensures that an MBean is registered for the specified <code>ManagedResource</code>. If an MBean |
| * cannot be found in the <code>MBeanServer</code>, then this creates and registers a |
| * <code>ModelMBean</code>. State changing callbacks into the <code>ManagedResource</code> will |
| * also be made. |
| * |
| * @param resource the ManagedResource to create a managing MBean for |
| * |
| * @return The object name of the MBean that manages the ManagedResource |
| * |
| * @see ManagedResource#setModelMBean |
| */ |
| static ObjectName ensureMBeanIsRegistered(ManagedResource resource) { |
| try { |
| ObjectName objName = ObjectName.getInstance(resource.getMBeanName()); |
| synchronized (MBeanUtil.class) { |
| if (mbeanServer != null && !mbeanServer.isRegistered(objName)) { |
| return createMBean(resource); |
| } |
| } |
| raiseOnFailure(mbeanServer.isRegistered(objName), |
| String.format("Could not find a MBean registered with ObjectName: %s.", |
| new Object[] {objName.toString()})); |
| return objName; |
| } catch (java.lang.Exception e) { |
| throw new RuntimeAdminException(e); |
| } |
| } |
| |
| /** |
| * Retrieves the <code>ManagedBean</code> configuration from the Registry for the specified |
| * <code>ManagedResource</code> |
| * |
| * @param resource the ManagedResource to find the configuration for |
| */ |
| static ManagedBean lookupManagedBean(ManagedResource resource) { |
| // find the registry defn for our MBean... |
| ManagedBean managed = null; |
| if (registry != null) { |
| managed = registry.findManagedBean(resource.getManagedResourceType().getClassTypeName()); |
| } else { |
| throw new IllegalArgumentException( |
| "ManagedBean is null"); |
| } |
| |
| if (managed == null) { |
| throw new IllegalArgumentException( |
| "ManagedBean is null"); |
| } |
| |
| // customize the defn... |
| managed.setClassName("org.apache.geode.admin.jmx.internal.MX4JModelMBean"); |
| |
| return managed; |
| } |
| |
| /** |
| * Registers a refresh notification for the specified client MBean. Specifying zero for the |
| * refreshInterval disables notification for the refresh client. Note: this does not currently |
| * support remote connections. |
| * |
| * @param client client to listen for refresh notifications |
| * @param userData userData to register with the Notification |
| * @param type refresh notification type the client will use |
| * @param refreshInterval the seconds between refreshes |
| */ |
| static void registerRefreshNotification(NotificationListener client, Object userData, |
| RefreshNotificationType type, long refreshInterval) { |
| if (client == null) { |
| throw new IllegalArgumentException( |
| "NotificationListener is required"); |
| } |
| if (type == null) { |
| throw new IllegalArgumentException( |
| "RefreshNotificationType is required"); |
| } |
| if (refreshTimerObjectName == null || refreshTimer == null) { |
| throw new IllegalStateException( |
| "RefreshTimer has not been properly initialized."); |
| } |
| |
| try { |
| // get the notifications for the specified client... |
| Map<RefreshNotificationType, Integer> notifications = null; |
| synchronized (refreshClients) { |
| notifications = (Map<RefreshNotificationType, Integer>) refreshClients.get(client); |
| } |
| |
| if (notifications == null) { |
| // If refreshInterval is being set to zero and notifications is removed return |
| if (refreshInterval <= 0) { |
| return; |
| } |
| |
| // never registered before, so add client... |
| notifications = new HashMap<RefreshNotificationType, Integer>(); |
| synchronized (refreshClients) { |
| refreshClients.put(client, notifications); |
| } |
| validateRefreshTimer(); |
| try { |
| // register client as a listener with MBeanServer... |
| mbeanServer.addNotificationListener(refreshTimerObjectName, // timer to listen to |
| client, // the NotificationListener object |
| null, // optional NotificationFilter TODO: convert to using |
| new Object() // not used but null throws IllegalArgumentException |
| ); |
| } catch (InstanceNotFoundException e) { |
| // should not happen since we already checked refreshTimerObjectName |
| logStackTrace(Level.WARN, e, |
| "Could not find registered RefreshTimer instance."); |
| } |
| } |
| |
| // TODO: change to manipulating timer indirectly thru mserver... |
| |
| // check for pre-existing refresh notification entry... |
| Integer timerNotificationId = (Integer) notifications.get(type); |
| if (timerNotificationId != null) { |
| try { |
| // found one, so let's remove it... |
| refreshTimer.removeNotification(timerNotificationId); |
| } catch (InstanceNotFoundException e) { |
| // that's ok cause we just wanted to remove it anyway |
| } finally { |
| // null out the map entry for that notification type... |
| notifications.put(type, null); |
| } |
| } |
| |
| if (refreshInterval > 0) { |
| // add notification to the refresh timer... |
| timerNotificationId = refreshTimer.addNotification(type.getType(), // type |
| type.getMessage(), // message = "refresh" |
| userData, // userData |
| new Date(System.currentTimeMillis() + refreshInterval * 1000L), // first occurrence |
| refreshInterval * 1000L); // period to repeat |
| |
| // put an entry into the map for the listener... |
| notifications.put(type, timerNotificationId); |
| } else { |
| // do nothing! refreshInterval must be over 0 to do anything... |
| } |
| } catch (java.lang.RuntimeException e) { |
| logStackTrace(Level.WARN, e); |
| throw e; |
| } catch (VirtualMachineError err) { |
| SystemFailure.initiateFailure(err); |
| // If this ever returns, rethrow the error. We're poisoned |
| // now, so don't let this thread continue. |
| throw err; |
| } catch (java.lang.Error e) { |
| // Whenever you catch Error or Throwable, you must also |
| // catch VirtualMachineError (see above). However, there is |
| // _still_ a possibility that you are dealing with a cascading |
| // error condition, so you also need to check to see if the JVM |
| // is still usable: |
| SystemFailure.checkFailure(); |
| logStackTrace(Level.ERROR, e); |
| throw e; |
| } |
| } |
| |
| /** |
| * Verifies a refresh notification for the specified client MBean. If notification is not |
| * registered, then returns a false |
| * |
| * @param client client to listen for refresh notifications |
| * @param type refresh notification type the client will use |
| * |
| * @return isRegistered boolean indicating if a notification is registered |
| */ |
| static boolean isRefreshNotificationRegistered(NotificationListener client, |
| RefreshNotificationType type) { |
| boolean isRegistered = false; |
| |
| // get the notifications for the specified client... |
| Map<RefreshNotificationType, Integer> notifications = null; |
| synchronized (refreshClients) { |
| notifications = (Map<RefreshNotificationType, Integer>) refreshClients.get(client); |
| } |
| |
| // never registered before if null ... |
| if (notifications != null) { |
| // check for pre-existing refresh notification entry... |
| Integer timerNotificationId = notifications.get(type); |
| if (timerNotificationId != null) { |
| isRegistered = true; |
| } |
| } |
| |
| return isRegistered; |
| } |
| |
| /** |
| * Validates refreshTimer has been registered without problems and attempts to re-register if |
| * there is a problem. |
| */ |
| static void validateRefreshTimer() { |
| if (refreshTimerObjectName == null || refreshTimer == null) { |
| createRefreshTimer(); |
| } |
| |
| raiseOnFailure(refreshTimer != null, "Failed to validate Refresh Timer"); |
| |
| if (mbeanServer != null && !mbeanServer.isRegistered(refreshTimerObjectName)) { |
| try { |
| mbeanServer.registerMBean(refreshTimer, refreshTimerObjectName); |
| } catch (JMException e) { |
| logStackTrace(Level.WARN, e); |
| } catch (JMRuntimeException e) { |
| logStackTrace(Level.WARN, e); |
| } |
| } |
| } |
| |
| /** |
| * Initializes the timer for sending refresh notifications. |
| */ |
| static void createRefreshTimer() { |
| try { |
| refreshTimer = new javax.management.timer.Timer(); |
| mbeanServer.registerMBean(refreshTimer, refreshTimerObjectName); |
| |
| refreshTimer.start(); |
| } catch (JMException e) { |
| logStackTrace(Level.WARN, e, |
| "Failed to create/register/start refresh timer."); |
| } catch (JMRuntimeException e) { |
| logStackTrace(Level.WARN, e, |
| "Failed to create/register/start refresh timer."); |
| } catch (Exception e) { |
| logStackTrace(Level.WARN, e, |
| "Failed to create/register/start refresh timer."); |
| } |
| } |
| |
| /** |
| * Initializes the timer for sending refresh notifications. |
| */ |
| static void stopRefreshTimer() { |
| try { |
| if (refreshTimer != null && mbeanServer != null) { |
| mbeanServer.unregisterMBean(refreshTimerObjectName); |
| |
| refreshTimer.stop(); |
| } |
| } catch (JMException e) { |
| logStackTrace(Level.WARN, e); |
| } catch (JMRuntimeException e) { |
| logStackTrace(Level.WARN, e); |
| } catch (Exception e) { |
| logStackTrace(Level.DEBUG, e, "Failed to stop refresh timer for MBeanUtil"); |
| } |
| } |
| |
| /** |
| * Return a String that been modified to be compliant as a property of an ObjectName. |
| * <p> |
| * The property name of an ObjectName may not contain any of the following characters: <b><i>: , = |
| * * ?</i></b> |
| * <p> |
| * This method will replace the above non-compliant characters with a dash: <b><i>-</i></b> |
| * <p> |
| * If value is empty, this method will return the string "nothing". |
| * <p> |
| * Note: this is <code>public</code> because certain tests call this from outside of the package. |
| * TODO: clean this up |
| * |
| * @param value the potentially non-compliant ObjectName property |
| * @return the value modified to be compliant as an ObjectName property |
| */ |
| public static String makeCompliantMBeanNameProperty(String value) { |
| value = value.replace(':', '-'); |
| value = value.replace(',', '-'); |
| value = value.replace('=', '-'); |
| value = value.replace('*', '-'); |
| value = value.replace('?', '-'); |
| if (value.length() < 1) { |
| value = "nothing"; |
| } |
| return value; |
| } |
| |
| /** |
| * Unregisters all GemFire MBeans and then releases the MBeanServer for garbage collection. |
| */ |
| static void releaseMBeanServer() { |
| try { |
| // unregister all GemFire mbeans... |
| Iterator iter = mbeanServer.queryNames(null, null).iterator(); |
| while (iter.hasNext()) { |
| ObjectName name = (ObjectName) iter.next(); |
| if (name.getDomain().startsWith(DEFAULT_DOMAIN)) { |
| unregisterMBean(name); |
| } |
| } |
| |
| // last, release the mbean server... |
| MBeanServerFactory.releaseMBeanServer(mbeanServer); |
| mbeanServer = null; |
| } catch (JMRuntimeException e) { |
| logStackTrace(Level.WARN, e); |
| } |
| /* |
| * See #42391. Cleaning up the static maps which might be still holding references to |
| * ManagedResources |
| */ |
| synchronized (MBeanUtil.managedResources) { |
| MBeanUtil.managedResources.clear(); |
| } |
| synchronized (refreshClients) { |
| refreshClients.clear(); |
| } |
| /* |
| * See #42391. Cleaning up the static maps which might be still holding references to |
| * ManagedResources |
| */ |
| synchronized (MBeanUtil.managedResources) { |
| MBeanUtil.managedResources.clear(); |
| } |
| synchronized (refreshClients) { |
| refreshClients.clear(); |
| } |
| } |
| |
| /** |
| * Returns true if a MBean with given ObjectName is registered. |
| * |
| * @param objectName ObjectName to use for checking if MBean is registered |
| * @return true if MBeanServer is not null & MBean with given ObjectName is registered with the |
| * MBeanServer |
| */ |
| static boolean isRegistered(ObjectName objectName) { |
| return mbeanServer != null && mbeanServer.isRegistered(objectName); |
| } |
| |
| /** |
| * Unregisters the identified MBean if it's registered. |
| */ |
| static void unregisterMBean(ObjectName objectName) { |
| try { |
| if (mbeanServer != null && mbeanServer.isRegistered(objectName)) { |
| mbeanServer.unregisterMBean(objectName); |
| } |
| } catch (MBeanRegistrationException e) { |
| logStackTrace(Level.WARN, null, |
| String.format("Failed while unregistering MBean with ObjectName : %s", |
| new Object[] {objectName})); |
| } catch (InstanceNotFoundException e) { |
| logStackTrace(Level.WARN, null, |
| String.format("While unregistering, could not find MBean with ObjectName : %s", |
| new Object[] {objectName})); |
| } catch (JMRuntimeException e) { |
| logStackTrace(Level.WARN, null, |
| String.format("Could not un-register MBean with ObjectName : %s", |
| new Object[] {objectName})); |
| } |
| } |
| |
| static void unregisterMBean(ManagedResource resource) { |
| if (resource != null) { |
| unregisterMBean(resource.getObjectName()); |
| |
| // call cleanup on managedResource here and not rely on listener |
| // since it is possible that notification listener not deliver |
| // all notifications of un-registration. If resource is |
| // cleaned here, another call from the listener should be as good as a no-op |
| cleanupResource(resource); |
| } |
| } |
| |
| // cleanup resource |
| private static void cleanupResource(ManagedResource resource) { |
| synchronized (MBeanUtil.managedResources) { |
| MBeanUtil.managedResources.remove(resource.getObjectName()); |
| } |
| resource.cleanupResource(); |
| |
| // get the notifications for the specified client... |
| Map<RefreshNotificationType, Integer> notifications = null; |
| synchronized (refreshClients) { |
| notifications = (Map<RefreshNotificationType, Integer>) refreshClients.remove(resource); |
| } |
| |
| // never registered before if null ... |
| // Also as of current, there is ever only 1 Notification type per |
| // MBean, so we do need need a while loop here |
| if (notifications != null) { |
| |
| // Fix for findbugs reported inefficiency with keySet(). |
| Set<Map.Entry<RefreshNotificationType, Integer>> entries = notifications.entrySet(); |
| |
| for (Map.Entry<RefreshNotificationType, Integer> e : entries) { |
| Integer timerNotificationId = e.getValue(); |
| if (null != timerNotificationId) { |
| try { |
| // found one, so let's remove it... |
| refreshTimer.removeNotification(timerNotificationId); |
| } catch (InstanceNotFoundException xptn) { |
| // that's ok cause we just wanted to remove it anyway |
| logStackTrace(Level.DEBUG, xptn); |
| } |
| } |
| } |
| |
| try { |
| if (mbeanServer != null && mbeanServer.isRegistered(refreshTimerObjectName)) { |
| // remove client as a listener with MBeanServer... |
| mbeanServer.removeNotificationListener(refreshTimerObjectName, // timer to listen to |
| (NotificationListener) resource // the NotificationListener object |
| ); |
| } |
| } catch (ListenerNotFoundException xptn) { |
| // should not happen since we already checked refreshTimerObjectName |
| logStackTrace(Level.WARN, null, xptn.getMessage()); |
| } catch (InstanceNotFoundException xptn) { |
| // should not happen since we already checked refreshTimerObjectName |
| logStackTrace(Level.WARN, null, |
| String.format("While unregistering, could not find MBean with ObjectName : %s", |
| new Object[] {refreshTimerObjectName})); |
| } |
| } |
| } |
| |
| // ----- borrowed the following from admin.internal.RemoteCommand ----- |
| /** Translates the path between Windows and UNIX. */ |
| static String getOSPath(String path) { |
| if (pathIsWindows(path)) { |
| return path.replace('/', '\\'); |
| } else { |
| return path.replace('\\', '/'); |
| } |
| } |
| |
| /** Returns true if the path is on Windows. */ |
| static boolean pathIsWindows(String path) { |
| if (path != null && path.length() > 1) { |
| return (Character.isLetter(path.charAt(0)) && path.charAt(1) == ':') |
| || (path.startsWith("//") || path.startsWith("\\\\")); |
| } |
| return false; |
| } |
| |
| static void registerServerNotificationListener() { |
| if (mbeanServer == null) { |
| return; |
| } |
| try { |
| // the MBeanServerDelegate name is spec'ed as the following... |
| ObjectName delegate = ObjectName.getInstance("JMImplementation:type=MBeanServerDelegate"); |
| mbeanServer.addNotificationListener(delegate, new NotificationListener() { |
| @Override |
| public void handleNotification(Notification notification, Object handback) { |
| MBeanServerNotification serverNotification = (MBeanServerNotification) notification; |
| if (MBeanServerNotification.UNREGISTRATION_NOTIFICATION |
| .equals(serverNotification.getType())) { |
| ObjectName objectName = serverNotification.getMBeanName(); |
| synchronized (MBeanUtil.managedResources) { |
| Object entry = MBeanUtil.managedResources.get(objectName); |
| if (entry == null) |
| return; |
| if (!(entry instanceof ManagedResource)) { |
| throw new ClassCastException(String.format("%s is not a ManagedResource", |
| new Object[] {entry.getClass().getName()})); |
| } |
| ManagedResource resource = (ManagedResource) entry; |
| { |
| // call cleanup on managedResource |
| cleanupResource(resource); |
| } |
| } |
| } |
| } |
| }, null, null); |
| } catch (JMException e) { |
| logStackTrace(Level.WARN, e, |
| "Failed to register ServerNotificationListener."); |
| } catch (JMRuntimeException e) { |
| logStackTrace(Level.WARN, e, |
| "Failed to register ServerNotificationListener."); |
| } |
| } |
| |
| /** |
| * Logs the stack trace for the given Throwable if logger is initialized else prints the stack |
| * trace using System.out. |
| * |
| * @param level severity level to log at |
| * @param throwable Throwable to log stack trace for |
| */ |
| public static void logStackTrace(Level level, Throwable throwable) { |
| logStackTrace(level, throwable, null); |
| } |
| |
| /** |
| * Logs the stack trace for the given Throwable if logger is initialized else prints the stack |
| * trace using System.out. |
| * |
| * @param level severity level to log at |
| * @param throwable Throwable to log stack trace for |
| * @param message user friendly error message to show |
| */ |
| public static void logStackTrace(Level level, Throwable throwable, String message) { |
| logger.log(level, message, throwable); |
| } |
| |
| /** |
| * Raises RuntimeAdminException with given 'message' if given 'condition' is false. |
| * |
| * @param condition condition to evaluate |
| * @param message failure message |
| */ |
| private static void raiseOnFailure(boolean condition, String message) { |
| if (!condition) { |
| throw new RuntimeAdminException(message); |
| } |
| } |
| } |