HIBERNATE JBoss.org
 |  Register  | 
     
News 
About 
   Feature List 
   Road Map 
Documentation 
   Related Projects 
   External Documentation 
Download 
Forum & Mailinglists 
Support & Training 
JIRA Issue Tracking
Wiki Community Area


Hibernate Public Training Courses


Get Hibernate in Action eBook!


JavaWorld 2003 Finalist


Jolt Award 2004 Winner
      
Documentation > Community Area > Chained Interceptor

Chained Interceptor

The Interceptor interface allows the registration of a custom object that gets called by Hibernate at some specific point when the application is running.

While this allows some flexibility, we can go one step further by allowing several interceptor instances to be chained together, forming what can be called a Chained Interceptor. Each interceptor can deal with a specific problem (eg auditing, logging, security, ...), and the chain transparently propagates the different callbacks to each instance.

The main problem with this approach is that while some callbacks are well suited for such an approach (basically, the ones returning a boolean / null (such as onLoad(), on Save(), preFlush(), postFlush()...)), there are other methods such as instantiate() or findDirty() which would be more difficult to integrate. A simple solution to these problematic cases is to simply have the first interceptor who returns something valid (that is, something not null) win.

The following code is an example of such a ChainedInterceptor. Note that this ChainedInterceptor implements the Interceptor interface so that it can be registered by the Hibernate framework. The interceptor to be chained are passed as an array in the constructor or using a setter.

Updated 06/12/2003 : added CallbackException

Updated 06/19/2003 : fixed typo in onDelete: changed onSave() to onDelete()

import java.io.Serializable;
import java.util.Iterator;
import net.sf.hibernate.Interceptor;
import net.sf.hibernate.type.Type;

/**
 * Implementation of the Hibernate <code>Interceptor</code> 
 * interface that allows the chaining of several different
 * instances of the same interface.
 * 
 * @author Laurent RIEU
 * @see Interceptor
 */
public class ChainedInterceptor implements Interceptor {
    // Interceptors to be chained
    private Interceptor[] interceptors;

    /**
     * Constructor 
     */
    public ChainedInterceptor() {
        super();
    }

    /**
     * Constructor
     * @param An array of interceptors 
     */
    public ChainedInterceptor(Interceptor[] interceptors) {
        super();
        this.interceptors = interceptors;
    }

    /**
     * @see net.sf.hibernate.Interceptor#onLoad(java.lang.Object, 
     * java.io.Serializable, java.lang.Object[], java.lang.String[], 
     * net.sf.hibernate.type.Type[])
     */
    public boolean onLoad(
        Object entity,
        Serializable id,
        Object[] state,
        String[] propertyNames,
        Type[] types) throws CallbackException {
        boolean result = false;
        for (int i = 0; i < interceptors.length; i++) {
            if (interceptors[i]
                .onLoad(entity, id, state, propertyNames, types)) {
                /* Returns true if one interceptor in the chain has 
                 * modified the object state result = true;
                 */
            }
        }
        return result;
    }

    /**
     * @see net.sf.hibernate.Interceptor#onFlushDirty(java.lang.Object, 
     * java.io.Serializable, java.lang.Object[], java.lang.Object[], 
     * java.lang.String[], net.sf.hibernate.type.Type[])
     */
    public boolean onFlushDirty(
        Object entity,
        Serializable id,
        Object[] currentState,
        Object[] previousState,
        String[] propertyNames,
        Type[] types) throws CallbackException {
        boolean result = false;
        for (int i = 0; i < interceptors.length; i++) {
            if (interceptors[i]
                .onFlushDirty(
                    entity,
                    id,
                    currentState,
                    previousState,
                    propertyNames,
                    types)) {
                /* Returns true if one interceptor in the chain has modified 
                 * the object current state result = true;
                 */
            }
        }
        return result;
    }

    /**
     * @see net.sf.hibernate.Interceptor#onSave(java.lang.Object, 
     * java.io.Serializable, java.lang.Object[], java.lang.String[], 
     * net.sf.hibernate.type.Type[])
     */
    public boolean onSave(
        Object entity,
        Serializable id,
        Object[] state,
        String[] propertyNames,
        Type[] types) throws CallbackException {
        boolean result = false;
        for (int i = 0; i < interceptors.length; i++) {
            if (interceptors[i]
                .onSave(entity, id, state, propertyNames, types)) {
                /* Returns true if one interceptor in the chain has
                 * modified the object state result = true;
                 */
            }
        }
        return result;
    }

    /**
     * @see net.sf.hibernate.Interceptor#onDelete(java.lang.Object, 
     * java.io.Serializable, java.lang.Object[], java.lang.String[], 
     * net.sf.hibernate.type.Type[])
     */
    public void onDelete(
        Object entity,
        Serializable id,
        Object[] state,
        String[] propertyNames,
        Type[] types) throws CallbackException {
        for (int i = 0; i < interceptors.length; i++) {
            interceptors[i].onDelete(entity, id, state, propertyNames, types);
        }
    }

    /**
     * @see net.sf.hibernate.Interceptor#preFlush(java.util.Iterator)
     */
    public void preFlush(Iterator entities) throws CallbackException {
        for (int i = 0; i < interceptors.length; i++) {
            interceptors[i].preFlush(entities);
        }
    }

    /**
     * @see net.sf.hibernate.Interceptor#postFlush(java.util.Iterator)
     */
    public void postFlush(Iterator entities) throws CallbackException {
        for (int i = 0; i < interceptors.length; i++) {
            interceptors[i].postFlush(entities);
        }
    }

    /**
     * @see net.sf.hibernate.Interceptor#isUnsaved(java.lang.Object)
     */
    public Boolean isUnsaved(Object entity) {
        Boolean result = null;
        for (int i = 0; i < interceptors.length; i++) {
            result = interceptors[i].isUnsaved(entity);
            if (result != null) {
                // If any interceptor has returned either true or false, stop the chain
                break;
            }
        }
        return result;
    }

    /**
     * @see net.sf.hibernate.Interceptor#findDirty(java.lang.Object, 
     * java.io.Serializable, java.lang.Object[], java.lang.Object[], 
     * java.lang.String[], net.sf.hibernate.type.Type[])
     */
    public int[] findDirty(
        Object entity,
        Serializable id,
        Object[] currentState,
        Object[] previousState,
        String[] propertyNames,
        Type[] types) {
        int[] result = null;
        for (int i = 0; i < interceptors.length; i++) {
            result =
                interceptors[i].findDirty(
                    entity,
                    id,
                    currentState,
                    previousState,
                    propertyNames,
                    types);
            if (result != null) {
                /* If any interceptor has returned something not null,
                 * stop the chain
                 */
                break;
            }
        }
        return result;
    }

    /**
     * @see net.sf.hibernate.Interceptor#instantiate(java.lang.Class, 
     * java.io.Serializable)
     */
    public Object instantiate(Class clazz, Serializable id) throws CallbackException {
        Object result = null;
        for (int i = 0; i < interceptors.length; i++) {
            result = interceptors[i].instantiate(clazz, id);
            if (result != null) {
                /* If any interceptor has returned something not null,
                 * stop the chain
                 */
                break;
            }
        }
        return result;
    }

    /**
     * Returns an array containing the instances of the <code>Interceptor</code> 
     * interface that are chained within this interceptor.
     * @return An array of interceptor
     */
    public Interceptor[] getInterceptors() {
        return interceptors;
    }

    /**
     * Sets  the instances of the <code>Interceptor</code> interface
     * that are chained within this interceptor.
     * @param interceptors
     */
    public void setInterceptors(Interceptor[] interceptors) {
        this.interceptors = interceptors;
    }

}
      

coWiki