/**
* NOTE: This copyright does *not* cover user programs that use HQ
* program services by normal system calls through the application
* program interfaces provided as part of the Hyperic Plug-in Development
* Kit or the Hyperic Client Development Kit - this is merely considered
* normal use of the program, and does *not* fall under the heading of
* "derived work".
*
* Copyright (C) [2010], VMware, Inc.
* This file is part of HQ.
*
* HQ is free software; you can redistribute it and/or modify
* it under the terms version 2 of the GNU General Public License as
* published by the Free Software Foundation. This program is distributed
* in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA.
*
*/
package org.hyperic.hibernate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import javax.management.InstanceAlreadyExistsException;
import javax.management.MBeanRegistrationException;
import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.NotCompliantMBeanException;
import javax.management.ObjectName;
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.management.ManagementService;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.cfg.Configuration;
import org.hibernate.jmx.StatisticsService;
import org.hyperic.hq.common.DiagnosticObject;
import org.hyperic.hq.common.DiagnosticsLogger;
import org.hyperic.util.PrintfFormat;
import org.hyperic.util.StringUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate3.LocalSessionFactoryBean;
import org.springframework.orm.hibernate3.SessionFactoryUtils;
/**
* Extension of the {@link LocalSessionFactoryBean} that preloads the 2nd level
* cache for performance optimizations and registers Hibernate and EhCache stat
* MBeans
* @author jhickey
*
*/
public class CacheInitializingLocalSessionFactoryBean
extends LocalSessionFactoryBean {
private final List<String> classesForCache;
private final Log log = LogFactory.getLog(CacheInitializingLocalSessionFactoryBean.class);
@Autowired
private MBeanServer mBeanServer;
@Autowired
private DiagnosticsLogger diagnosticsLogger;
//required for proper shutdown
private DiagnosticObject cacheDiagnostics ;
private static final String HIBERNATE_STATS_OBJECT_NAME = "Hibernate:type=statistics,application=hq";
public CacheInitializingLocalSessionFactoryBean(List<String> classesForCache) {
super();
this.classesForCache = classesForCache;
}
@Override
public void destroy() throws HibernateException {
super.destroy();
try {
mBeanServer.unregisterMBean(new ObjectName(HIBERNATE_STATS_OBJECT_NAME));
} catch (Exception e) {
logger.warn("Error unregistering Hibernate Stats MBean", e);
}
//remove the cache diagnostic object from the logger
this.diagnosticsLogger.removeDiagnosticObject(this.cacheDiagnostics) ;
}
@Override
protected void afterSessionFactoryCreation() throws Exception {
super.afterSessionFactoryCreation();
final Configuration configurations = this.getConfiguration() ;
if(configurations != null) {
log.info("DATABASE CONNECTION URL: " + this.getConfiguration().getProperty("hibernate.connection.url")) ;
}//EO if configuration were provided
registerMBeans();
initEhCacheDiagnostics();
preloadCache();
}
private void registerMBeans() throws MalformedObjectNameException,
InstanceAlreadyExistsException, MBeanRegistrationException, NotCompliantMBeanException {
ObjectName on = new ObjectName(HIBERNATE_STATS_OBJECT_NAME);
StatisticsService mBean = new StatisticsService();
mBean.setSessionFactory(getSessionFactory());
mBeanServer.registerMBean(mBean, on);
ManagementService.registerMBeans(CacheManager.getInstance(), mBeanServer, false, false,
false, true);
}
@SuppressWarnings("unchecked")
private void preloadCache() {
Session session = SessionFactoryUtils.getSession(getSessionFactory(), true);
for (String className : classesForCache) {
Class<?> clazz;
className = className.trim();
if (className.length() == 0 || className.startsWith("#")) {
continue;
}
try {
clazz = Class.forName(className);
} catch (Exception e) {
log.warn("Unable to find preload cache for class [" + className + "]", e);
continue;
}
long start = System.currentTimeMillis();
Collection<Object> vals = session.createCriteria(clazz).list();
long end = System.currentTimeMillis();
log.info("Preloaded " + vals.size() + " [" + clazz.getName() + "] in " + (end - start) +
" millis");
// Evict, to avoid dirty checking everything in the inventory
for (Object val : vals) {
session.evict(val);
}
}
}
private void initEhCacheDiagnostics() {
// Add ehcache statistics to the diagnostics
this.cacheDiagnostics = new DiagnosticObject() {
private final PrintfFormat _fmt = new PrintfFormat("%-55s %-6d %-6d %6d");
private final PrintfFormat _hdr = new PrintfFormat("%-55s %-6s %-6s %6s");
public String getName() {
return "EhCache Diagnostics";
}
public String getShortName() {
return "ehcacheDiag";
}
private List<Cache> getSortedCaches() {
List<Cache> res = getCaches();
Collections.sort(res, new Comparator<Cache>() {
public int compare(Cache c1, Cache c2) {
return c1.getName().compareTo(c2.getName());
}
});
return res;
}
public String getStatus() {
String separator = System.getProperty("line.separator");
StringBuffer buf = new StringBuffer(separator);
Object[] fmtArgs = new Object[5];
fmtArgs[0] = "Cache";
fmtArgs[1] = "Size";
fmtArgs[2] = "Hits";
fmtArgs[3] = "Misses";
buf.append(_hdr.sprintf(fmtArgs)).append(separator);
fmtArgs[0] = "=====";
fmtArgs[1] = "====";
fmtArgs[2] = "====";
fmtArgs[3] = "=====";
buf.append(_hdr.sprintf(fmtArgs));
for (Cache cache : getSortedCaches()) {
fmtArgs[0] = StringUtil.dotProximate(cache.getName(), 55);
fmtArgs[1] = new Integer(cache.getSize());
fmtArgs[2] = new Long(cache.getStatistics().getCacheHits());
fmtArgs[3] = new Long(cache.getStatistics().getCacheMisses());
buf.append(separator).append(_fmt.sprintf(fmtArgs));
}
return buf.toString();
}
public String getShortStatus() {
return getStatus();
}
@Override
public String toString() {
return "ehcache";
}
};
diagnosticsLogger.addDiagnosticObject(cacheDiagnostics);
}
private List<Cache> getCaches() {
CacheManager cacheManager = CacheManager.getInstance();
String[] caches = cacheManager.getCacheNames();
List<Cache> res = new ArrayList<Cache>(caches.length);
for (int i = 0; i < caches.length; i++) {
res.add(cacheManager.getCache(caches[i]));
}
return res;
}
}