1 /**
2 * Copyright 2003-2007 Greg Luck
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.event.CacheManagerEventListener;
22 import org.apache.commons.logging.Log;
23 import org.apache.commons.logging.LogFactory;
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.Set;
34
35 /**
36 * Ehcache CacheManagers and Caches have lifecycles. Often normal use of a CacheManager will be
37 * to shut it down and create a new one from within a running JVM. For example, in JEE environments,
38 * applications are often undeployed and then redeployed. A servlet listener, {@link net.sf.ehcache.constructs.web.ShutdownListener}
39 * enables this to be detected and the CacheManager shutdown.
40 * <p/>
41 * When a CacheManager is shut down we need to ensure there is no memory, resource or thread leakage.
42 * An MBeanServer, particularly a platform MBeanServer, can be expected to exist for the lifespan of the JVM.
43 * Accordingly, we need to deregister them when needed without creating a leakage.
44 * <p/>
45 * The second purpose of this class (and this package) is to keep management concerns away from the core ehcache packages.
46 * That way, JMX is not a required dependency, but rather an optional one.
47 *
48 * @author Greg Luck
49 * @version $Id: ManagementService.java 512 2007-07-10 09:18:45Z gregluck $
50 * @since 1.3
51 */
52 public final class ManagementService implements CacheManagerEventListener {
53
54 private static final Log LOG = LogFactory.getLog(ManagementService.class.getName());
55
56 private MBeanServer mBeanServer;
57 private net.sf.ehcache.CacheManager backingCacheManager;
58 private boolean registerCacheManager;
59 private boolean registerCaches;
60 private boolean registerCacheConfigurations;
61 private boolean registerCacheStatistics;
62 private Status status;
63
64 /**
65 * Require use of the factory method
66 */
67 private ManagementService() {
68
69 }
70
71 /**
72 * This method causes the selected monitoring options to be be registered
73 * with the provided MBeanServer for caches in the given CacheManager.
74 * <p/>
75 * While registering the CacheManager enables traversal to all of the other items,
76 * this requires programmatic traversal. The other options allow entry points closer
77 * to an item of interest and are more accessible from JMX management tools like JConsole.
78 * Moreover CacheManager and Cache are not serializable, so remote monitoring is not possible for CacheManager
79 * or Cache, while CacheStatistics and CacheConfiguration are. Finally CacheManager and Cache enable
80 * management operations to be performed.
81 * <p/>
82 * Once monitoring is enabled caches will automatically added and removed from the MBeanServer
83 * as they are added and disposed of from the CacheManager. When the CacheManager itself shutsdown
84 * all registered MBeans will be unregistered.
85 *
86 * @param cacheManager the CacheManager to listen to
87 * @param mBeanServer the MBeanServer to register MBeans to
88 * @param registerCacheManager Whether to register the CacheManager MBean
89 * @param registerCaches Whether to register the Cache MBeans
90 * @param registerCacheConfigurations Whether to register the CacheConfiguration MBeans
91 * @param registerCacheStatistics Whether to register the CacheStatistics MBeans
92 */
93 public static void registerMBeans(
94 net.sf.ehcache.CacheManager cacheManager,
95 MBeanServer mBeanServer,
96 boolean registerCacheManager,
97 boolean registerCaches,
98 boolean registerCacheConfigurations,
99 boolean registerCacheStatistics) throws CacheException {
100
101 ManagementService registry = new ManagementService();
102 registry.status = Status.STATUS_UNINITIALISED;
103 registry.backingCacheManager = cacheManager;
104 registry.mBeanServer = mBeanServer;
105 registry.registerCacheManager = registerCacheManager;
106 registry.registerCaches = registerCaches;
107 registry.registerCacheConfigurations = registerCacheConfigurations;
108 registry.registerCacheStatistics = registerCacheStatistics;
109
110 registry.init();
111 }
112
113
114 /**
115 * Call to start the listeners and do any other required initialisation.
116 * Once intialised, it registers itself as a CacheManageEvenListener with the backing CacheManager, so
117 * that it can participate in lifecycle and other events.
118 *
119 * @throws net.sf.ehcache.CacheException - all exceptions are wrapped in CacheException
120 */
121 public void init() throws CacheException {
122 CacheManager cacheManager = new CacheManager(backingCacheManager);
123 try {
124 registerCacheManager(cacheManager);
125
126 List caches = cacheManager.getCaches();
127 for (int i = 0; i < caches.size(); i++) {
128 Cache cache = (Cache) caches.get(i);
129 registerCachesIfRequired(cache);
130 registerCacheStatisticsIfRequired(cache);
131 registerCacheConfigurationIfRequired(cache);
132 }
133 } catch (Exception e) {
134 throw new CacheException(e);
135 }
136 status = Status.STATUS_ALIVE;
137 backingCacheManager.getCacheManagerEventListenerRegistry().registerListener(this);
138 }
139
140 private void registerCacheManager(CacheManager cacheManager) throws InstanceAlreadyExistsException,
141 MBeanRegistrationException, NotCompliantMBeanException {
142 if (registerCacheManager) {
143 mBeanServer.registerMBean(cacheManager, cacheManager.getObjectName());
144 }
145 }
146
147 private void registerCacheConfigurationIfRequired(Cache cache) throws InstanceAlreadyExistsException,
148 MBeanRegistrationException, NotCompliantMBeanException {
149 if (registerCacheConfigurations) {
150 CacheConfiguration cacheConfiguration = cache.getCacheConfiguration();
151 mBeanServer.registerMBean(cacheConfiguration, cacheConfiguration.getObjectName());
152 }
153 }
154
155 private void registerCacheStatisticsIfRequired(Cache cache) throws InstanceAlreadyExistsException,
156 MBeanRegistrationException, NotCompliantMBeanException {
157 if (registerCacheStatistics) {
158 CacheStatistics cacheStatistics = cache.getStatistics();
159 mBeanServer.registerMBean(cacheStatistics, cacheStatistics.getObjectName());
160 }
161 }
162
163 private void registerCachesIfRequired(Cache cache) throws InstanceAlreadyExistsException,
164 MBeanRegistrationException, NotCompliantMBeanException {
165 if (registerCaches) {
166 mBeanServer.registerMBean(cache, cache.getObjectName());
167 }
168 }
169
170 /**
171 * Returns the listener status.
172 *
173 * @return the status at the point in time the method is called
174 */
175 public Status getStatus() {
176 return status;
177 }
178
179 /**
180 * Stop the listener and free any resources.
181 * Removes registered ObjectNames
182 *
183 * @throws net.sf.ehcache.CacheException - all exceptions are wrapped in CacheException
184 */
185 public void dispose() throws CacheException {
186 Set registeredObjectNames = null;
187
188 try {
189 //CacheManager MBean
190 registeredObjectNames = mBeanServer.queryNames(CacheManager.createObjectName(backingCacheManager), null);
191 //Other MBeans for this CacheManager
192 registeredObjectNames.addAll(mBeanServer.queryNames(new ObjectName("net.sf.ehcache:*,CacheManager="
193 + backingCacheManager.toString()), null));
194 } catch (MalformedObjectNameException e) {
195 //this should not happen
196 LOG.error("Error querying MBeanServer. Error was " + e.getMessage(), e);
197 }
198 for (Iterator iterator = registeredObjectNames.iterator(); iterator.hasNext();) {
199 ObjectName objectName = (ObjectName) iterator.next();
200 try {
201 mBeanServer.unregisterMBean(objectName);
202 } catch (Exception e) {
203 LOG.error("Error unregistering object instance " + objectName
204 + " . Error was " + e.getMessage(), e);
205 }
206 }
207 status = Status.STATUS_SHUTDOWN;
208 }
209
210 /**
211 * Called immediately after a cache has been added and activated.
212 * <p/>
213 * Note that the CacheManager calls this method from a synchronized method. Any attempt to
214 * call a synchronized method on CacheManager from this method will cause a deadlock.
215 * <p/>
216 * Note that activation will also cause a CacheEventListener status change notification
217 * from {@link net.sf.ehcache.Status#STATUS_UNINITIALISED} to
218 * {@link net.sf.ehcache.Status#STATUS_ALIVE}. Care should be taken on processing that
219 * notification because:
220 * <ul>
221 * <li>the cache will not yet be accessible from the CacheManager.
222 * <li>the addCaches methods which cause this notification are synchronized on the
223 * CacheManager. An attempt to call {@link net.sf.ehcache.CacheManager#getEhcache(String)}
224 * will cause a deadlock.
225 * </ul>
226 * The calling method will block until this method returns.
227 * <p/>
228 *
229 * @param cacheName the name of the <code>Cache</code> the operation relates to
230 * @see net.sf.ehcache.event.CacheEventListener
231 */
232 public void notifyCacheAdded(String cacheName) {
233 if (registerCaches || registerCacheStatistics || registerCacheConfigurations) {
234 Cache cache = new Cache(backingCacheManager.getCache(cacheName));
235 try {
236 registerCachesIfRequired(cache);
237 registerCacheStatisticsIfRequired(cache);
238 registerCacheConfigurationIfRequired(cache);
239 } catch (Exception e) {
240 LOG.error("Error registering cache for management for " + cache.getObjectName()
241 + " . Error was " + e.getMessage(), e);
242 }
243 }
244 }
245
246 /**
247 * Called immediately after a cache has been disposed and removed. The calling method will
248 * block until this method returns.
249 * <p/>
250 * Note that the CacheManager calls this method from a synchronized method. Any attempt to
251 * call a synchronized method on CacheManager from this method will cause a deadlock.
252 * <p/>
253 * Note that a {@link net.sf.ehcache.event.CacheEventListener} status changed will also be triggered. Any
254 * attempt from that notification to access CacheManager will also result in a deadlock.
255 *
256 * @param cacheName the name of the <code>Cache</code> the operation relates to
257 */
258 public void notifyCacheRemoved(String cacheName) {
259
260 ObjectName objectName = null;
261 try {
262 if (registerCaches) {
263 objectName = Cache.createObjectName(backingCacheManager.toString(), cacheName);
264 mBeanServer.unregisterMBean(objectName);
265 }
266 if (registerCacheConfigurations) {
267 objectName = CacheConfiguration.createObjectName(backingCacheManager.toString(), cacheName);
268 mBeanServer.unregisterMBean(objectName);
269 }
270 if (registerCacheStatistics) {
271 objectName = CacheStatistics.createObjectName(backingCacheManager.toString(), cacheName);
272 mBeanServer.unregisterMBean(objectName);
273 }
274 } catch (Exception e) {
275 LOG.error("Error unregistering cache for management for " + objectName
276 + " . Error was " + e.getMessage(), e);
277 }
278
279 }
280
281 }