-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 1 post ] 
Author Message
 Post subject: Dynamic auditable pojo
PostPosted: Mon Sep 25, 2006 12:44 pm 
Newbie

Joined: Mon Sep 25, 2006 10:13 am
Posts: 1
First ... sorry for my english .. it could be better ...


I found the Auditable example on the hibernate web site .

i want to use the same principle but without any change in my code .

I found a way to dynamicly mark an hibernate pojo as auditable

Here is the example :

I store the auditable classes names in a list , and pass it to a custom classloader .

This classloader use javaassist to dynamicly extends the Auditable class .

Here is the source .

Code:


/*
* Créé le 25 sept. 2006
*
*/
package fr.generali.gb.commun.hibernate.interceptor;


import fr.generali.gb.commun.log.LogManager;

import org.apache.commons.logging.Log;

import java.util.List;

import javassist.ClassPool;
import javassist.CtClass;


/**
*
* Ce class loader permet de modifier dynamiquement les  pojos hibernate selectionnés
*
* afin de leur ajouter des attributs d'audit .
*
* @author ponthiaux.eric@gmail.com
*
*/

public class AuditableClassLoader extends ClassLoader {


    private List list;

    /** Generali vie framework logger */
   
    private Log classLogger = LogManager.instance ().getLog (this.getClass ());

    /**
     * Creates a new AuditableClassLoader object.
     *
     * @param parent parent class loader
     * @param auditables  list de classe à auditer
     *
     */
   
    public AuditableClassLoader (ClassLoader parent, List auditables) {
       
        super(parent);

        this.list = auditables;


     }

    public Class loadClass (String name) throws ClassNotFoundException {

        if (list.contains (name)) {

            makeAuditable (name);

         }

        return super.loadClass (name);

     }


    public void makeAuditable (String name) {

        try {

            ClassPool pool = ClassPool.getDefault ();

            CtClass cl = ClassPool.getDefault ().get (name);

            if(cl.getSuperclass().getName().equals(Auditable.class.getName()))  {
               
               classLogger.debug("This class already extends auditable !");   
               
               return ;
            }
           
            classLogger.debug ("*********************************************************************************************************");

            classLogger.debug ("DomainObject superclass : " + name);

            classLogger.debug ("Rewriting : " + name);
           
            cl.setSuperclass (ClassPool.getDefault ().get (Auditable.class.getName ()));

            cl.toClass (getParent ());

            classLogger.debug ("Rewriting : " + name + " done ...... ");

            classLogger.debug ("*********************************************************************************************************");

            list.remove (name);

         } catch (Exception ex) {

            ex.printStackTrace ();

         }

     }


    protected Class findClass (String name) throws ClassNotFoundException {

        if (list.contains (name)) {

            makeAuditable (name);

         }

        return super.findClass (name);

     }


   
    protected synchronized Class loadClass (String name, boolean resolve) throws ClassNotFoundException {

        if (list.contains (name)) {

            makeAuditable (name);

         }

        return super.loadClass (name, resolve);

     }


}



I use spring to configure my hibernate session factory .

So i made a custom local session factory to set the classloader

and add the missing auditable property at runtime .


Code:

/*
* Créé le 25 sept. 2006
*
*/

package fr.generali.gb.commun.hibernate.factory;

import java.util.Iterator;
import java.util.List;

import org.apache.commons.logging.Log;
import org.hibernate.Hibernate;
import org.hibernate.HibernateException;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.mapping.Column;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.RootClass;
import org.hibernate.mapping.SimpleValue;
import org.hibernate.mapping.Value;
import org.hibernate.tool.hbm2ddl.SchemaUpdate;
import org.springframework.orm.hibernate3.LocalSessionFactoryBean;

import fr.generali.gb.commun.hibernate.interceptor.AuditableClassLoader;
import fr.generali.gb.commun.log.LogManager;

/**
* @author ponthiaux.eric@gmail.com

*/

public class AuditableLocalSessionFactoryBean extends LocalSessionFactoryBean {

   private Log classLogger = LogManager.instance().getLog(this.getClass());

   private String CREATED_COLUMN_NAME = "CREATED";

   private String LAST_UPDATED_COLUMN_NAME = " LAST_UPDATED";

   private String CREATED_BY_COLUMN_NAME = "CREATED_BY";

   private String UPDATED_BY_COLUMN_NAME = "UPDATED_BY";

   private List auditables;

   protected SessionFactory newSessionFactory(Configuration configuration)  throws HibernateException {

      Iterator iter = configuration.getClassMappings();

      while (iter.hasNext()) {

         RootClass cl = (RootClass) iter.next();

         if (auditables.contains(cl.getClassName())) {
            
            classLogger.info(cl.getClassName() + " marked as auditable !");
            
            addPropertyCreated(cl);

            addPropertyCreatedBy(cl);

            addPropertyLastUpdated(cl);

            addPropertyUpdatedBy(cl);

         }


      }

      SessionFactory factory =  super.newSessionFactory(configuration);
      
      return factory;
   }


   private void addPropertyCreated(RootClass cl) {

      classLogger.info("Adding property created for " + cl.getClassName());

      Property created = new Property();

      created.setInsertable(true);

      created.setName("created");

      created.setPersistentClass(cl);

      created.setNodeName("created");

      created.setPropertyAccessorName("property");

      SimpleValue value = new SimpleValue(cl.getTable());
      
      value.addColumn(new Column(CREATED_COLUMN_NAME));

      value.setTypeName("timestamp");

      created.setValue(value);

      cl.addProperty(created);

   }


   private void addPropertyCreatedBy(RootClass cl) {

      classLogger.info("Adding property created by  for " + cl.getClassName());

      Property createdBy = new Property();

      createdBy.setInsertable(true);

      createdBy.setName("createdBy");

      createdBy.setPersistentClass(cl);

      createdBy.setNodeName("createdBy");

      createdBy.setPropertyAccessorName("property");

      SimpleValue value = new SimpleValue(cl.getTable());

      value.addColumn(new Column(CREATED_BY_COLUMN_NAME));

      value.setTypeName("long");

      createdBy.setValue(value);

      cl.addProperty(createdBy);

   }


   private void addPropertyLastUpdated(RootClass cl) {

      classLogger.info("Adding property lastUpdated   for " + cl.getClassName());

      Property lastUpdated = new Property();

      lastUpdated.setInsertable(true);

      lastUpdated.setName("lastUpdated");

      lastUpdated.setPersistentClass(cl);

      lastUpdated.setNodeName("lastUpdated");

      lastUpdated.setPropertyAccessorName("property");

      SimpleValue value = new SimpleValue(cl.getTable());

      value.addColumn(new Column(LAST_UPDATED_COLUMN_NAME));

      value.setTypeName("timestamp");

      lastUpdated.setValue(value);

      cl.addProperty(lastUpdated);

   }


   private void addPropertyUpdatedBy(RootClass cl) {

      classLogger.info("Adding property lastUpdated   for " + cl.getClassName());

      Property updatedBy = new Property();

      updatedBy.setInsertable(true);

      updatedBy.setName("updatedBy");

      updatedBy.setPersistentClass(cl);

      updatedBy.setNodeName("updatedBy");

      updatedBy.setPropertyAccessorName("property");

      SimpleValue value = new SimpleValue(cl.getTable());

      value.addColumn(new Column(UPDATED_BY_COLUMN_NAME));

      value.setTypeName("long");

      updatedBy.setValue(value);

      cl.addProperty(updatedBy);

   }

   public AuditableLocalSessionFactoryBean(List auditables) {

      this.auditables = auditables;

      AuditableClassLoader classLoader = new AuditableClassLoader(Thread.currentThread().getContextClassLoader(), auditables);

      Thread.currentThread().setContextClassLoader(classLoader);

   }

}



And each time a pojo is created or saved , an hibernate interceptor set the right values if the pojo extends the auditable class

Code:

/*
* Créé le 22 sept. 2006
*
* ponthiaux.eric@gmail.com
*
*
*/
package fr.generali.gb.commun.hibernate.interceptor;


import java.io.Serializable;
import java.sql.Timestamp;
import java.util.Date;
import java.util.Iterator;

import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.logging.Log;
import org.hibernate.CallbackException;
import org.hibernate.EntityMode;
import org.hibernate.Interceptor;
import org.hibernate.Transaction;
import org.hibernate.type.Type;

import fr.generali.gb.commun.log.LogManager;


/**
* Version modifiée de l'empty interceptor hibernate  fournit en exemple sur le site hibernate   
*
* @author ponthiaux.eric@gmail.com
*
*/

public class AcegyInterceptor implements Interceptor, Serializable  {

    private Log classLogger = LogManager.instance ().getLog (this.getClass ());

    /**
     *
     * Creates a new UserInterceptor object.
     *
     */
   
    public AcegyInterceptor () {
       
     }
   

    private Long  getCreatedBy(){
       
       return new Long(0);
       
    }

    private Long  getUpdatedBy(){
       
       return new Long(1);
    }
       

    public void afterTransactionBegin (Transaction tx) {

     }

    public void afterTransactionCompletion (Transaction tx) {

     }


    public void beforeTransactionCompletion (Transaction tx) {

     }


    public int [] findDirty (Object entity, Serializable id, Object currentState[], Object previousState[], String propertyNames[], Type types[]) {

        classLogger.debug ("Enter    public int [] findDirty (Object entity, Serializable id, Object currentState[], Object previousState[], String propertyNames[], Type types[]) ");
       
        if( (entity instanceof Auditable) ) {
           
                  Auditable auditable = ((Auditable)entity);
                  
                  classLogger.debug("Setting last updated attribute in " + entity.getClass().getName());
                  
                  classLogger.debug("Setting updated by  attribute in " + entity.getClass().getName());
                  
                  auditable.setLastUpdated(new Timestamp(new Date().getTime()));
                  
                  auditable.setUpdatedBy(getUpdatedBy());
                  
         }

        classLogger.debug ("Exit      public int [] findDirty (Object entity, Serializable id, Object currentState[], Object previousState[], String propertyNames[], Type types[])  ");

        return null;


     }


    public Object getEntity (String entityName, Serializable id) {

        return null;


     }

    public String getEntityName (Object object) {


        return null;


     }


    public Object instantiate (String entityName, EntityMode entityMode, Serializable id) {

        return null;


     }


    public Boolean isTransient (Object entity) {

        return null;


     }


    public void onCollectionRecreate (Object collection, Serializable key) throws CallbackException {

     }


    public void onCollectionRemove (Object collection, Serializable key) throws CallbackException {

     }


    public void onCollectionUpdate (Object collection, Serializable key) throws CallbackException {

     }


    public void onDelete (Object entity, Serializable id, Object state[], String propertyNames[], Type types[]) {

     }


    public boolean onFlushDirty (Object entity, Serializable id, Object currentState[], Object previousState[], String propertyNames[], Type types[]) {

        if( (entity instanceof Auditable) ) {
           
                  Auditable auditable = ((Auditable)entity);
                  
                  classLogger.debug("Setting last updated attribute in " + entity.getClass().getName());
                  
                  classLogger.debug("Setting updated by  attribute in " + entity.getClass().getName());
                  
                  auditable.setLastUpdated(new Timestamp(new Date().getTime()));
                  
                  auditable.setUpdatedBy(getUpdatedBy());
                  
         }
           
        return false;


     }

    public boolean onLoad (Object entity, Serializable id, Object state[], String propertyNames[], Type types[]) {

        classLogger.debug ("Enter    public boolean onLoad (Object entity, Serializable id, Object state[], String propertyNames[], Type types[])  ");

        if (entity instanceof Auditable) {
           
            classLogger.debug ("loading an instance of auditable  " +ToStringBuilder.reflectionToString(entity));

         }

        classLogger.debug ("Exit   public boolean onLoad (Object entity, Serializable id, Object state[], String propertyNames[], Type types[])  ");

        return false;


     }


    public String onPrepareStatement (String sql) {

        classLogger.debug ("Enter      public String onPrepareStatement (String sql) ");

        classLogger.debug ("Exit     public String onPrepareStatement (String sql)");

        return sql;


     }


    public boolean onSave (Object entity, Serializable id, Object state[], String propertyNames[], Type types[]) {

        classLogger.debug ("Enter     public boolean onSave (Object entity, Serializable id, Object state[], String propertyNames[], Type types[]) ");

        classLogger.debug ("Is instance of Auditable "  + (entity instanceof Auditable));
       
        if( (entity instanceof Auditable) ) {
       
           Auditable auditable = ((Auditable)entity);
          
           if( auditable.getCreated()==null) {
              
              classLogger.debug("Setting created by attribute in " + entity.getClass().getName());
              
              classLogger.debug("Setting created  attribute in " + entity.getClass().getName());
              
              auditable.setCreated(new Timestamp(new Date().getTime()));
              
              auditable.setCreatedBy(getCreatedBy());
              
              auditable.setLastUpdated(new Timestamp(new Date().getTime()));
              
              auditable.setUpdatedBy(getCreatedBy());
              
           } else {
              
              classLogger.debug("Setting last updated attribute in " + entity.getClass().getName());
              
              classLogger.debug("Setting updated by  attribute in " + entity.getClass().getName());
              
              auditable.setLastUpdated(new Timestamp(new Date().getTime()));
              
              auditable.setUpdatedBy(getUpdatedBy());
              
           }
       
        }

        classLogger.debug ("Exit     public boolean onSave (Object entity, Serializable id, Object state[], String propertyNames[], Type types[]) ");

        return false;


     }

    public void postFlush (Iterator entities) {

     }

    public void preFlush (Iterator entities) {

     }

}



The result is that any pojo can now be auditable if the right columns are created in the database .

I have nothing to change in my code .

The spring config look like this :

Code:

<bean id="acegyInterceptor" class="fr.generali.gb.commun.hibernate.interceptor.AcegyInterceptor">
   
   </bean>

   <bean id="hibernateRessource" class="fr.generali.gb.commun.hibernate.factory.AuditableLocalSessionFactoryBean">

      <constructor-arg>
         <list>
            <value>fr.generali.transaction.test.dao.pojo.MyDomainObject</value>
         </list>
      </constructor-arg>

      <property name="configLocation">
         <value>hibernate.cfg.xml</value>
      </property>
      
      <property name="entityInterceptor">
         <ref bean="acegyInterceptor" />
      </property>
   
   </bean>


and the auditable class come from the Hibernate Auditable example

Code:

/*
* Créé le 22 sept. 2006
*
*/

package fr.generali.gb.commun.hibernate.interceptor;

import java.sql.Timestamp;

/**
* @author ponthiaux.eric@gmail.com
*
* Classe hibernate permettant de mettre à jour les infos d'accés a un objet de la base
*
*
*/

public class Auditable  {
   
   private Timestamp lastUpdated ;
   
    private Timestamp created ;
   
    private Long updatedBy ;
   
    private Long createdBy ;
   
    public Timestamp getLastUpdated()                 { return lastUpdated;  }
   
    protected  void setLastUpdated(Timestamp lastUpdated) { this.lastUpdated = lastUpdated; }
   
    public Timestamp getCreated()                     { return created; }
   
    protected  void setCreated(Timestamp created)         { this.created = created; }
   
    public Long getUpdatedBy()                        { return updatedBy; }
   
    protected  void setUpdatedBy(Long updatedBy)          { this.updatedBy = updatedBy; }
   
    public Long getCreatedBy()                        { return createdBy; }
   
    public void setCreatedBy(Long createdBy)          { this.createdBy = createdBy; }
   
    public static boolean isAuditable(Object obj ) {
       
       return (obj instanceof Auditable);
       
    }
   
    public static Auditable toAuditable(Object obj ) {
       
       return (Auditable)obj;
       
    }
   
}



All comments will be apreciated .


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 1 post ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.