4 Replies Latest reply on Apr 2, 2013 9:13 AM by ibenjes

    How do I configure a Hibernate Interceptor in Seam?

    jakec

      I am trying to implement Hibernate db auditing like http://www.hibernate.org/48.html (but adding field, oldValue, and newValue), and I need to create the Session with my custom Hibernate Interceptor, which is given the User ID in the constructor.

      The only place in Seam I see any version of SessionFactory.openSession() being used is in ManagedHibernateSession.initSession(), but I don't really see how to get my Interceptor in there. I could extend ManagedHibernateSession and inject currentUser, but how do I configure Seam to use my extended class? I see the Portal sample app pointing to ManagedHibernateSession in components.xml, but we are using EntityManager, with managed-persistence-context declared in components.xml, not Session.

      How do I configure a Hibernate Interceptor in Seam?

      Or, is there a better way to do Audit logging in Seam? At first I was looking at using the @EntityListeners annotation, but it would involve lots of Reflection, and there doesn't seem to be a way to get at the previous values of the Entity's properties anyway.

      org.hibernate.Interceptor.onFlushDirty() gives me all that (too bad it doesn't also give me boolean isDirty[]), but I don't see how to get it configured within Seam.

        • 1. Re: How do I configure a Hibernate Interceptor in Seam?

          I also needed access to the Configuration object (for a different reason), and I worked around it by extending HibernateSessionFactory, overriding its createSessionFactory method, and putting the original code of the method in there along with my modifications:

          @Scope(ScopeType.APPLICATION)
          @BypassInterceptors
          @Startup
          public class SmartHibernateSessionFactory extends HibernateSessionFactory {
          
           @SuppressWarnings("unchecked")
           protected SessionFactory createSessionFactory() throws ClassNotFoundException {
           ...
           configuration.setInterceptor(...)
           ...
           }
          }
          


          Now, this is definitely a hack: there's code duplication involved, and if a new version of Seam changes anything in createSessionFactory you'll need to remember to carry over the changes. My suggestion to the Seam team would be to add an empty method (maybe called furtherConfiguration) into HibernateSessionFactory and then change the last line in createSessionFactory to
          return furtherConfiguration(configuration.buildSessionFactory());
          . That way one could easily change the configuration object without all the maintenance headaches.

          • 2. Re: How do I configure a Hibernate Interceptor in Seam?
            jakec

            That might work, if I inject currentUser into the Interceptor via @In instead of passing it in through a constructor.

            However, I agree that it is a hack. I think I'd rather compare values through reflection and introspection in an @EntityListeners class than get a Hibernate Interceptor in through the back door like that.

            • 3. Re: How do I configure a Hibernate Interceptor in Seam?
              tammy_mc

              This worked for me. I did have to override and replace the whole HibernateSessionFactory.createSessionFactory() method (which means we HAVE to remember this if we ever update our Seam version).

              I added only one single line of code there, right above HibernateSessionFactory.createSessionFactory() call:

                  configuration.setInterceptor(new HibernateSecurityInterceptor(configuration.getInterceptor()));

              I added our own AuditInterceptor so it could be wrapped by HibernateSecurityInterceptor:

                  configuration.setInterceptor(new AuditInterceptor()); // This is all we added.

               

              Then in components.xml I made changes to use our new AuditableSessionFactory:

                 <persistence:hibernate-session-factory name="auditableSessionFactory" cfg-resource-name="hibernate.cfg.xml" auto-create="true" />

                 <persistence:managed-hibernate-session name="hibernateSession" auto-create="true" session-factory="#{auditableSessionFactory}"/>

               

              The 2 classes I wrote are AuditInterceptor and AuditableSessionFactory

              AuditableSessionFactory extends HibernateSessionFactory and just overrides the one method createSessionFactory() replacing the single line as I describe above.

              AuditableSessionFactory has to have the following Seam annotations declared on it as well:

              @Scope(ScopeType.APPLICATION)

              @Install(precedence=Install.DEPLOYMENT)

              @BypassInterceptors

              @Name("auditableSessionFactory")

              @Startup

              public class AuditableSessionFactory extends HibernateSessionFactory

               

              The @Install annotation increases the precedence of my AuditableSessionFactory so Seam chooses to use it over the HibernateSessionFactory which is also annotated to be created at app startup.

               

              And AuditInterceptor extends EmptyInterceptor and then just overrides the methods I am interested in for updating some properties onFlushDirty (update) and onSave (new).

              • 4. Re: How do I configure a Hibernate Interceptor in Seam?
                ibenjes

                Just enabling a Hibernate Interceptor (per Session Factory) in Seam is easy, you don't have to overwrite the HibernateSessionFactory.

                 

                In the persistence.xml file you just add

                 

                <property name="hibernate.ejb.interceptor" value="com.yourcompany.hibernate.HibernateInterceptor" />

                 

                The problem is how do you enable a 'per Session' Hibernate Interceptor in Seam?