Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to intercept all Hibernate sessions when they're created (Spring / Grails environment)

Is there a way of intercepting all new Hibernate sessions when they're created? I need to access each Session instance to enable a Hibernate filter with a parameter.

The only solution I've gotten working has involved wrapping the SessionFactory, but this involved a lot of semi nasty hacks as well as it required me to implement around 60 methods, where only a few are interesting.

Hibernate's SessionFactory implementation is for some annoying reason declared final so extending it is not an option. I've also tried aspects and Java proxies without any luck.

like image 393
Kimble Avatar asked Dec 22 '22 00:12

Kimble


1 Answers

I was able to create a JDK proxy:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Date;

import org.hibernate.SessionFactory;
import org.hibernate.engine.SessionFactoryImplementor;

public class SessionFactoryProxyCreator {

   public static SessionFactory instance;

   public static SessionFactory createProxy(final SessionFactory realSessionFactory) {
      ClassLoader cl = SessionFactory.class.getClassLoader();
      Class<?>[] interfaces = new Class[] { SessionFactory.class, SessionFactoryImplementor.class };
      instance = (SessionFactory)Proxy.newProxyInstance(cl, interfaces, new InvocationHandler() {
         public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

            if ("openSession".equals(method.getName())) {
               System.out.println("NEW SESSION AT " + new Date());
            }

            return method.invoke(realSessionFactory, args);
         }
      });

      return instance;
   }
}

and you would call this from a custom SessionFactoryBean:

import org.codehaus.groovy.grails.orm.hibernate.ConfigurableLocalSessionFactoryBean;
import org.hibernate.HibernateException;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class MyConfigurableLocalSessionFactoryBean extends ConfigurableLocalSessionFactoryBean {

   public MyConfigurableLocalSessionFactoryBean() {
      setCurrentSessionContextClass(MyCurrentSessionContext.class);
   }

   @Override
   protected SessionFactory buildSessionFactory() throws Exception {
      setExposeTransactionAwareSessionFactory(false);
      return SessionFactoryProxyCreator.createProxy(super.buildSessionFactory());
   }

   @Override
   protected SessionFactory newSessionFactory(Configuration config) throws HibernateException {
      setExposeTransactionAwareSessionFactory(false);
      return SessionFactoryProxyCreator.createProxy(super.newSessionFactory(config));
   }
}

which depends on a modified version of Spring's SpringSessionContext that uses the proxy instead of the real session factory:

import org.hibernate.HibernateException;
import org.hibernate.classic.Session;
import org.hibernate.context.CurrentSessionContext;
import org.hibernate.engine.SessionFactoryImplementor;
import org.springframework.orm.hibernate3.SessionFactoryUtils;

public class MyCurrentSessionContext implements CurrentSessionContext {

   public MyCurrentSessionContext(SessionFactoryImplementor sessionFactory) {
      // ignore the real sessionFactory, need to use the proxy
   }

   public Session currentSession() throws HibernateException {
      try {
         return (org.hibernate.classic.Session)SessionFactoryUtils.doGetSession(
               SessionFactoryProxyCreator.instance, false);
      }
      catch (IllegalStateException e) {
         throw new HibernateException(e.getMessage());
      }
   }
}

This needs to be registered in resources.groovy to replace the standard Grails ConfigurableLocalSessionFactoryBean:

import org.codehaus.groovy.grails.commons.ApplicationHolder as AH
import org.codehaus.groovy.grails.orm.hibernate.events.PatchedDefaultFlushEventListener

beans = {

   sessionFactory(MyConfigurableLocalSessionFactoryBean) {

      def ds = AH.application.config.dataSource
      def hibConfig = AH.application.config.hibernate

      dataSource = ref('dataSource')
      List hibConfigLocations = []
      if (AH.application.classLoader.getResource('hibernate.cfg.xml')) {
         hibConfigLocations << 'classpath:hibernate.cfg.xml'
      }
      def explicitLocations = hibConfig?.config?.location
      if (explicitLocations) {
         if (explicitLocations instanceof Collection) {
            hibConfigLocations.addAll(explicitLocations.collect { it.toString() })
         }
         else {
            hibConfigLocations << hibConfig.config.location.toString()
         }
      }
      configLocations = hibConfigLocations
      if (ds?.configClass) {
         configClass = ds.configClass
      }
      hibernateProperties = ref('hibernateProperties')
      grailsApplication = ref('grailsApplication', true)
      lobHandler = ref('lobHandlerDetector')
      entityInterceptor = ref('entityInterceptor')
      eventListeners = ['flush': new PatchedDefaultFlushEventListener(),
                        'pre-load':    ref('eventTriggeringInterceptor'),
                        'post-load':   ref('eventTriggeringInterceptor'),
                        'save':        ref('eventTriggeringInterceptor'),
                        'save-update': ref('eventTriggeringInterceptor'),
                        'post-insert': ref('eventTriggeringInterceptor'),
                        'pre-update':  ref('eventTriggeringInterceptor'),
                        'post-update': ref('eventTriggeringInterceptor'),
                        'pre-delete':  ref('eventTriggeringInterceptor'),
                        'post-delete': ref('eventTriggeringInterceptor')]
   }
}
like image 152
Burt Beckwith Avatar answered Apr 09 '23 20:04

Burt Beckwith