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.sampled;
18
19 import java.lang.management.ManagementFactory;
20 import java.util.Map;
21 import java.util.Set;
22
23 import javax.management.InstanceAlreadyExistsException;
24 import javax.management.MBeanRegistrationException;
25 import javax.management.MBeanServer;
26 import javax.management.MalformedObjectNameException;
27 import javax.management.NotCompliantMBeanException;
28 import javax.management.ObjectName;
29
30 import net.sf.ehcache.CacheException;
31 import net.sf.ehcache.CacheManager;
32 import net.sf.ehcache.Ehcache;
33 import net.sf.ehcache.Status;
34 import net.sf.ehcache.event.CacheManagerEventListener;
35 import net.sf.ehcache.hibernate.management.impl.BaseEmitterBean;
36 import net.sf.ehcache.management.provider.MBeanRegistrationProvider;
37 import net.sf.ehcache.management.provider.MBeanRegistrationProviderException;
38 import net.sf.ehcache.store.chm.ConcurrentHashMap;
39 import net.sf.ehcache.terracotta.ClusteredInstanceFactory;
40
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
43
44 /***
45 * An implementation of {@link MBeanRegistrationProvider} which registers
46 * sampled MBeans for the CacheManager and its Caches.
47 * This also implements {@link CacheManagerEventListener} to add/remove/cleanup
48 * MBeans for new caches added or removed
49 *
50 * <p />
51 *
52 * @author <a href="mailto:asanoujam@terracottatech.com">Abhishek Sanoujam</a>
53 * @since 1.7
54 */
55 public class SampledMBeanRegistrationProvider implements MBeanRegistrationProvider, CacheManagerEventListener {
56
57 private static final Logger LOG = LoggerFactory.getLogger(SampledMBeanRegistrationProvider.class.getName());
58
59 private static final int MAX_MBEAN_REGISTRATION_RETRIES = 50;
60
61 private Status status = Status.STATUS_UNINITIALISED;
62 private CacheManager cacheManager;
63 private String clientUUID;
64 private final MBeanServer mBeanServer;
65 private final Map<ObjectName, BaseEmitterBean> mbeans = new ConcurrentHashMap<ObjectName, BaseEmitterBean>();
66
67
68
69
70
71
72
73 private volatile String registeredCacheManagerName;
74 private SampledCacheManager cacheManagerMBean;
75
76 /***
77 * Default constructor
78 */
79 public SampledMBeanRegistrationProvider() {
80 this.mBeanServer = ManagementFactory.getPlatformMBeanServer();
81 }
82
83 /***
84 * {@inheritDoc}
85 */
86 public synchronized void initialize(CacheManager cacheManagerParam, ClusteredInstanceFactory clusteredInstanceFactory) {
87 if (isAlive()) {
88 return;
89 }
90 status = Status.STATUS_ALIVE;
91 this.cacheManager = cacheManagerParam;
92 this.clientUUID = clusteredInstanceFactory == null ? "" : clusteredInstanceFactory.getUUID();
93 try {
94 cacheManagerMBean = new SampledCacheManager(cacheManager);
95 registerCacheManagerMBean(cacheManagerMBean);
96 } catch (Exception e) {
97 status = Status.STATUS_UNINITIALISED;
98 throw new CacheException(e);
99 }
100
101
102
103 cacheManager.getCacheManagerEventListenerRegistry().registerListener(this);
104 }
105
106 private void registerCacheManagerMBean(SampledCacheManager cacheManagerMBean) throws Exception {
107 int tries = 0;
108 boolean success = false;
109 Exception exception = null;
110 do {
111 this.registeredCacheManagerName = cacheManager.getName();
112 if (tries != 0) {
113 registeredCacheManagerName += "_" + tries;
114 }
115 try {
116
117 ObjectName cacheManagerObjectName = SampledEhcacheMBeans.getCacheManagerObjectName(clientUUID, registeredCacheManagerName);
118 mBeanServer.registerMBean(cacheManagerMBean,
119 cacheManagerObjectName);
120 mbeans.put(cacheManagerObjectName, cacheManagerMBean);
121 success = true;
122 cacheManagerMBean.setMBeanRegisteredName(registeredCacheManagerName);
123 break;
124 } catch (InstanceAlreadyExistsException e) {
125 success = false;
126 exception = e;
127 }
128 tries++;
129 } while (tries < MAX_MBEAN_REGISTRATION_RETRIES);
130 if (!success) {
131 throw new Exception("Cannot register mbean for CacheManager with name" + cacheManager.getName() + " after "
132 + MAX_MBEAN_REGISTRATION_RETRIES + " retries. Last tried name=" + registeredCacheManagerName, exception);
133 }
134
135
136 String[] caches = cacheManager.getCacheNames();
137 for (String cacheName : caches) {
138 Ehcache cache = cacheManager.getEhcache(cacheName);
139 registerCacheMBean(cache);
140 registerStoreMBean(cache);
141 }
142 }
143
144 /***
145 * {@inheritDoc}
146 */
147 public synchronized void reinitialize(ClusteredInstanceFactory clusteredInstanceFactory) throws MBeanRegistrationProviderException {
148 dispose();
149 initialize(this.cacheManager, clusteredInstanceFactory);
150 }
151
152 /***
153 * {@inheritDoc}
154 */
155 public synchronized boolean isInitialized() {
156 return status == Status.STATUS_ALIVE;
157 }
158
159 /***
160 * CacheManagerEventListener.init() - no need to do anything here
161 */
162 public void init() throws CacheException {
163
164 }
165
166
167
168
169
170 private void registerCacheMBean(Ehcache cache) throws InstanceAlreadyExistsException, MBeanRegistrationException,
171 NotCompliantMBeanException {
172
173 if (cache.isStatisticsEnabled()) {
174 cache.setSampledStatisticsEnabled(true);
175 }
176 SampledCache terracottaCacheMBean = new SampledCache(cache);
177 try {
178 ObjectName cacheObjectName = SampledEhcacheMBeans.getCacheObjectName(clientUUID, registeredCacheManagerName,
179 terracottaCacheMBean.getImmutableCacheName());
180 mBeanServer.registerMBean(terracottaCacheMBean,
181 cacheObjectName);
182 mbeans.put(cacheObjectName, terracottaCacheMBean);
183 } catch (MalformedObjectNameException e) {
184 throw new MBeanRegistrationException(e);
185 }
186 }
187
188 private void registerStoreMBean(Ehcache cache) throws InstanceAlreadyExistsException, MBeanRegistrationException,
189 NotCompliantMBeanException {
190
191 Object bean;
192 if (cache instanceof net.sf.ehcache.Cache) {
193 bean = ((net.sf.ehcache.Cache) cache).getStoreMBean();
194 if (bean != null) {
195 try {
196 mBeanServer.registerMBean(bean,
197 SampledEhcacheMBeans.getStoreObjectName(clientUUID, registeredCacheManagerName,
198 cache.getName()));
199 } catch (MalformedObjectNameException e) {
200 throw new MBeanRegistrationException(e);
201 }
202 }
203 }
204 }
205
206 /***
207 * Returns the listener status.
208 *
209 * @return the status at the point in time the method is called
210 */
211 public synchronized Status getStatus() {
212 return status;
213 }
214
215 /***
216 * Stop the listener and free any resources. Removes registered ObjectNames
217 *
218 * @throws net.sf.ehcache.CacheException
219 * - all exceptions are wrapped in CacheException
220 */
221 public synchronized void dispose() throws CacheException {
222 if (!isAlive()) {
223 return;
224 }
225
226 Set<ObjectName> registeredObjectNames = mbeans.keySet();
227 for (ObjectName objectName : registeredObjectNames) {
228 try {
229 if (mBeanServer.isRegistered(objectName)) {
230 mBeanServer.unregisterMBean(objectName);
231 }
232 BaseEmitterBean mbean = mbeans.get(objectName);
233 mbean.dispose();
234 } catch (Exception e) {
235 LOG.warn("Error unregistering object instance " + objectName + " . Error was " + e.getMessage(), e);
236 }
237 }
238 mbeans.clear();
239
240 cacheManager.getCacheManagerEventListenerRegistry().unregisterListener(this);
241
242 status = Status.STATUS_SHUTDOWN;
243 }
244
245 /***
246 * Returns true if this {@link SampledMBeanRegistrationProvider} is alive
247 *
248 * @return true if alive otherwise false
249 */
250 public synchronized boolean isAlive() {
251 return status == Status.STATUS_ALIVE;
252 }
253
254 /***
255 * Called immediately after a cache has been added and activated.
256 */
257 public void notifyCacheAdded(String cacheName) {
258 if (!isAlive()) {
259 return;
260 }
261 try {
262 Ehcache cache = cacheManager.getEhcache(cacheName);
263 registerCacheMBean(cache);
264 registerStoreMBean(cache);
265 } catch (Exception e) {
266 LOG.warn("Error registering cache for management for " + cacheName + " . Error was " + e.getMessage(), e);
267 }
268 }
269
270 /***
271 * Called immediately after a cache has been disposed and removed. The
272 * calling method will block until this method
273 * returns.
274 *
275 * @param cacheName
276 * the name of the <code>Cache</code> the operation relates to
277 */
278 public void notifyCacheRemoved(String cacheName) {
279 if (!isAlive()) {
280 return;
281 }
282 ObjectName objectName = null;
283 try {
284 objectName = SampledEhcacheMBeans.getCacheObjectName(clientUUID, registeredCacheManagerName, cacheName);
285 if (mBeanServer.isRegistered(objectName)) {
286 mBeanServer.unregisterMBean(objectName);
287 }
288 } catch (Exception e) {
289 LOG.warn("Error unregistering cache for management for " + objectName + " . Error was " + e.getMessage(), e);
290 }
291 }
292 }