/* * JBoss, Home of Professional Open Source. * Copyright 2000 - 2008, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.cache.jmx; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.Cache; import org.jboss.cache.CacheException; import org.jboss.cache.CacheFactory; import org.jboss.cache.CacheSPI; import org.jboss.cache.CacheStatus; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.config.BuddyReplicationConfig; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.ConfigurationException; import org.jboss.cache.config.EvictionConfig; import org.jboss.cache.config.LegacyConfigurationException; import org.jboss.cache.config.RuntimeConfig; import org.jboss.cache.config.parsing.JGroupsStackParser; import org.jboss.cache.config.parsing.XmlConfigurationParser2x; import org.jboss.cache.config.parsing.element.BuddyElementParser; import org.jboss.cache.config.parsing.element.EvictionElementParser; import org.jboss.cache.config.parsing.element.LoadersElementParser; import org.jboss.cache.util.CachePrinter; import org.jgroups.Address; import org.jgroups.Channel; import org.jgroups.ChannelFactory; import org.jgroups.jmx.JChannelFactoryMBean; import org.w3c.dom.Element; import javax.management.*; import javax.transaction.TransactionManager; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; /** * Wrapper class that exposes a {@link CacheJmxWrapperMBean JMX management interface} * * @author Brian Stansberry * @author Galder Zamarreno * @version $Revision$ * @deprecated use {@link org.jboss.cache.jmx.JmxRegistrationManager}. This class will not be supported from 3.0 on. */ @Deprecated public class CacheJmxWrapper extends NotificationBroadcasterSupport implements CacheJmxWrapperMBean, MBeanRegistration, CacheNotificationBroadcaster { private Log log = LogFactory.getLog(getClass().getName()); private MBeanServer server; private String cacheObjectName; private boolean jmxResourceRegistered; private CacheSPI cache; private Configuration config; private boolean registerJmxResource = true; private final AtomicInteger listenerCount = new AtomicInteger(0); private final AtomicLong sequence = new AtomicLong(0); private final CacheNotificationListener cacheNotificationListener; private CacheStatus cacheStatus; private String notificationServiceName; private boolean registered; private boolean disableStateChangeNotifications; // Legacy config support private Element buddyReplConfig; private Element evictionConfig; private Element cacheLoaderConfig; private Element clusterConfig; private JChannelFactoryMBean multiplexerService; private BuddyElementParser buddyElementParser = new BuddyElementParser(); private LoadersElementParser loadersElementParser = new LoadersElementParser(); private EvictionElementParser evictionElementParser = new EvictionElementParser(); private JGroupsStackParser stackParser = new JGroupsStackParser(); // ----------------------------------------------------------- Constructors public CacheJmxWrapper() { cacheNotificationListener = new CacheNotificationListener(this); cacheStatus = CacheStatus.INSTANTIATED; } public CacheJmxWrapper(Cache cache) { this(); setCache(cache); } // --------------------------------------------------- CacheJmxWrapperMBean public Cache getCache() { return cache; } public Configuration getConfiguration() { Configuration cfg = (cache == null ? config : cache.getConfiguration()); if (cfg == null) { cfg = config = new Configuration(); } return cfg; } public String printConfigurationAsString() { Configuration cfg = getConfiguration(); return cfg == null ? "Configuration is null" : cfg.toString(); } public String printConfigurationAsHtmlString() { Configuration cfg = getConfiguration(); return cfg == null ? "Configuration is null" : CachePrinter.formatHtml(cfg.toString()); } public String printCacheDetails() { return cache == null ? "Cache is null" : CachePrinter.printCacheDetails(cache); } public String printCacheDetailsAsHtml() { return cache == null ? "Cache is null" : CachePrinter.formatHtml(CachePrinter.printCacheDetails(cache)); } public CacheStatus getCacheStatus() { return cacheStatus; } public int getState() { switch (cacheStatus) { case INSTANTIATED: case CREATING: return registered ? REGISTERED : UNREGISTERED; case CREATED: return CREATED; case STARTING: return STARTING; case STARTED: return STARTED; case STOPPING: return STOPPING; case STOPPED: case DESTROYING: return STOPPED; case DESTROYED: return registered ? DESTROYED : UNREGISTERED; case FAILED: default: return FAILED; } } public Address getLocalAddress() { return cache == null ? null : cache.getLocalAddress(); } public List
getMembers() { return cache == null ? null : cache.getMembers(); } public int getNumberOfNodes() { return cache == null ? -1 : cache.getNumberOfNodes(); } public int getNumberOfAttributes() { return cache == null ? -1 : cache.getNumberOfAttributes(); } public String printLockInfo() { return cache == null ? "Cache is null" : CachePrinter.printCacheLockingInfo(cache); } public String printLockInfoAsHtml() { return cache == null ? "Cache is null" : CachePrinter.formatHtml(CachePrinter.printCacheLockingInfo(cache)); } public boolean getRegisterJmxResource() { return registerJmxResource; } public void setRegisterJmxResource(boolean register) { this.registerJmxResource = register; } // ---------------------------------------------------- LegacyConfiguration public Element getBuddyReplicationConfig() { return buddyReplConfig; } public Element getCacheLoaderConfig() { return cacheLoaderConfig; } public Element getCacheLoaderConfiguration() { return getCacheLoaderConfig(); } public String getCacheMode() { return getConfiguration().getCacheModeString(); } public String getClusterName() { return getConfiguration().getClusterName(); } public String getClusterProperties() { return getConfiguration().getClusterConfig(); } public Element getClusterConfig() { return clusterConfig; } public Element getEvictionPolicyConfig() { return evictionConfig; } public boolean getExposeManagementStatistics() { return getConfiguration().getExposeManagementStatistics(); } public boolean getUseInterceptorMbeans() { return getExposeManagementStatistics(); } public boolean getFetchInMemoryState() { return getConfiguration().isFetchInMemoryState(); } public long getStateRetrievalTimeout() { return getConfiguration().getStateRetrievalTimeout(); } @Deprecated public void setInitialStateRetrievalTimeout(long timeout) { setStateRetrievalTimeout(timeout); } public String getIsolationLevel() { return getConfiguration().getIsolationLevelString(); } public long getLockAcquisitionTimeout() { return getConfiguration().getLockAcquisitionTimeout(); } public JChannelFactoryMBean getMultiplexerService() { return multiplexerService; } public String getMultiplexerStack() { return getConfiguration().getMultiplexerStack(); } public ChannelFactory getMuxChannelFactory() { return getConfiguration().getRuntimeConfig().getMuxChannelFactory(); } public String getNodeLockingScheme() { return getConfiguration().getNodeLockingSchemeString(); } public long getReplQueueInterval() { return getConfiguration().getReplQueueInterval(); } public int getReplQueueMaxElements() { return getConfiguration().getReplQueueMaxElements(); } public String getReplicationVersion() { return getConfiguration().getReplVersionString(); } public boolean getSyncCommitPhase() { return getConfiguration().isSyncCommitPhase(); } public long getSyncReplTimeout() { return getConfiguration().getSyncReplTimeout(); } public boolean getSyncRollbackPhase() { return getConfiguration().isSyncRollbackPhase(); } public TransactionManager getTransactionManager() { return getConfiguration().getRuntimeConfig().getTransactionManager(); } public String getTransactionManagerLookupClass() { return getConfiguration().getTransactionManagerLookupClass(); } @Deprecated @SuppressWarnings("deprecation") public boolean getUseRegionBasedMarshalling() { return getConfiguration().isUseRegionBasedMarshalling(); } public boolean isUseLazyDeserialization() { return getConfiguration().isUseLazyDeserialization(); } public boolean getUseReplQueue() { return getConfiguration().isUseReplQueue(); } public boolean isInactiveOnStartup() { return getConfiguration().isInactiveOnStartup(); } public void setBuddyReplicationConfig(Element config) { BuddyReplicationConfig brc = null; if (config != null) { try { brc = buddyElementParser.parseBuddyElement(config); } catch (LegacyConfigurationException lce) { brc = XmlConfigurationParser2x.parseBuddyReplicationConfig(config); } } getConfiguration().setBuddyReplicationConfig(brc); this.buddyReplConfig = config; } public void setCacheLoaderConfig(Element cacheLoaderConfig) { CacheLoaderConfig clc = null; if (cacheLoaderConfig != null) { try { clc = loadersElementParser.parseLoadersElement(cacheLoaderConfig); } catch (LegacyConfigurationException lce) { clc = XmlConfigurationParser2x.parseCacheLoaderConfig(cacheLoaderConfig); } } getConfiguration().setCacheLoaderConfig(clc); this.cacheLoaderConfig = cacheLoaderConfig; } public void setCacheLoaderConfiguration(Element config) { log.warn("MBean attribute 'CacheLoaderConfiguration' is deprecated; " + "use 'CacheLoaderConfig'"); setCacheLoaderConfig(config); } public void setCacheMode(String mode) throws Exception { getConfiguration().setCacheModeString(mode); } public void setClusterConfig(Element config) { String props = null; if (config != null) { stackParser.parseClusterConfigXml(config); } getConfiguration().setClusterConfig(props); this.clusterConfig = config; } @Deprecated public long getInitialStateRetrievalTimeout() { return getStateRetrievalTimeout(); } public void setClusterName(String name) { getConfiguration().setClusterName(name); } public void setClusterProperties(String cluster_props) { getConfiguration().setClusterConfig(cluster_props); } public void setEvictionPolicyConfig(Element config) { EvictionConfig ec = null; if (config != null) { try { ec = evictionElementParser.parseEvictionElement(config); } catch (LegacyConfigurationException ce) { ec = XmlConfigurationParser2x.parseEvictionConfig(config); } } getConfiguration().setEvictionConfig(ec); this.evictionConfig = config; } public void setExposeManagementStatistics(boolean expose) { getConfiguration().setExposeManagementStatistics(expose); } public void setUseInterceptorMbeans(boolean use) { log.warn("MBean attribute 'UseInterceptorMbeans' is deprecated; " + "use 'ExposeManagementStatistics'"); setExposeManagementStatistics(use); } public void setFetchInMemoryState(boolean flag) { getConfiguration().setFetchInMemoryState(flag); } public void setInactiveOnStartup(boolean inactiveOnStartup) { getConfiguration().setInactiveOnStartup(inactiveOnStartup); } public void setStateRetrievalTimeout(long timeout) { getConfiguration().setStateRetrievalTimeout(timeout); } public void setIsolationLevel(String level) { getConfiguration().setIsolationLevelString(level); } public void setLockAcquisitionTimeout(long timeout) { getConfiguration().setLockAcquisitionTimeout(timeout); } public void setMultiplexerService(JChannelFactoryMBean muxService) { this.multiplexerService = muxService; } public void setMultiplexerStack(String stackName) { getConfiguration().setMultiplexerStack(stackName); } public void setMuxChannelFactory(ChannelFactory factory) { getConfiguration().getRuntimeConfig().setMuxChannelFactory(factory); } public void setNodeLockingScheme(String nodeLockingScheme) { getConfiguration().setNodeLockingSchemeString(nodeLockingScheme); } public void setReplQueueInterval(long interval) { getConfiguration().setReplQueueInterval(interval); } public void setReplQueueMaxElements(int max_elements) { getConfiguration().setReplQueueMaxElements(max_elements); } public void setReplicationVersion(String version) { getConfiguration().setReplVersionString(version); } public void setSyncCommitPhase(boolean sync_commit_phase) { getConfiguration().setSyncCommitPhase(sync_commit_phase); } public void setSyncReplTimeout(long timeout) { getConfiguration().setSyncReplTimeout(timeout); } public void setSyncRollbackPhase(boolean sync_rollback_phase) { getConfiguration().setSyncRollbackPhase(sync_rollback_phase); } public void setTransactionManager(TransactionManager manager) { getConfiguration().getRuntimeConfig().setTransactionManager(manager); } public void setTransactionManagerLookupClass(String cl) throws Exception { getConfiguration().setTransactionManagerLookupClass(cl); } @Deprecated @SuppressWarnings("deprecation") public void setUseRegionBasedMarshalling(boolean isTrue) { getConfiguration().setUseRegionBasedMarshalling(isTrue); } public void setUseReplQueue(boolean flag) { getConfiguration().setUseReplQueue(flag); } // -------------------------------------------------------------- Lifecycle public void create() throws CacheException { if (!cacheStatus.createAllowed()) { if (cacheStatus.needToDestroyFailedCache()) { destroy(); } else { return; } } try { cacheStatus = CacheStatus.CREATING; if (cache == null) { if (config == null) { throw new ConfigurationException("Must call setConfiguration() or setCache() before call to create()"); } constructCache(); } cache.create(); cacheStatus = CacheStatus.CREATED; } catch (Throwable t) { handleLifecycleTransitionFailure(t); } } public void start() throws CacheException { if (!cacheStatus.startAllowed()) { if (cacheStatus.needToDestroyFailedCache()) { destroy(); // this will take us back to DESTROYED } if (cacheStatus.needCreateBeforeStart()) { create(); } else { return; } } try { int oldState = getState(); cacheStatus = CacheStatus.STARTING; int startingState = getState(); sendStateChangeNotification(oldState, startingState, getClass().getSimpleName() + " starting", null); cache.start(); registerJmxResources(); cacheStatus = CacheStatus.STARTED; sendStateChangeNotification(startingState, getState(), getClass().getSimpleName() + " started", null); } catch (Throwable t) { handleLifecycleTransitionFailure(t); } } public void stop() { if (!cacheStatus.stopAllowed()) { return; } // Trying to stop() from FAILED is valid, but may not work boolean failed = cacheStatus == CacheStatus.FAILED; try { int oldState = getState(); cacheStatus = CacheStatus.STOPPING; int stoppingState = getState(); sendStateChangeNotification(oldState, stoppingState, getClass().getSimpleName() + " stopping", null); cache.stop(); if (cache.getCacheStatus() == CacheStatus.DESTROYED) { // Cache was already destroyed externally; // so get rid of the jmx resources unregisterJmxResources(); } cacheStatus = CacheStatus.STOPPED; sendStateChangeNotification(stoppingState, getState(), getClass().getSimpleName() + " stopped", null); } catch (Throwable t) { if (failed) { log.warn("Attempted to stop() from FAILED state, " + "but caught exception; try calling destroy()", t); } handleLifecycleTransitionFailure(t); } } public void destroy() { if (!cacheStatus.destroyAllowed()) { if (cacheStatus.needStopBeforeDestroy()) { try { stop(); } catch (CacheException e) { log.warn("Needed to call stop() before destroying but stop() " + "threw exception. Proceeding to destroy", e); } } else { return; } } try { cacheStatus = CacheStatus.DESTROYING; // The cache is destroyed, so we shouldn't leave the ResourcesDMBean // in JMX, even if we didn't register them in create unregisterJmxResources(); if (cache != null) { cache.destroy(); } } finally { // We always proceed to DESTROYED cacheStatus = CacheStatus.DESTROYED; } } // ------------------------------------------------------ MBeanRegistration /** * Caches the provided server and objName. */ public ObjectName preRegister(MBeanServer server, ObjectName objName) throws Exception { this.server = server; if (cacheObjectName == null) { if (objName != null) { cacheObjectName = objName.getCanonicalName(); } else { getCacheObjectName(); } } // Inform our CacheNotificationListener of the ObjectName it should transmit if (notificationServiceName == null) notificationServiceName = cacheObjectName; cacheNotificationListener.setServiceName(notificationServiceName); return new ObjectName(cacheObjectName); } /** * Registers the cache's MBean resources, if {@link #getRegisterJmxResource()} * is true. */ public void postRegister(Boolean registrationDone) { if (Boolean.TRUE.equals(registrationDone)) { log.debug("Registered in JMX under " + cacheObjectName); if (cache != null && CacheStatus.STARTED.equals(cache.getCacheStatus())) { try { registerJmxResources(); } catch (Exception e) { log.error("Caught exception registering cache ResourcesDMBean with JMX", e); } } registered = true; } } /** * No-op. */ public void preDeregister() throws Exception { } /** * Unregisters the ResourcesDMBean, if {@link #getRegisterJmxResource()} is * true. */ public void postDeregister() { unregisterJmxResources(); server = null; registered = false; } // --------------------------------------------------------- Public methods /** * Sets the configuration that the underlying cache should use. * * @param config the configuration * @throws IllegalArgumentException if config is null. */ public void setConfiguration(Configuration config) { this.config = config; } /** * Allows direct injection of the underlying cache. * * @param cache */ public void setCache(Cache cache) { if (cacheStatus != CacheStatus.INSTANTIATED && cacheStatus != CacheStatus.CREATING && cacheStatus != CacheStatus.DESTROYED) { throw new IllegalStateException("Cannot set underlying cache after call to create()"); } this.cache = (CacheSPI) cache; this.config = (cache == null ? null : cache.getConfiguration()); synchronized (listenerCount) { if (listenerCount.get() > 0 && cache != null) { cache.addCacheListener(cacheNotificationListener); } } } public String getCacheObjectName() { if (cacheObjectName == null) { if (config.getClusterName() == null) { cacheObjectName = JmxRegistrationManager.LOCAL_CACHE_PREFIX + "Cache" + System.currentTimeMillis(); } else { cacheObjectName = JmxRegistrationManager.REPLICATED_CACHE_PREFIX + config.getClusterName(); } } return cacheObjectName; } public void setCacheObjectName(String name) throws MalformedObjectNameException { if (name != null) { // test the name new ObjectName(name); } this.cacheObjectName = name; } /** * Gets whether sending of JMX notifications for this mbean's * start/stop lifecycle changes is disabled. * * @see #setDisableStateChangeNotifications(boolean) */ public boolean isDisableStateChangeNotifications() { return disableStateChangeNotifications; } /** * Hook to allow PojoCacheJmxWrapper to suppress state change * notifications from this mbean in lieu of its own. * * @param disableStateChangeNotifications * */ public void setDisableStateChangeNotifications(boolean disableStateChangeNotifications) { this.disableStateChangeNotifications = disableStateChangeNotifications; } public MBeanServer getMBeanServer() { return server; } public String getNotificationServiceName() { return notificationServiceName; } public void setNotificationServiceName(String notificationServiceName) { this.notificationServiceName = notificationServiceName; this.cacheNotificationListener.setServiceName(notificationServiceName); } public long getNextNotificationSequenceNumber() { return sequence.getAndIncrement(); } // ---------------------------------------------------- Superclass Overrides @Override public void addNotificationListener(NotificationListener notificationListener, NotificationFilter notificationFilter, Object object) throws IllegalArgumentException { super.addNotificationListener(notificationListener, notificationFilter, object); notificationRegistration(true); } @Override public void removeNotificationListener(NotificationListener notificationListener) throws ListenerNotFoundException { super.removeNotificationListener(notificationListener); notificationRegistration(false); } @Override public void removeNotificationListener(NotificationListener listener, NotificationFilter filter, Object handback) throws ListenerNotFoundException { super.removeNotificationListener(listener, filter, handback); notificationRegistration(false); } @Override public MBeanNotificationInfo[] getNotificationInfo() { return CacheNotificationListener.getNotificationInfo(); } // ------------------------------------------------------ Protected Methods protected void constructCache() throws ConfigurationException { log.debug("Constructing Cache"); CacheFactory cf = new DefaultCacheFactory(); setCache(cf.createCache(config, false)); if (multiplexerService != null) { injectMuxChannel(); } } protected boolean registerJmxResources() throws CacheException { if (registerJmxResource && config.getExposeManagementStatistics() && !jmxResourceRegistered && server != null) { log.debug("Registering jmx resources"); JmxRegistrationManager registrationManager = new JmxRegistrationManager(server, cache, this.cacheObjectName); registrationManager.registerAllMBeans(); jmxResourceRegistered = true; return true; } return false; } protected void unregisterJmxResources() { if (registerJmxResource && jmxResourceRegistered && server != null) { log.debug("Unregistering interceptors"); JmxRegistrationManager registrationManager = new JmxRegistrationManager(server, cache, this.cacheObjectName); registrationManager.unregisterAllMBeans(); jmxResourceRegistered = false; } } // -------------------------------------------------------- Private methods private void injectMuxChannel() throws CacheException { Configuration cfg = getConfiguration(); RuntimeConfig rtcfg = cfg.getRuntimeConfig(); // Only inject if there isn't already a channel or factory if (rtcfg.getMuxChannelFactory() == null && rtcfg.getChannel() == null) { Channel ch; try { ch = multiplexerService.createMultiplexerChannel(cfg.getMultiplexerStack(), cfg.getClusterName()); } catch (Exception e) { throw new CacheException("Exception creating multiplexed channel", e); } rtcfg.setChannel(ch); } } /** * Adds and removes the CacheListener. * A counter is used to determine whether we have any clients who are * registered for notifications from this mbean. When the count is zero, * we don't need to listen to cache events, so we remove the CacheListener. * Note that a client who terminates without unregistering for notifications * will leave the count greater than zero so we'll still listen in that case. * * @param add true if the event was a listerner addition, * false if it was a removal */ private void notificationRegistration(boolean add) { // This method adds and removes the CacheImpl listener. // The m_listeners counter is used to determine whether // we have any clients who are registered for notifications // from this mbean. When the count is zero, we don't need to // listen to cache events. Note that a client who terminates // without unregistering for notifications will leave the count // greater than zero so we'll still listen in that case. synchronized (listenerCount) { if (add) { listenerCount.incrementAndGet(); if (cache != null) { cache.addCacheListener(cacheNotificationListener); } } else { if (listenerCount.decrementAndGet() <= 0) { if (cache != null) { cache.removeCacheListener(cacheNotificationListener); } listenerCount.set(0); } } } } /** * Sets the cacheStatus to FAILED and rethrows the problem as one * of the declared types. Converts any non-RuntimeException Exception * to CacheException. * * @param t * @throws CacheException * @throws RuntimeException * @throws Error */ private void handleLifecycleTransitionFailure(Throwable t) throws RuntimeException, Error { int oldState = getState(); cacheStatus = CacheStatus.FAILED; sendStateChangeNotification(oldState, getState(), getClass().getSimpleName() + " failed", t); if (t instanceof CacheException) { throw (CacheException) t; } else if (t instanceof RuntimeException) { throw (RuntimeException) t; } else if (t instanceof Error) { throw (Error) t; } else { throw new CacheException(t); } } /** * Helper for sending out state change notifications */ private void sendStateChangeNotification(int oldState, int newState, String msg, Throwable t) { if (isDisableStateChangeNotifications()) { return; } long now = System.currentTimeMillis(); AttributeChangeNotification stateChangeNotification = new AttributeChangeNotification( this, getNextNotificationSequenceNumber(), now, msg, "State", "java.lang.Integer", oldState, newState); stateChangeNotification.setUserData(t); sendNotification(stateChangeNotification); } }