net.sf.ehcache.management.ManagementService.java Source code

Java tutorial

Introduction

Here is the source code for net.sf.ehcache.management.ManagementService.java

Source

/**
 *  Copyright 2003-2007 Luck Consulting Pty Ltd
 *
 *  Licensed 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 net.sf.ehcache.management;

import net.sf.ehcache.CacheException;
import net.sf.ehcache.Status;
import net.sf.ehcache.event.CacheManagerEventListener;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import javax.management.InstanceAlreadyExistsException;
import javax.management.MBeanRegistrationException;
import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.NotCompliantMBeanException;
import javax.management.ObjectName;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

/**
 * Ehcache CacheManagers and Caches have lifecycles. Often normal use of a CacheManager
 * will be to shut it down and create a new one from within a running JVM. For example,
 * in Java EE environments, applications are often undeployed and then redeployed. A
 * servlet listener, {@link net.sf.ehcache.constructs.web.ShutdownListener} enables this
 * to be detected and the CacheManager shutdown.
 * <p/>
 * When a CacheManager is shut down we need to ensure there is no memory, resource or
 * thread leakage. An MBeanServer, particularly a platform MBeanServer, can be expected
 * to exist for the lifespan of the JVM. Accordingly, we need to deregister them when
 * needed without creating a leakage.
 * <p/>
 * The second purpose of this class (and this package) is to keep management concerns away
 * from the core ehcache packages. That way, JMX is not a required dependency, but rather
 * an optional one.
 * <p/>
 * This class is constructable as of 1.5 to support injection via IoC containers.
 *
 * @author Greg Luck
 * @version $Id: ManagementService.java 704 2008-07-13 00:17:52Z gregluck $
 * @since 1.3
 */
public class ManagementService implements CacheManagerEventListener {

    private static final Log LOG = LogFactory.getLog(ManagementService.class.getName());

    private MBeanServer mBeanServer;
    private net.sf.ehcache.CacheManager backingCacheManager;
    private boolean registerCacheManager;
    private boolean registerCaches;
    private boolean registerCacheConfigurations;
    private boolean registerCacheStatistics;
    private Status status;

    /**
     * A constructor for a management service for a range of possible MBeans.
     * <p/>
     * The {@link #init()} method needs to be called after construction which causes
     *  the selected monitoring options to be be registered
     * with the provided MBeanServer for caches in the given CacheManager.
     * <p/>
     * While registering the CacheManager enables traversal to all of the other
     * items,
     * this requires programmatic traversal. The other options allow entry points closer
     * to an item of interest and are more accessible from JMX management tools like JConsole.
     * Moreover CacheManager and Cache are not serializable, so remote monitoring is not possible
     * for CacheManager or Cache, while CacheStatistics and CacheConfiguration are. Finally
     * CacheManager and Cache enable management operations to be performed.
     * <p/>
     * Once monitoring is enabled caches will automatically added and removed from the MBeanServer
     * as they are added and disposed of from the CacheManager. When the CacheManager itself
     * shutsdown all registered MBeans will be unregistered.
     *
     * @param cacheManager                the CacheManager to listen to
     * @param mBeanServer                 the MBeanServer to register MBeans to
     * @param registerCacheManager        Whether to register the CacheManager MBean
     * @param registerCaches              Whether to register the Cache MBeans
     * @param registerCacheConfigurations Whether to register the CacheConfiguration MBeans
     * @param registerCacheStatistics     Whether to register the CacheStatistics MBeans
     * @throws net.sf.ehcache.CacheException if something goes wrong with init()
     */
    public ManagementService(net.sf.ehcache.CacheManager cacheManager, MBeanServer mBeanServer,
            boolean registerCacheManager, boolean registerCaches, boolean registerCacheConfigurations,
            boolean registerCacheStatistics) throws CacheException {

        status = Status.STATUS_UNINITIALISED;
        backingCacheManager = cacheManager;
        this.mBeanServer = mBeanServer;
        this.registerCacheManager = registerCacheManager;
        this.registerCaches = registerCaches;
        this.registerCacheConfigurations = registerCacheConfigurations;
        this.registerCacheStatistics = registerCacheStatistics;
    }

    /**
     * A convenience static method which creates a ManagementService and initialises it with the
     * supplied parameters.
     *
     * @param cacheManager                the CacheManager to listen to
     * @param mBeanServer                 the MBeanServer to register MBeans to
     * @param registerCacheManager        Whether to register the CacheManager MBean
     * @param registerCaches              Whether to register the Cache MBeans
     * @param registerCacheConfigurations Whether to register the CacheConfiguration MBeans
     * @param registerCacheStatistics     Whether to register the CacheStatistics MBeans
     * @see ManagementService#ManagementService(net.sf.ehcache.CacheManager, javax.management.MBeanServer, boolean, boolean, boolean, boolean)
     */
    public static void registerMBeans(net.sf.ehcache.CacheManager cacheManager, MBeanServer mBeanServer,
            boolean registerCacheManager, boolean registerCaches, boolean registerCacheConfigurations,
            boolean registerCacheStatistics) throws CacheException {

        ManagementService registry = new ManagementService(cacheManager, mBeanServer, registerCacheManager,
                registerCaches, registerCacheConfigurations, registerCacheStatistics);

        registry.init();
    }

    /**
     * Call to register the mbeans in the mbean server and start the event listeners and do any other required initialisation.
     * Once intialised, it registers itself as a CacheManageEvenListener with the backing CacheManager, so
     * that it can participate in lifecycle and other events.
     *
     * @throws net.sf.ehcache.CacheException - all exceptions are wrapped in CacheException
     */
    public void init() throws CacheException {
        CacheManager cacheManager = new CacheManager(backingCacheManager);
        try {
            registerCacheManager(cacheManager);

            List caches = cacheManager.getCaches();
            for (int i = 0; i < caches.size(); i++) {
                Cache cache = (Cache) caches.get(i);
                registerCachesIfRequired(cache);
                registerCacheStatisticsIfRequired(cache);
                registerCacheConfigurationIfRequired(cache);
            }
        } catch (Exception e) {
            throw new CacheException(e);
        }
        status = Status.STATUS_ALIVE;
        backingCacheManager.getCacheManagerEventListenerRegistry().registerListener(this);
    }

    private void registerCacheManager(CacheManager cacheManager)
            throws InstanceAlreadyExistsException, MBeanRegistrationException, NotCompliantMBeanException {
        if (registerCacheManager) {
            mBeanServer.registerMBean(cacheManager, cacheManager.getObjectName());
        }
    }

    private void registerCacheConfigurationIfRequired(Cache cache)
            throws InstanceAlreadyExistsException, MBeanRegistrationException, NotCompliantMBeanException {
        if (registerCacheConfigurations) {
            CacheConfiguration cacheConfiguration = cache.getCacheConfiguration();
            mBeanServer.registerMBean(cacheConfiguration, cacheConfiguration.getObjectName());
        }
    }

    private void registerCacheStatisticsIfRequired(Cache cache)
            throws InstanceAlreadyExistsException, MBeanRegistrationException, NotCompliantMBeanException {
        if (registerCacheStatistics) {
            CacheStatistics cacheStatistics = cache.getStatistics();
            mBeanServer.registerMBean(cacheStatistics, cacheStatistics.getObjectName());
        }
    }

    private void registerCachesIfRequired(Cache cache)
            throws InstanceAlreadyExistsException, MBeanRegistrationException, NotCompliantMBeanException {
        if (registerCaches) {
            mBeanServer.registerMBean(cache, cache.getObjectName());
        }
    }

    /**
     * Returns the listener status.
     *
     * @return the status at the point in time the method is called
     */
    public Status getStatus() {
        return status;
    }

    /**
     * Stop the listener and free any resources.
     * Removes registered ObjectNames
     *
     * @throws net.sf.ehcache.CacheException - all exceptions are wrapped in CacheException
     */
    public void dispose() throws CacheException {
        Set registeredObjectNames = null;

        try {
            //CacheManager MBean
            registeredObjectNames = mBeanServer.queryNames(CacheManager.createObjectName(backingCacheManager),
                    null);
            //Other MBeans for this CacheManager
            registeredObjectNames.addAll(mBeanServer.queryNames(
                    new ObjectName("net.sf.ehcache:*,CacheManager=" + backingCacheManager.toString()), null));
        } catch (MalformedObjectNameException e) {
            //this should not happen
            LOG.error("Error querying MBeanServer. Error was " + e.getMessage(), e);
        }
        for (Iterator iterator = registeredObjectNames.iterator(); iterator.hasNext();) {
            ObjectName objectName = (ObjectName) iterator.next();
            try {
                mBeanServer.unregisterMBean(objectName);
            } catch (Exception e) {
                LOG.error("Error unregistering object instance " + objectName + " . Error was " + e.getMessage(),
                        e);
            }
        }
        status = Status.STATUS_SHUTDOWN;
    }

    /**
     * Called immediately after a cache has been added and activated.
     * <p/>
     * Note that the CacheManager calls this method from a synchronized method. Any attempt to
     * call a synchronized method on CacheManager from this method will cause a deadlock.
     * <p/>
     * Note that activation will also cause a CacheEventListener status change notification
     * from {@link net.sf.ehcache.Status#STATUS_UNINITIALISED} to
     * {@link net.sf.ehcache.Status#STATUS_ALIVE}. Care should be taken on processing that
     * notification because:
     * <ul>
     * <li>the cache will not yet be accessible from the CacheManager.
     * <li>the addCaches methods which cause this notification are synchronized on the
     * CacheManager. An attempt to call {@link net.sf.ehcache.CacheManager#getEhcache(String)}
     * will cause a deadlock.
     * </ul>
     * The calling method will block until this method returns.
     * <p/>
     *
     * @param cacheName the name of the <code>Cache</code> the operation relates to
     * @see net.sf.ehcache.event.CacheEventListener
     */
    public void notifyCacheAdded(String cacheName) {
        if (registerCaches || registerCacheStatistics || registerCacheConfigurations) {
            Cache cache = new Cache(backingCacheManager.getCache(cacheName));
            try {
                registerCachesIfRequired(cache);
                registerCacheStatisticsIfRequired(cache);
                registerCacheConfigurationIfRequired(cache);
            } catch (Exception e) {
                LOG.error("Error registering cache for management for " + cache.getObjectName() + " . Error was "
                        + e.getMessage(), e);
            }
        }
    }

    /**
     * Called immediately after a cache has been disposed and removed. The calling method will
     * block until this method returns.
     * <p/>
     * Note that the CacheManager calls this method from a synchronized method. Any attempt to
     * call a synchronized method on CacheManager from this method will cause a deadlock.
     * <p/>
     * Note that a {@link net.sf.ehcache.event.CacheEventListener} status changed will also be triggered. Any
     * attempt from that notification to access CacheManager will also result in a deadlock.
     *
     * @param cacheName the name of the <code>Cache</code> the operation relates to
     */
    public void notifyCacheRemoved(String cacheName) {

        ObjectName objectName = null;
        try {
            if (registerCaches) {
                objectName = Cache.createObjectName(backingCacheManager.toString(), cacheName);
                mBeanServer.unregisterMBean(objectName);
            }
            if (registerCacheConfigurations) {
                objectName = CacheConfiguration.createObjectName(backingCacheManager.toString(), cacheName);
                mBeanServer.unregisterMBean(objectName);
            }
            if (registerCacheStatistics) {
                objectName = CacheStatistics.createObjectName(backingCacheManager.toString(), cacheName);
                mBeanServer.unregisterMBean(objectName);
            }
        } catch (Exception e) {
            LOG.error(
                    "Error unregistering cache for management for " + objectName + " . Error was " + e.getMessage(),
                    e);
        }

    }

}