views:

1775

answers:

2

I've set hibernate.generate_statistics=true and now need to register the mbeans so I can see the statistics in the jmx console. I can't seem to get anywhere and this doesn't seem like it should be such a difficult problem. Maybe I'm making things overcomplicated, but in any case so far I've tried:

  • I copied EhCacheProvider and had it instantiate an extended version of CacheManager which overloaded init() and called ManagementService.registerMBeans(...) after initialization. The code all ran fine until the actual call to registerMBeans(...) which would cause the provider initialization to fail with a generic error (unfortunately I didn't write it down.) This approach was motivated by the methods used in this liferay performance walkthrough.
  • I created my own MBean with a start method that ran similar code to this example of registering ehcache's jmx mbeans. Everything appeared to work correctly and my mbean shows up in the jmx console but nothing for net.sf.ehcache.
  • I've since upgraded ehcache to 1.5 (we were using 1.3, not sure if that's specific to jboss 4.2.1 or just something we chose ourselves) and changed to using the SingletonEhCacheProvider and trying to just manually grab the statistics instead of dealing with the mbean registration. It hasn't really gone any better though; if I call getInstance() the CacheManager that's returned only has a copy of StandardQueryCache, but jboss logs show that many other caches have been initialized (one for each of the cached entities in our application.)

EDIT: Well I have figured out one thing...connecting via JConsole does reveal the statistics mbeans. I guess ManagementFactory.getPlatformMBeanServer() doesn't give you the same mbean server as jboss is using. Anyway it looks like I'm encountering a similar problem as when I tried collecting the statistics manually, because I'm getting all zeros even after clicking through my app for a bit.

A: 

Solved. Since I was not seeing all the caches for my entities I suspected I was not getting the right SessionFactory instance. I started out with this line (see the example jmx registration code in the link I provided in the question):

SessionFactory sf = (new Configuration()).configure().buildSessionFactory();

The end result was the cache manager I ultimately ended up with was a new instance and not the one from the persistence context. So I tried refactoring as:

EntityManagerFactory emf = Persistence.createEntityManagerFactory("myPersistenceUnit");
return ((EntityManagerFactoryImpl)emf).getSessionFactory();

but that just threw an exception (I don't remember the exact text; something to the effect of "can't initialize persistence context.") So left with no other options, I added a stateless bean (UtilMgr) to my application and let persistence inject the correct SessionFactory. Here's that bean:

import javax.ejb.Stateless;
import javax.persistence.PersistenceUnit;
import net.sf.ehcache.CacheManager;
import org.hibernate.SessionFactory;

@Stateless
public class UtilMgrBean implements UtilMgr {
    // NOTE: rename as necessary
    @PersistenceUnit(unitName = "myPersistenceCtx")
    private SessionFactory sessionFactory;

    public SessionFactory getSessionFactory() {
        return this.sessionFactory;
    }

    public CacheManager getCacheManager() {
        return CacheManager.getInstance(); // NOTE: assumes SingletonEhCacheProvider
    }
}

and here's the corrected code from the previously mentioned walkthrough:

try {
    // NOTE: lookupBean is a utility method in our app we use for jndi lookups.
    //   replace as necessary for your application.
    UtilMgr utilMgr = (UtilMgr)Manager.lookupBean("UtilMgrBean", UtilMgr.class);
    SessionFactory sf = utilMgr.getSessionFactory();
    MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();

    // NOTE: replace myAppName as necessary
    ObjectName on = new ObjectName("Hibernate:type=statistics,application=myAppName");

    // Enable Hibernate JMX Statistics
    StatisticsService statsMBean = new StatisticsService();
    statsMBean.setSessionFactory(sf);
    statsMBean.setStatisticsEnabled(true);
    mbs.registerMBean(statsMBean, on);

    CacheManager cacheMgr = utilMgr.getCacheManager();
    ManagementService.registerMBeans(cacheMgr, mbs, true, true, true, true);
} catch(Throwable t) {
    throw new RuntimeException(t);
}

You can also use this getCacheManager() method of UtilMgr if you want to retrieve statistics manually (which is what I'll probably do anyway.) You can find more info about how use use the Cache and Statistics objects in the ehcache code samples.

If anyone can fill me in on a way to statically lookup the session factory without the need for creating this extra session bean, I'd love to hear it.

Matt S.
+1  A: 

The answer given above assumes that SingletonEhcacheProvider is being used and it also needs the utilmgr bean, this other solution uses a startup bean and does not make the singleton assumption

@Name("hibernateStatistics")
@Scope(ScopeType.APPLICATION)
@Startup
public class HibernateUtils {
@In
private EntityManager entityManager;

@Create
public void onStartup() {
    if (entityManager != null) {
        try {
            //lookup the jboss mbean server
            MBeanServer beanServer = org.jboss.mx.util.MBeanServerLocator.locateJBoss();
            StatisticsService mBean = new StatisticsService();
            ObjectName objectName = new ObjectName("Hibernate:type=statistics,application=<application-name>");
            try{
                beanServer.unregisterMBean(objectName);
            }catch(Exception exc) {
                //no problems, as unregister is not important
            }
            SessionFactory sessionFactory = ((HibernateSessionProxy) entityManager.getDelegate()).getSessionFactory();
            mBean.setSessionFactory(sessionFactory);
            beanServer.registerMBean(mBean, objectName);

            if (sessionFactory instanceof SessionFactoryImplementor ){
                CacheProvider cacheProvider = ((SessionFactoryImplementor)sessionFactory).getSettings().getCacheProvider();
                if (cacheProvider instanceof EhCacheProvider)  {
                    try{
                        Field field = EhCacheProvider.class.getDeclaredField("manager");
                        field.setAccessible(true);
                        CacheManager cacheMgr = (CacheManager) field.get(cacheProvider);
                        ManagementService.registerMBeans(cacheMgr, beanServer, true, true, true, true);
                    }catch(Exception exc) {
                        //do nothing
                        exc.printStackTrace();
                    }
                }
            }

        } catch (Exception e) {
            throw new RuntimeException("The persistence context " + entityManager.toString() + "is not properly      configured.", e);
        }
    }
 }

}

We use MbeanServerLocator as jboss mbean would be the second mbean server in environments like linux.

sans_sense