2

Collecting Hibernate/Ehcache statistics and exposing them through JMX in Spring-based setups seems easy. The Internet has lots of resources that help e.g. http://snippets.dzone.com/posts/show/11159

However, all those articles assume one is working with a Hibernate session factory of some sort. I'm not - my entities are JPA annotated and I use a javax.persistence.EntityManager. If I were deploying to a Java EE container I might have been able to obtain a Hibernate session factory through JNDI as described here http://internna.blogspot.com/2007/08/hibernate-statistics-in-enterprise-5.html but I'm on Tomcat...

How to go about this? I haven't come up with a solution yet.

If I had a reference to the Ehcache CacheManager I could try something like:

<context:mbean-server />
<bean class="net.sf.ehcache.management.ManagementService" init-method="init">
  <constructor-arg ref="..myCacheManager.."/>
  <constructor-arg ref="mbeanServer"/>
  <constructor-arg value="true"/>
  <constructor-arg value="true"/>
  <constructor-arg value="true"/>
  <constructor-arg value="true"/>
</bean>

Since the cache manager is created by Hibernate (i.e. it's not a Spring bean) it won't work. I tried replacing that ref with

<constructor-arg><bean id="cacheManager" class="net.sf.ehcache.CacheManager" factory-method="getInstance"/></constructor-arg>

hoping I'd somehow catch the right instance. Won't work either as this would in fact create a new cache manager instance.

Arjan Tijms
  • 37,782
  • 12
  • 108
  • 140
Marcel Stör
  • 22,695
  • 19
  • 92
  • 198

6 Answers6

3

I've recently built a sample Spring based webapp that very cleanly enables JMX for latest versions of Spring, Hibernate and Ehcache.

It has examples for both EntityManager based access and DAO access (including transactions!). It also shows how to do annotation based injection in order to negate having to use Spring's xml config for beans. There is even a SpringMVC based example servlet using annotations. Basically, this is a Spring based version of a fairly powerful application server running on top of any servlet engine.

It isn't documented yet, but I'll get to that soon. Take a look at the configuration files and source code and it should be pretty clear.

The motivation behind this is that I got tired of all of the crazy blog posts with 50 different ways to set things up and finally made a single simple source that people can work from. It is up on github so feel free to fork the project and do whatever you want with it.

https://github.com/lookfirst/fallback

Public Profile
  • 1,817
  • 1
  • 21
  • 20
  • -1 for bothering to upload nearly 60 third party libs to github, what a waste of resources. Downside of Ant vs. Maven build...Checked your applicationContext.xml but failed to see how its content is relevant to this question? – Marcel Stör May 21 '11 at 07:59
  • 1
    Waste of resources? Are you really that concerned with a few bits on someone else's disk? As for my applicationContext.xml, you are looking in the wrong place. https://github.com/lookfirst/fallback/blob/master/src/java/com/github/fallback/util/JmxAgent.java – Public Profile May 23 '11 at 00:39
  • 1
    @Marcel how does uploading third party libs to his github account affect the quality of this answer? I would I could downvote a comment. – matt b Jul 15 '11 at 15:12
  • @matt b, it doesn't. I only just realized that my comment is missing a few words. What I meant to say was "Thanks for the answer but -1...". I didn't actually downvote Jon's answer. – Marcel Stör Jul 18 '11 at 18:57
  • 1
    Thank you for the nice template. – Dominik Aug 03 '11 at 08:47
  • I used the HibernateStatisticsFactoryBean from here: https://hibernate.atlassian.net/browse/HHH-6190?focusedCommentId=56718&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-56718 – Benedikt Köppel Jan 15 '16 at 17:22
2

You can actually expose any CacheManager using Spring Expression Language.

<bean id="hibernateCacheManagement" class="net.sf.ehcache.management.ManagementService" init-method="init" >
    <constructor-arg value="#{T(net.sf.ehcache.CacheManager).getCacheManager('CACHE_NAME')}"/>
    <constructor-arg><ref bean="mbeanServer"/></constructor-arg>
    <constructor-arg value="true"/>
    <constructor-arg value="true"/>
    <constructor-arg value="true"/>
    <constructor-arg value="true"/>
</bean>

Where CACHE_NAME is the name of the cache configured in the ehcache.xml file.

<ehcache name="CACHE_NAME">
    ...
</ehcache>
Luzifer42
  • 698
  • 3
  • 8
2

The JPA EntityManager exposes the underlying Hibernate session, so you can get at its Factory:

   public static Session getHibernateSession(EntityManager entityManager) {
        Session session;
        if (entityManager.getDelegate() instanceof EntityManagerImpl) {
            EntityManagerImpl entityManagerImpl = (EntityManagerImpl) entityManager.getDelegate();
            session = entityManagerImpl.getSession();
        } else {
            session = (Session) entityManager.getDelegate();
        }

        return session;
    }

You can then use this Session to enable the statistics like we have done it in RHQ project

Heiko Rupp
  • 30,426
  • 13
  • 82
  • 119
1

I ended up writing the following class

HibernateStatisticsJmxRegistration

import javax.management.JMException;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.persistence.EntityManagerFactory;

import org.hibernate.SessionFactory;
import org.hibernate.ejb.HibernateEntityManagerFactory;
import org.hibernate.jmx.StatisticsService;
import org.springframework.beans.factory.annotation.Autowired;

/**
 * Provides code to register Hibernate's statistics bean with a JMX MBean server. Assumes that both
 * the MBeanServer and the EntityManagerFactory are available as Spring-managed beans. Note that
 * while registering this class enables the collection of statistics even if that was previously
 * disabled.
 * <p>
 * May become obsolete once <a href="https://hibernate.onjira.com/browse/HHH-6034">HHH-6034</a> is
 * implemented. Even if not the confusing situation abround the meanwhile deprecated
 * {@link StatisticsService} should be clear then.
 */
@SuppressWarnings({"deprecation", "javadoc" })
public class HibernateStatisticsJmxRegistration {

  @Autowired
  private EntityManagerFactory entityManagerFactory;

  @Autowired
  private MBeanServer mbeanServer;

  private ObjectName objectName;

  private String jmxObjectName = "org.hibernate:name=HibernateStatistics";

  /**
   * Registers the statistics MBean that wraps a Hibernate session factory. The bean is registered
   * under the name provided by {@link HibernateStatisticsJmxRegistration#getJmxObjectName()}.
   *
   * @throws JMException if anything fails..
   * @see HibernateStatisticsJmxRegistration#unregister()
   */
  public void register() throws JMException {
    final SessionFactory sessionFactory = ((HibernateEntityManagerFactory) entityManagerFactory).getSessionFactory();

    objectName = new ObjectName(jmxObjectName);

    final StatisticsService statsMBean = new StatisticsService();
    statsMBean.setSessionFactory(sessionFactory);
    statsMBean.setStatisticsEnabled(true);
    mbeanServer.registerMBean(statsMBean, objectName);
  }

  /**
   * Unregisters the MBean that was registered.
   *
   * @throws JMException if the de-registration fails
   * @see HibernateStatisticsJmxRegistration#register()
   */
  public void unregister() throws JMException {
    mbeanServer.unregisterMBean(objectName);
  }

  /**
   * Override the default JMX object name. Obviously you need to call this method before
   * registration for it to have any effect. The string must comply to the rules described in
   * {@link ObjectName}. Suggested is {@code <domain>:name=<name>}.
   *
   * @param jmxObjectName the name to use during registration
   */
  public void setJmxObjectName(String jmxObjectName) {
    this.jmxObjectName = jmxObjectName;
  }
}

Spring configuration

<!-- Setting up Ehcache manager for various caches (offer facade, images). -->
<bean id="ehCacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
  <property name="configLocation" value="classpath:ehcache.xml" />
</bean>  
<ehcache:annotation-driven cache-manager="ehCacheManager" />

<!-- Exposing cache statistics through JMX. -->
<context:mbean-server />
<bean class="net.sf.ehcache.management.ManagementService" init-method="init">
  <constructor-arg ref="ehCacheManager"/>
  <constructor-arg ref="mbeanServer"/>
  <constructor-arg value="true"/>
  <constructor-arg value="true"/>
  <constructor-arg value="true"/>
  <constructor-arg value="true"/>
</bean>    
<bean class="HibernateStatisticsJmxRegistration"
      init-method="register" destroy-method="unregister" />
Marcel Stör
  • 22,695
  • 19
  • 92
  • 198
0

Very ouch, but this works, too, where (guess what) mbeanServer is the bean name of the MBean Server and entityManagerFactory is the bean name of the EntityManagerFactory:

<bean id="managementService-ehcache-hibernate"
    class="net.sf.ehcache.management.ManagementService"
    init-method="init"
    destroy-method="dispose">

    <constructor-arg value="#{T(org.springframework.security.util.FieldUtils).getFieldValue(@entityManagerFactory.cache.unwrap(T(org.hibernate.cache.spi.RegionFactory)),'manager')}"/>
    <constructor-arg ref="mbeanServer"/>
    <constructor-arg index="2" value="true"/>
    <constructor-arg index="3" value="true"/>
    <constructor-arg index="4" value="true"/>
    <constructor-arg index="5" value="true"/>
</bean>
Dr. Hans-Peter Störr
  • 25,298
  • 30
  • 102
  • 139
-1

Spring exposes on JMX only his beans but you can programaticaly export a resource via the MBeanExporter.

Vincent Devillers
  • 1,628
  • 1
  • 11
  • 17