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 /***
50 * Registers mbean for the input cache manager and the session factory name
51 *
52 * @param manager
53 * the backing cachemanager
54 * @param properties
55 * session factory config properties
56 */
57 public void registerMBean(final CacheManager manager, final Properties properties) {
58 if (Boolean.getBoolean("tc.active")) {
59 manager.getTimer().scheduleAtFixedRate(new RegisterMBeansTask(manager, properties), SLEEP_MILLIS,
60 SLEEP_MILLIS);
61 }
62 }
63
64 /***
65 *
66 * Task for running mbean registration that can be scheduled in a timer
67 *
68 */
69 private static class RegisterMBeansTask extends TimerTask {
70 private static final int NUM_SECONDS = 30;
71 private long startTime;
72 private final AtomicBoolean mbeanRegistered = new AtomicBoolean(false);
73 private EhcacheHibernateMBeanRegistration ehcacheHibernateMBeanRegistration = new EhcacheHibernateMBeanRegistrationImpl();
74 private final CacheManager manager;
75 private final Properties properties;
76
77 public RegisterMBeansTask(CacheManager manager, Properties properties) {
78 this.manager = manager;
79 this.properties = properties;
80 }
81
82 @Override
83 public void run() {
84 LOG.debug("Running mbean initializer task for ehcache hibernate...");
85 startTime = System.currentTimeMillis();
86 if (mbeanRegistered.compareAndSet(false, true)) {
87 try {
88 ehcacheHibernateMBeanRegistration.registerMBeanForCacheManager(manager, properties);
89 LOG.debug("Successfully registered bean");
90 } catch (Exception e) {
91 throw new CacheException(e);
92 }
93 }
94 SessionFactory sessionFactory = locateSessionFactory();
95 if (sessionFactory == null) {
96 LOG.debug("SessionFactory is probably still being initialized..."
97 + " waiting for it to complete before enabling hibernate statistics monitoring via JMX");
98 if (System.currentTimeMillis() > startTime + (NUM_SECONDS * MILLIS_PER_SECOND)) {
99 LOG.info("Hibernate statistics monitoring through JMX is DISABLED.");
100 LOG.info("Failed to look up SessionFactory after " + NUM_SECONDS + " seconds using session-factory properties '"
101 + properties + "'");
102 this.cancel();
103 }
104 return;
105 } else {
106 ehcacheHibernateMBeanRegistration.enableHibernateStatisticsSupport(sessionFactory);
107 LOG.info("Hibernate statistics monitoring through JMX is ENABLED. ");
108 this.cancel();
109 }
110 }
111
112 private SessionFactory locateSessionFactory() {
113 String jndiName = properties.getProperty(Environment.SESSION_FACTORY_NAME);
114 if (jndiName != null) {
115 return (SessionFactory)SessionFactoryObjectFactory.getNamedInstance(jndiName);
116 }
117 try {
118 Class factoryType = SessionFactoryObjectFactory.class;
119 Field instancesField = getField(factoryType, "INSTANCES");
120 if (instancesField == null) {
121 throw new RuntimeException("Expected INSTANCES field on SessionFactoryObjectFactory");
122 }
123 instancesField.setAccessible(true);
124 Map map = (Map)instancesField.get(null);
125 if (map == null) {
126 return null;
127 }
128 Iterator values = map.values().iterator();
129 while (values.hasNext()) {
130 SessionFactory sessionFactory = (SessionFactory)values.next();
131 Class sessionFactoryType = sessionFactory.getClass();
132 Field propertiesField = getField(sessionFactoryType, "properties");
133 if (propertiesField != null) {
134 propertiesField.setAccessible(true);
135 Properties props = (Properties)propertiesField.get(sessionFactory);
136 if (props != null && props.equals(properties)) {
137 return sessionFactory;
138 }
139 }
140 }
141 } catch (Exception e) {
142 /***/
143 }
144 return null;
145 }
146 }
147
148 private static Field getField(Class c, String fieldName) {
149 for (Field field : c.getDeclaredFields()) {
150 if (field.getName().equals(fieldName)) {
151 return field;
152 }
153 }
154 throw new NoSuchFieldError("Type '" + c + "' has no field '" + fieldName + "'");
155 }
156 }