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.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      // name of the cacheManager when the mbeans are registered.
68      // On cacheManager.dispose(), need to remove
69      // the mbean with the name used while registering the mbean.
70      // Avoid leaking mbeans when user changes name of the cacheManager after
71      // construction
72      // by doing setName()
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         // setup event listener so that addition of new caches registers
102         // corresponding Cache MBeans
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                 // register the CacheManager MBean
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         // register Cache MBeans for the caches
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         // no-op
164     }
165 
166     // no need to worry about duplicate cache names
167     // cache manager does not allow duplicate cache names
168     // and as cache manager mbeans names are unique, the mbeans for the caches
169     // will also be unique
170     private void registerCacheMBean(Ehcache cache) throws InstanceAlreadyExistsException, MBeanRegistrationException,
171             NotCompliantMBeanException {
172         // enable sampled stats
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         // enable sampled stats
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 }