View Javadoc

1   /***
2    *  Copyright 2003-2010 Terracotta, Inc.
3    *
4    *  Licensed under the Apache License, Version 2.0 (the "License");
5    *  you may not use this file except in compliance with the License.
6    *  You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *  Unless required by applicable law or agreed to in writing, software
11   *  distributed under the License is distributed on an "AS IS" BASIS,
12   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *  See the License for the specific language governing permissions and
14   *  limitations under the License.
15   */
16  
17  package net.sf.ehcache.management;
18  
19  import net.sf.ehcache.CacheException;
20  import net.sf.ehcache.Status;
21  import net.sf.ehcache.distribution.CacheManagerPeerProvider;
22  import net.sf.ehcache.event.CacheManagerEventListener;
23  import net.sf.ehcache.hibernate.management.impl.EhcacheHibernateMbeanNames;
24  
25  import javax.management.InstanceAlreadyExistsException;
26  import javax.management.MBeanRegistrationException;
27  import javax.management.MBeanServer;
28  import javax.management.MalformedObjectNameException;
29  import javax.management.NotCompliantMBeanException;
30  import javax.management.ObjectName;
31  import java.util.Iterator;
32  import java.util.List;
33  import java.util.Map;
34  import java.util.Set;
35  
36  import org.slf4j.Logger;
37  import org.slf4j.LoggerFactory;
38  
39  /***
40   * Ehcache CacheManagers and Caches have lifecycles. Often normal use of a CacheManager
41   * will be to shut it down and create a new one from within a running JVM. For example,
42   * in Java EE environments, applications are often undeployed and then redeployed. A
43   * servlet listener available in the web module, <code>net.sf.ehcache.constructs.web.ShutdownListener</code>}
44   * enables this to be detected and the CacheManager shutdown.
45   * <p/>
46   * When a CacheManager is shut down we need to ensure there is no memory, resource or
47   * thread leakage. An MBeanServer, particularly a platform MBeanServer, can be expected
48   * to exist for the lifespan of the JVM. Accordingly, we need to deregister them when
49   * needed without creating a leakage.
50   * <p/>
51   * The second purpose of this class (and this package) is to keep management concerns away
52   * from the core ehcache packages. That way, JMX is not a required dependency, but rather
53   * an optional one.
54   * <p/>
55   * This class is constructable as of 1.5 to support injection via IoC containers.
56   *
57   * @author Greg Luck
58   * @version $Id: ManagementService.html 13146 2011-08-01 17:12:39Z oletizi $
59   * @since 1.3
60   */
61  public class ManagementService implements CacheManagerEventListener {
62  
63      private static final Logger LOG = LoggerFactory.getLogger(ManagementService.class.getName());
64  
65      private MBeanServer mBeanServer;
66      private net.sf.ehcache.CacheManager backingCacheManager;
67      private boolean registerCacheManager;
68      private boolean registerCaches;
69      private boolean registerCacheConfigurations;
70      private boolean registerCacheStatistics;
71      private boolean registerCacheStores;
72      private Status status;
73  
74  
75  
76  
77      /***
78       * A constructor for a management service for a range of possible MBeans.
79       * <p/>
80       * The {@link #init()} method needs to be called after construction which causes
81       * the selected monitoring options to be be registered
82       * with the provided MBeanServer for caches in the given CacheManager.
83       * <p/>
84       * While registering the CacheManager enables traversal to all of the other
85       * items,
86       * this requires programmatic traversal. The other options allow entry points closer
87       * to an item of interest and are more accessible from JMX management tools like JConsole.
88       * Moreover CacheManager and Cache are not serializable, so remote monitoring is not possible
89       * for CacheManager or Cache, while CacheStatistics and CacheConfiguration are. Finally
90       * CacheManager and Cache enable management operations to be performed.
91       * <p/>
92       * Once monitoring is enabled caches will automatically added and removed from the MBeanServer
93       * as they are added and disposed of from the CacheManager. When the CacheManager itself
94       * shutsdown all registered MBeans will be unregistered.
95       *
96       * @param cacheManager                the CacheManager to listen to
97       * @param mBeanServer                 the MBeanServer to register MBeans to
98       * @param registerCacheManager        Whether to register the CacheManager MBean
99       * @param registerCaches              Whether to register the Cache MBeans
100      * @param registerCacheConfigurations Whether to register the CacheConfiguration MBeans
101      * @param registerCacheStatistics     Whether to register the CacheStatistics MBeans
102      * @throws net.sf.ehcache.CacheException if something goes wrong with init()
103      * @since 2.2
104      */
105     public ManagementService(net.sf.ehcache.CacheManager cacheManager,
106                              MBeanServer mBeanServer,
107                              boolean registerCacheManager,
108                              boolean registerCaches,
109                              boolean registerCacheConfigurations,
110                              boolean registerCacheStatistics,
111                              boolean registerCacheStores) throws CacheException {
112 
113         status = Status.STATUS_UNINITIALISED;
114         backingCacheManager = cacheManager;
115         this.mBeanServer = mBeanServer;
116         this.registerCacheManager = registerCacheManager;
117         this.registerCaches = registerCaches;
118         this.registerCacheConfigurations = registerCacheConfigurations;
119         this.registerCacheStatistics = registerCacheStatistics;
120         this.registerCacheStores = registerCacheStores;
121     }
122 
123     /***
124          * A constructor for a management service for a range of possible MBeans.
125          * <p/>
126          * The {@link #init()} method needs to be called after construction which causes
127          * the selected monitoring options to be be registered
128          * with the provided MBeanServer for caches in the given CacheManager.
129          * <p/>
130          * While registering the CacheManager enables traversal to all of the other
131          * items,
132          * this requires programmatic traversal. The other options allow entry points closer
133          * to an item of interest and are more accessible from JMX management tools like JConsole.
134          * Moreover CacheManager and Cache are not serializable, so remote monitoring is not possible
135          * for CacheManager or Cache, while CacheStatistics and CacheConfiguration are. Finally
136          * CacheManager and Cache enable management operations to be performed.
137          * <p/>
138          * Once monitoring is enabled caches will automatically added and removed from the MBeanServer
139          * as they are added and disposed of from the CacheManager. When the CacheManager itself
140          * shutsdown all registered MBeans will be unregistered.
141          *
142          * @param cacheManager                the CacheManager to listen to
143          * @param mBeanServer                 the MBeanServer to register MBeans to
144          * @param registerCacheManager        Whether to register the CacheManager MBean
145          * @param registerCaches              Whether to register the Cache MBeans
146          * @param registerCacheConfigurations Whether to register the CacheConfiguration MBeans
147          * @param registerCacheStatistics     Whether to register the CacheStatistics MBeans
148          * @throws net.sf.ehcache.CacheException if something goes wrong with init()
149          */
150         public ManagementService(net.sf.ehcache.CacheManager cacheManager,
151                                  MBeanServer mBeanServer,
152                                  boolean registerCacheManager,
153                                  boolean registerCaches,
154                                  boolean registerCacheConfigurations,
155                                  boolean registerCacheStatistics) throws CacheException {
156 
157             this(cacheManager, mBeanServer, registerCacheManager, registerCaches,
158                     registerCacheConfigurations, registerCacheStatistics, false);
159         }
160 
161 
162     /***
163      * A convenience static method which creates a ManagementService and initialises it with the
164      * supplied parameters.
165      *
166      * @param cacheManager                the CacheManager to listen to
167      * @param mBeanServer                 the MBeanServer to register MBeans to
168      * @param registerCacheManager        Whether to register the CacheManager MBean
169      * @param registerCaches              Whether to register the Cache MBeans
170      * @param registerCacheConfigurations Whether to register the CacheConfiguration MBeans
171      * @param registerCacheStatistics     Whether to register the CacheStatistics MBeans
172      * @see ManagementService#ManagementService(net.sf.ehcache.CacheManager, javax.management.MBeanServer, boolean, boolean, boolean, boolean, boolean)
173      * @since 2.2
174      */
175     public static void registerMBeans(
176             net.sf.ehcache.CacheManager cacheManager,
177             MBeanServer mBeanServer,
178             boolean registerCacheManager,
179             boolean registerCaches,
180             boolean registerCacheConfigurations,
181             boolean registerCacheStatistics,
182             boolean registerCacheStores) throws CacheException {
183 
184         ManagementService registry = new ManagementService(cacheManager,
185                 mBeanServer,
186                 registerCacheManager,
187                 registerCaches,
188                 registerCacheConfigurations,
189                 registerCacheStatistics,
190                 registerCacheStores);
191 
192         registry.init();
193     }
194 
195     /***
196      * A convenience static method which creates a ManagementService and initialises it with the
197      * supplied parameters.
198      * <p/>
199      * This one is provided for backward compatibility
200      *
201      * @param cacheManager                the CacheManager to listen to
202      * @param mBeanServer                 the MBeanServer to register MBeans to
203      * @param registerCacheManager        Whether to register the CacheManager MBean
204      * @param registerCaches              Whether to register the Cache MBeans
205      * @param registerCacheConfigurations Whether to register the CacheConfiguration MBeans
206      * @param registerCacheStatistics     Whether to register the CacheStatistics MBeans
207      * @see ManagementService#ManagementService(net.sf.ehcache.CacheManager, javax.management.MBeanServer, boolean, boolean, boolean, boolean)
208      */
209     public static void registerMBeans(
210             net.sf.ehcache.CacheManager cacheManager,
211             MBeanServer mBeanServer,
212             boolean registerCacheManager,
213             boolean registerCaches,
214             boolean registerCacheConfigurations,
215             boolean registerCacheStatistics) throws CacheException {
216 
217         registerMBeans(cacheManager, mBeanServer, registerCacheManager, registerCaches, registerCacheConfigurations,
218                 registerCacheStatistics, false);
219     }
220 
221 
222     /***
223      * Call to register the mbeans in the mbean server and start the event listeners and do any other required initialisation.
224      * Once intialised, it registers itself as a CacheManageEvenListener with the backing CacheManager, so
225      * that it can participate in lifecycle and other events.
226      *
227      * @throws net.sf.ehcache.CacheException - all exceptions are wrapped in CacheException
228      */
229     public void init() throws CacheException {
230         CacheManager cacheManager = new CacheManager(backingCacheManager);
231         try {
232             registerCacheManager(cacheManager);
233 
234             registerPeerProviders();
235 
236             List caches = cacheManager.getCaches();
237             for (int i = 0; i < caches.size(); i++) {
238                 Cache cache = (Cache) caches.get(i);
239                 registerCachesIfRequired(cache);
240                 registerCacheStatisticsIfRequired(cache);
241                 registerCacheConfigurationIfRequired(cache);
242                 registerCacheStoreIfRequired(cache);
243             }
244         } catch (Exception e) {
245             throw new CacheException(e);
246         }
247         status = Status.STATUS_ALIVE;
248         backingCacheManager.getCacheManagerEventListenerRegistry().registerListener(this);
249     }
250 
251 
252     private void registerPeerProviders() {
253         final Map<String, CacheManagerPeerProvider> cacheManagerPeerProviders = this.backingCacheManager.getCacheManagerPeerProviders();
254         for (final CacheManagerPeerProvider cacheManagerPeerProvider : cacheManagerPeerProviders.values()) {
255             if (cacheManagerPeerProvider instanceof ManagedCacheManagerPeerProvider) {
256                 ((ManagedCacheManagerPeerProvider) cacheManagerPeerProvider).register(this.mBeanServer);
257             }
258         }
259     }
260 
261     private void registerCacheManager(CacheManager cacheManager) throws InstanceAlreadyExistsException,
262             MBeanRegistrationException, NotCompliantMBeanException {
263         if (registerCacheManager) {
264             mBeanServer.registerMBean(cacheManager, cacheManager.getObjectName());
265         }
266     }
267 
268     private void registerCacheConfigurationIfRequired(Cache cache) throws InstanceAlreadyExistsException,
269             MBeanRegistrationException, NotCompliantMBeanException {
270         if (registerCacheConfigurations) {
271             CacheConfiguration cacheConfiguration = cache.getCacheConfiguration();
272             mBeanServer.registerMBean(cacheConfiguration, cacheConfiguration.getObjectName());
273         }
274     }
275 
276     private void registerCacheStatisticsIfRequired(Cache cache) throws InstanceAlreadyExistsException,
277             MBeanRegistrationException, NotCompliantMBeanException {
278         if (registerCacheStatistics) {
279             CacheStatistics cacheStatistics = cache.getStatistics();
280             mBeanServer.registerMBean(cacheStatistics, cacheStatistics.getObjectName());
281         }
282     }
283 
284     private void registerCachesIfRequired(Cache cache) throws InstanceAlreadyExistsException,
285             MBeanRegistrationException, NotCompliantMBeanException {
286         if (registerCaches) {
287             mBeanServer.registerMBean(cache, cache.getObjectName());
288         }
289     }
290 
291     private void registerCacheStoreIfRequired(Cache cache) throws InstanceAlreadyExistsException,
292             MBeanRegistrationException, NotCompliantMBeanException {
293         if (registerCacheStores) {
294             Store cacheStore = cache.getStore();
295             if (cacheStore != null) {
296                 mBeanServer.registerMBean(cacheStore, cacheStore.getObjectName());
297             }
298         }
299     }
300 
301     /***
302      * Returns the listener status.
303      *
304      * @return the status at the point in time the method is called
305      */
306     public Status getStatus() {
307         return status;
308     }
309 
310     /***
311      * Stop the listener and free any resources.
312      * Removes registered ObjectNames
313      *
314      * @throws net.sf.ehcache.CacheException - all exceptions are wrapped in CacheException
315      */
316     public void dispose() throws CacheException {
317         Set registeredObjectNames = null;
318 
319         try {
320             // CacheManager MBean
321             registeredObjectNames = mBeanServer.queryNames(CacheManager.createObjectName(backingCacheManager), null);
322             // Other MBeans for this CacheManager
323             registeredObjectNames.addAll(mBeanServer.queryNames(new ObjectName("net.sf.ehcache:*,CacheManager="
324                     + EhcacheHibernateMbeanNames.mbeanSafe(backingCacheManager.toString())), null));
325         } catch (MalformedObjectNameException e) {
326             // this should not happen
327             LOG.error("Error querying MBeanServer. Error was " + e.getMessage(), e);
328         }
329         for (Iterator iterator = registeredObjectNames.iterator(); iterator.hasNext();) {
330             ObjectName objectName = (ObjectName) iterator.next();
331             try {
332                 mBeanServer.unregisterMBean(objectName);
333             } catch (Exception e) {
334                 LOG.error("Error unregistering object instance " + objectName + " . Error was " + e.getMessage(), e);
335             }
336         }
337         status = Status.STATUS_SHUTDOWN;
338     }
339 
340     /***
341      * Called immediately after a cache has been added and activated.
342      * <p/>
343      * Note that the CacheManager calls this method from a synchronized method. Any attempt to
344      * call a synchronized method on CacheManager from this method will cause a deadlock.
345      * <p/>
346      * Note that activation will also cause a CacheEventListener status change notification
347      * from {@link net.sf.ehcache.Status#STATUS_UNINITIALISED} to
348      * {@link net.sf.ehcache.Status#STATUS_ALIVE}. Care should be taken on processing that
349      * notification because:
350      * <ul>
351      * <li>the cache will not yet be accessible from the CacheManager.
352      * <li>the addCaches methods which cause this notification are synchronized on the
353      * CacheManager. An attempt to call {@link net.sf.ehcache.CacheManager#getEhcache(String)}
354      * will cause a deadlock.
355      * </ul>
356      * The calling method will block until this method returns.
357      * <p/>
358      *
359      * @param cacheName the name of the <code>Cache</code> the operation relates to
360      * @see net.sf.ehcache.event.CacheEventListener
361      */
362     public void notifyCacheAdded(String cacheName) {
363         if (registerCaches || registerCacheStatistics || registerCacheConfigurations) {
364             Cache cache = new Cache(backingCacheManager.getEhcache(cacheName));
365             try {
366                 registerCachesIfRequired(cache);
367                 registerCacheStatisticsIfRequired(cache);
368                 registerCacheConfigurationIfRequired(cache);
369                 registerCacheStoreIfRequired(cache);
370             } catch (Exception e) {
371                 LOG.error("Error registering cache for management for " + cache.getObjectName()
372                         + " . Error was " + e.getMessage(), e);
373             }
374         }
375     }
376 
377     /***
378      * Called immediately after a cache has been disposed and removed. The calling method will
379      * block until this method returns.
380      * <p/>
381      * Note that the CacheManager calls this method from a synchronized method. Any attempt to
382      * call a synchronized method on CacheManager from this method will cause a deadlock.
383      * <p/>
384      * Note that a {@link net.sf.ehcache.event.CacheEventListener} status changed will also be triggered. Any
385      * attempt from that notification to access CacheManager will also result in a deadlock.
386      *
387      * @param cacheName the name of the <code>Cache</code> the operation relates to
388      */
389     public void notifyCacheRemoved(String cacheName) {
390 
391         ObjectName objectName = null;
392         try {
393             if (registerCaches) {
394                 objectName = Cache.createObjectName(backingCacheManager.toString(), cacheName);
395                 mBeanServer.unregisterMBean(objectName);
396             }
397             if (registerCacheConfigurations) {
398                 objectName = CacheConfiguration.createObjectName(backingCacheManager.toString(), cacheName);
399                 mBeanServer.unregisterMBean(objectName);
400             }
401             if (registerCacheStatistics) {
402                 objectName = CacheStatistics.createObjectName(backingCacheManager.toString(), cacheName);
403                 mBeanServer.unregisterMBean(objectName);
404             }
405             if (registerCacheStores) {
406                 objectName = Store.createObjectName(backingCacheManager.toString(), cacheName);
407                 if (mBeanServer.isRegistered(objectName)) {
408                     mBeanServer.unregisterMBean(objectName);
409                 }
410             }
411         } catch (Exception e) {
412             LOG.error("Error unregistering cache for management for " + objectName
413                     + " . Error was " + e.getMessage(), e);
414         }
415 
416     }
417 
418 }