Tuesday, June 22, 2010

Enabling JMX Monitoring for Spring-JPA-Hibernate Application

Requirement: Be sure that the application correctly utilizing second level caching by using ehcache. And what is the cache utilization level for the application?
Technologies that is used in the appilcation: JPA/Hibernate, Spring 2.5, JSF+Richfaces.
After some googling I understood that the only way to get caching utilization is to monitor it by JMX.  My collegue Ray McDermott came to the resque and sent me the article:
which I inspired form.
I assume that you have already a similar application. So I will not go through the how you can create such kind of project. If you want to create an application from scratch please read the article above.
To enable caching statistics in your Spring-JPA/Hibernate application first set the hibernate parameters in persistence.xml  like:
<property name="hibernate.cache.provider_class" value="net.sf.ehcache.hibernate.SingletonEhCacheProvider"/>
<property name="hibernate.cache.use_query_cache" value="true"/>
<property name="hibernate.jdbc.wrap_result_sets" value="true"/>
<property name="hibernate.cache.use_minimal_puts" value="false" />
<property name="hibernate.cache.use_second_level_cache" value="true" />
<property name="hibernate.cache.use_structured_entries" value="true" />
<property name="hibernate.generate_statistics" value="true" />
Second, we need configure JMX Monitoring aspects:
Simply we need to create two aspects:
  • JMX MBean - which can represent a device, an application, or any resource that needs to be managed.
  • JMX Agent - which is an application that registers MBean(s) with a MBeanServer, e.g. the platform MBeanServer.

In this case we do not need to create JMX Mbean since it will be provided by Hibernate.
We need a JMX Agent:
import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.persistence.EntityManagerFactory;

import net.sf.ehcache.CacheManager;
import net.sf.ehcache.management.ManagementService;

import org.hibernate.SessionFactory;
import org.hibernate.ejb.HibernateEntityManagerFactory;
import org.hibernate.jmx.StatisticsService;

public class JmxAgent {

      private EntityManagerFactory entityManagerFactory;
      private MBeanServer mbs;

      public void init() throws Exception {

            SessionFactory sf = ((HibernateEntityManagerFactory) entityManagerFactory)
                        .getSessionFactory();

            ObjectName on = new ObjectName(
                        "Hibernate:type=statistics,application=flrs-web");

            StatisticsService statsMBean = new StatisticsService();
            statsMBean.setSessionFactory(sf);
            statsMBean.setStatisticsEnabled(true);
            mbs.registerMBean(statsMBean, on);

            CacheManager cacheMgr = CacheManager.getInstance();
            ManagementService.registerMBeans(cacheMgr, mbs, true, true, true, true);
      }

      public MBeanServer getMBeanServer() {
            return mbs;
      }

      public void setEntityManagerFactory(
                  EntityManagerFactory entityManagerFactory) {
            this.entityManagerFactory = entityManagerFactory;
      }

      public void setMbs(MBeanServer mbs) {
            this.mbs = mbs;
      }

}
JMXAgent is Spring aware bean and init() is the initializing method for that bean.  JMXAgent has two properties:
MbeanServer ‘mbs’ and EntityManagerFactory  ‘entityManagerFactory’ which are injected by the spring during appilcation context creation.
Here is the Spring configuration of JMXAgent:
<bean id="mbeanServer" class="java.lang.management.ManagementFactory"
      factory-method="getPlatformMBeanServer" />
     
<bean id="jmxAgent" class="com.xxx.flrs.view.utility.JmxAgent" init-method="init">
      <property name="entityManagerFactory" ref="entityManagerFactory"/>
      <property name="mbs" ref="mbeanServer"/>
bean>
That’s all you need to do in the application.
After you complete the application modification, deploy the JMX enabled version in your application server. I tried in Glassfish v2.1.1. But no matter which application server you used. Just be sure that if JMX is enabled for your application server.
And finally here is the funniest part, Monitor hibernate statistics:
To do that first open Jconsole. If %JAVA_HOME% /bin folder is in you path, just open command prompt and type jconsole. You will be asked for a connection:

Find the Advanced tab and type the JMX Url of your application server. Username and Password is the same pair that you login administration console for Glassfish.
I tried other Local and Remote connection ways but i was not sucessfull.
If everything goes right, you should see the window:

Find the Mbeans tab and here is the Hibernate Statistics:





Thank You

Create Custom JNDI URL Resource Provider for Glassfish

Again very simple requirement, again Glassfish and again it cannot be satisfied by default. May be my requirement is not a part of reference implementation :))

Anyway, one of my collegue requested that "put the application properties file outside of the application and look up the property file from JNDI". First I thought that it should be very easy since we define JDBC Datasource zillion times and how can it be different?
Answer is; it is totally different in Glassfish!

Bad New: You need to develop your own ObjectFactory implemantation for URL resources.
Good New: It is so simple to do.

Let me summarize how you can do that:

  1. Create a java project in your favorite IDE. Name can be GlassfishUrlFactory
  2. Create the package that you will put the "javax.naming.spi.ObjectFactory" implementation. Suggestion com.xxx.glassfish.jndi.resource 
  3. Create the ObjectFactory implementation class. Source code looks like:
package com.xxx.glassfish.urlfactory;
import java.net.URL;
import java.util.Hashtable;

import javax.naming.Context;
import javax.naming.Name;
import javax.naming.Reference;
import javax.naming.spi.ObjectFactory;

public class GlassfishUrlFactory implements ObjectFactory {

    
    
    public Object getObjectInstance(Object obj, Name name, Context nameCtx,
            Hashtable environment) throws Exception {

        Reference ref = (Reference) obj;
        String propertyFileUrl=null;
       
        if(ref!=null){
            propertyFileUrl=ref.get("fileUrl").getContent().toString();
        }else{
            throw new Exception("Please Add 'fileUrl' property to JNDI Resource Definition!!");
        }
       
        URL url = new URL(propertyFileUrl);
        return url;
    }
    
    
}

    
Reference is something similar to java.util.Map. It is used to get the parameters that you pass when you define JNDI Resource in your application server. For example imagine that you defined a JDBC datasource and set the username property from the admin console. JDBC Factory class will get it like 

username=ref.get("username").getContent().toString();



       4. Export the project as JAR file. 

       5. Put the JAR file under Glassfish lib folder.
      6. Define a Custom JNDI Resource in Glassfish

       7. Restart the Application Server.

     Here is a sample code how can you look up properties file from Spring: