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.hibernate.management.impl;
18  
19  import java.lang.reflect.Field;
20  import java.util.Iterator;
21  import java.util.Map;
22  import java.util.Properties;
23  import java.util.TimerTask;
24  import java.util.concurrent.atomic.AtomicBoolean;
25  
26  import net.sf.ehcache.CacheManager;
27  
28  import org.hibernate.SessionFactory;
29  import org.hibernate.cache.CacheException;
30  import org.hibernate.cfg.Environment;
31  import org.hibernate.impl.SessionFactoryObjectFactory;
32  import org.slf4j.Logger;
33  import org.slf4j.LoggerFactory;
34  
35  /***
36   * Helper class for registering mbeans for ehcache backed hibernate second level cache
37   *
38   * <p />
39   *
40   * @author <a href="mailto:asanoujam@terracottatech.com">Abhishek Sanoujam</a>
41   *
42   */
43  public class ProviderMBeanRegistrationHelper {
44      private static final Logger LOG = LoggerFactory.getLogger(ProviderMBeanRegistrationHelper.class);
45  
46      private static final int MILLIS_PER_SECOND = 1000;
47      private static final int SLEEP_MILLIS = 500;
48  
49      private volatile EhcacheHibernateMBeanRegistrationImpl ehcacheHibernateMBeanRegistration;
50  
51      /***
52       * Registers mbean for the input cache manager and the session factory name
53       *
54       * @param manager
55       *            the backing cachemanager
56       * @param properties
57       *            session factory config properties
58       */
59      public void registerMBean(final CacheManager manager, final Properties properties) {
60          if (Boolean.getBoolean("tc.active")) {
61              ehcacheHibernateMBeanRegistration = new EhcacheHibernateMBeanRegistrationImpl();
62              manager.getTimer().scheduleAtFixedRate(new RegisterMBeansTask(ehcacheHibernateMBeanRegistration, manager, properties), SLEEP_MILLIS,
63                      SLEEP_MILLIS);
64          }
65      }
66  
67      /***
68       * Unregisters previously registered mbean.
69       */
70      public void unregisterMBean() {
71          if (ehcacheHibernateMBeanRegistration != null) {
72              ehcacheHibernateMBeanRegistration.dispose();
73              ehcacheHibernateMBeanRegistration = null;
74          }
75      }
76  
77      /***
78       *
79       * Task for running mbean registration that can be scheduled in a timer
80       *
81       */
82      private static class RegisterMBeansTask extends TimerTask {
83          private static final int NUM_SECONDS = 30;
84          private long startTime;
85          private final AtomicBoolean mbeanRegistered = new AtomicBoolean(false);
86          private final EhcacheHibernateMBeanRegistrationImpl ehcacheHibernateMBeanRegistration;
87          private final CacheManager manager;
88          private final Properties properties;
89  
90          public RegisterMBeansTask(EhcacheHibernateMBeanRegistrationImpl ehcacheHibernateMBeanRegistration,
91                  CacheManager manager, Properties properties) {
92              this.ehcacheHibernateMBeanRegistration = ehcacheHibernateMBeanRegistration;
93              this.manager = manager;
94              this.properties = properties;
95          }
96  
97          @Override
98          public void run() {
99              LOG.debug("Running mbean initializer task for ehcache hibernate...");
100             startTime = System.currentTimeMillis();
101             if (mbeanRegistered.compareAndSet(false, true)) {
102                 try {
103                     ehcacheHibernateMBeanRegistration.registerMBeanForCacheManager(manager, properties);
104                     LOG.debug("Successfully registered bean");
105                 } catch (Exception e) {
106                     throw new CacheException(e);
107                 }
108             }
109             SessionFactory sessionFactory = locateSessionFactory();
110             if (sessionFactory == null) {
111                 LOG.debug("SessionFactory is probably still being initialized..."
112                         + " waiting for it to complete before enabling hibernate statistics monitoring via JMX");
113                 if (System.currentTimeMillis() > startTime + (NUM_SECONDS * MILLIS_PER_SECOND)) {
114                     LOG.info("Hibernate statistics monitoring through JMX is DISABLED.");
115                     LOG.info("Failed to look up SessionFactory after " + NUM_SECONDS + " seconds using session-factory properties '"
116                             + properties + "'");
117                     this.cancel();
118                 }
119                 return;
120             } else {
121                 ehcacheHibernateMBeanRegistration.enableHibernateStatisticsSupport(sessionFactory);
122                 LOG.info("Hibernate statistics monitoring through JMX is ENABLED. ");
123                 this.cancel();
124             }
125         }
126 
127         private SessionFactory locateSessionFactory() {
128             String jndiName = properties.getProperty(Environment.SESSION_FACTORY_NAME);
129             if (jndiName != null) {
130                 return (SessionFactory)SessionFactoryObjectFactory.getNamedInstance(jndiName);
131             }
132             try {
133                 Class factoryType = SessionFactoryObjectFactory.class;
134                 Field instancesField = getField(factoryType, "INSTANCES");
135                 if (instancesField == null) {
136                     throw new RuntimeException("Expected INSTANCES field on " + SessionFactoryObjectFactory.class.getName());
137                 }
138                 instancesField.setAccessible(true);
139                 Map map = (Map)instancesField.get(null);
140                 if (map == null) {
141                     return null;
142                 }
143                 Iterator values = map.values().iterator();
144                 while (values.hasNext()) {
145                     SessionFactory sessionFactory = (SessionFactory)values.next();
146                     Class sessionFactoryType = sessionFactory.getClass();
147                     Field propertiesField = getField(sessionFactoryType, "properties");
148                     if (propertiesField != null) {
149                         propertiesField.setAccessible(true);
150                         Properties props = (Properties)propertiesField.get(sessionFactory);
151                         if (props != null && props.equals(properties)) {
152                             return sessionFactory;
153                         }
154                     }
155                 }
156             } catch (RuntimeException re) {
157                 LOG.error("Error locating Hibernate Session Factory", re);
158             } catch (IllegalAccessException iae) {
159                 LOG.error("Error locating Hibernate Session Factory", iae);
160             }
161             return null;
162         }
163     }
164 
165     private static Field getField(Class c, String fieldName) {
166         for (Field field : c.getDeclaredFields()) {
167             if (field.getName().equals(fieldName)) {
168                 return field;
169             }
170         }
171         throw new NoSuchFieldError("Type '" + c + "' has no field '" + fieldName + "'");
172     }
173 }