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
321 registeredObjectNames = mBeanServer.queryNames(CacheManager.createObjectName(backingCacheManager), null);
322
323 registeredObjectNames.addAll(mBeanServer.queryNames(new ObjectName("net.sf.ehcache:*,CacheManager="
324 + EhcacheHibernateMbeanNames.mbeanSafe(backingCacheManager.toString())), null));
325 } catch (MalformedObjectNameException e) {
326
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 }