com.fortuityframework.hibernate.EventInterceptor.java Source code

Java tutorial

Introduction

Here is the source code for com.fortuityframework.hibernate.EventInterceptor.java

Source

/*
 * Copyright 2009 Jeroen Steenbeeke
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at 
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License. 
 */
package com.fortuityframework.hibernate;

import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import javax.persistence.Entity;
import javax.persistence.MappedSuperclass;

import org.hibernate.CallbackException;
import org.hibernate.EmptyInterceptor;
import org.hibernate.EntityMode;
import org.hibernate.Interceptor;
import org.hibernate.Transaction;
import org.hibernate.type.Type;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.fortuityframework.core.annotation.jpa.FortuityEntity;
import com.fortuityframework.core.annotation.jpa.FortuityProperty;
import com.fortuityframework.core.dispatch.EventBroker;
import com.fortuityframework.core.dispatch.EventException;
import com.fortuityframework.core.dispatch.IEventBroker;
import com.fortuityframework.core.event.Event;
import com.fortuityframework.core.event.jpa.JPAEntityCreateEvent;
import com.fortuityframework.core.event.jpa.JPAEntityDeleteEvent;
import com.fortuityframework.core.event.jpa.JPAEntityLoadEvent;
import com.fortuityframework.core.event.jpa.JPAEntityUpdateEvent;
import com.fortuityframework.core.event.jpa.JPAPreviousValueAwareEntityUpdateEvent;
import com.fortuityframework.core.event.jpa.JPAPreviousValueAwarePropertyChangeEvent;
import com.fortuityframework.core.event.jpa.JPAPropertyChangeEvent;

/**
 * Interceptor to use with Hibernate to create property and entity change events
 * 
 * @author Jeroen Steenbeeke
 * 
 */
public class EventInterceptor implements Interceptor {
    private static final Logger log = LoggerFactory.getLogger(EventInterceptor.class);

    private IEventBroker broker;

    private Interceptor chainedInterceptor;

    /**
     * Creates a new event interceptor
     * 
     * @param broker
     *            The eventbroker to use for dispatching events
     */
    public EventInterceptor(IEventBroker broker) {
        this.broker = broker;
        // Anonymous class to avoid private constructor
        this.chainedInterceptor = new EmptyInterceptor() {
            private static final long serialVersionUID = 1L;
        };
    }

    /**
     * Creates a new event interceptor
     * 
     * @param broker
     *            The eventbroker to use for dispatching events
     * @param chainedInterceptor
     *            An additional interceptor that Hibernate events should be
     *            chained to
     */
    public EventInterceptor(EventBroker broker, Interceptor chainedInterceptor) {
        this.broker = broker;
        this.chainedInterceptor = chainedInterceptor;
    }

    /**
     * @see org.hibernate.EmptyInterceptor#onDelete(java.lang.Object,
     *      java.io.Serializable, java.lang.Object[], java.lang.String[],
     *      org.hibernate.type.Type[])
     */
    @Override
    public void onDelete(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) {
        Class<?> entityClass = entity.getClass();
        FortuityEntity metadata = entityClass.getAnnotation(FortuityEntity.class);

        if (metadata != null) {
            for (Class<? extends JPAEntityDeleteEvent<?>> eventClass : getDeleteEvents(metadata)) {
                dispatchDeleteEvent(entity, eventClass);
            }
        }

        chainedInterceptor.onDelete(entity, id, state, propertyNames, types);
    }

    /**
     * @see org.hibernate.EmptyInterceptor#onSave(java.lang.Object,
     *      java.io.Serializable, java.lang.Object[], java.lang.String[],
     *      org.hibernate.type.Type[])
     */
    @Override
    public boolean onSave(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) {
        Class<?> entityClass = entity.getClass();
        FortuityEntity metadata = entityClass.getAnnotation(FortuityEntity.class);

        if (metadata != null) {
            Class<? extends JPAEntityCreateEvent<?>>[] events = getCreateEvents(metadata);

            for (Class<? extends JPAEntityCreateEvent<?>> eventClass : events) {
                dispatchCreateEvent(entity, eventClass);
            }
        }

        return chainedInterceptor.onSave(entity, id, state, propertyNames, types);
    }

    private Class<? extends JPAEntityCreateEvent<?>>[] getCreateEvents(FortuityEntity metadata) {
        Class<? extends JPAEntityCreateEvent<?>>[] events = (Class<? extends JPAEntityCreateEvent<?>>[]) metadata
                .onCreate();
        return events;
    }

    private Class<? extends JPAEntityUpdateEvent<?>>[] getUpdateEvents(FortuityEntity metadata) {
        Class<? extends JPAEntityUpdateEvent<?>>[] events = (Class<? extends JPAEntityUpdateEvent<?>>[]) metadata
                .onUpdate();
        return events;
    }

    private Class<? extends JPAEntityDeleteEvent<?>>[] getDeleteEvents(FortuityEntity metadata) {
        Class<? extends JPAEntityDeleteEvent<?>>[] events = (Class<? extends JPAEntityDeleteEvent<?>>[]) metadata
                .onDelete();
        return events;
    }

    private Class<? extends JPAEntityLoadEvent<?>>[] getLoadEvents(FortuityEntity metadata) {
        Class<? extends JPAEntityLoadEvent<?>>[] events = (Class<? extends JPAEntityLoadEvent<?>>[]) metadata
                .onLoad();
        return events;
    }

    private Class<? extends JPAPropertyChangeEvent<?>>[] getPropertyUpdateEvents(FortuityProperty metadata) {
        Class<? extends JPAPropertyChangeEvent<?>>[] events = (Class<? extends JPAPropertyChangeEvent<?>>[]) metadata
                .onChange();
        return events;
    }

    /**
     * @see org.hibernate.EmptyInterceptor#onFlushDirty(java.lang.Object,
     *      java.io.Serializable, java.lang.Object[], java.lang.Object[],
     *      java.lang.String[], org.hibernate.type.Type[])
     */
    @Override
    public boolean onFlushDirty(Object entity, Serializable id, Object[] currentState, Object[] previousState,
            String[] propertyNames, Type[] types) {
        Class<?> entityClass = entity.getClass();

        Map<String, Object> oldValues = new HashMap<String, Object>();
        Map<String, Object> newValues = new HashMap<String, Object>();

        for (int i = 0; i < propertyNames.length; i++) {
            if (previousState != null) {
                oldValues.put(propertyNames[i], previousState[i]);
            }
            newValues.put(propertyNames[i], currentState[i]);
        }

        FortuityEntity entityMetadata = entityClass.getAnnotation(FortuityEntity.class);
        if (entityMetadata != null) {
            for (Class<? extends JPAEntityUpdateEvent<?>> eventClass : getUpdateEvents(entityMetadata)) {
                dispatchUpdateEvent(entity, oldValues, newValues, eventClass);
            }
        }

        Class<?> next = entityClass;
        while (next.isAnnotationPresent(Entity.class) || next.isAnnotationPresent(MappedSuperclass.class)) {
            for (Field f : next.getDeclaredFields()) {
                FortuityProperty propertyMetadata = f.getAnnotation(FortuityProperty.class);
                if (propertyMetadata != null) {
                    for (Class<? extends JPAPropertyChangeEvent<?>> eventClass : getPropertyUpdateEvents(
                            propertyMetadata)) {
                        if (oldValues.containsKey(f.getName())) {
                            Object oldValue = oldValues.get(f.getName());
                            Object newValue = newValues.get(f.getName());
                            String fieldName = f.getName();

                            if ((oldValue != null && newValue != null && !oldValue.equals(newValue))
                                    || (oldValue == null && newValue != null)) {
                                dispatchPropertyChangeEvent(entity, eventClass, oldValue, newValue, fieldName);
                            }
                        }
                    }
                }
            }

            next = next.getSuperclass();
        }

        return chainedInterceptor.onFlushDirty(entity, id, currentState, previousState, propertyNames, types);
    }

    /**
     * @param entity
     * @param eventClass
     * @param oldValue
     * @param newValue
     * @param fieldName
     */
    private void dispatchPropertyChangeEvent(Object entity, Class<? extends JPAPropertyChangeEvent<?>> eventClass,
            Object oldValue, Object newValue, String fieldName) {
        try {
            JPAPropertyChangeEvent<?> event;

            if (JPAPreviousValueAwarePropertyChangeEvent.class.isAssignableFrom(eventClass)) {
                Constructor<? extends JPAPropertyChangeEvent<?>> ctor = eventClass.getConstructor(entity.getClass(),
                        String.class, Object.class, Object.class);
                event = ctor.newInstance(entity, fieldName, newValue, oldValue);
            } else {

                Constructor<? extends JPAPropertyChangeEvent<?>> ctor = eventClass.getConstructor(entity.getClass(),
                        String.class, Object.class);

                event = ctor.newInstance(entity, fieldName, newValue);
            }

            broker.dispatchEvent(event);
        } catch (SecurityException e) {
            log.error("Declared event " + eventClass.getName() + " does not have an accessible constructor", e);
            throw new EventException(e);
        } catch (NoSuchMethodException e) {
            log.error("Declared event " + eventClass.getName()
                    + " does not have a proper default constructor. Please extend the default constructor of JPAPropertyChangeEvent or that of JPAPreviousValueAwarePropertyChangeEvent",
                    e);
            throw new EventException(e);
        } catch (IllegalArgumentException e) {
            log.error("Invocation of event constructor failed", e);
            throw new EventException(e);
        } catch (java.lang.InstantiationException e) {
            log.error("Invocation of event constructor failed", e);
            throw new EventException(e);
        } catch (IllegalAccessException e) {
            log.error("Invocation of event constructor failed", e);
            throw new EventException(e);
        } catch (InvocationTargetException e) {
            log.error("Invocation of event constructor failed", e);
            throw new EventException(e);
        }
    }

    /**
     * @param entity
     * @param oldValues
     * @param newValues
     * @param eventClass
     */
    private void dispatchUpdateEvent(Object entity, Map<String, Object> oldValues, Map<String, Object> newValues,
            Class<? extends JPAEntityUpdateEvent<?>> eventClass) {
        try {
            Event<?> event;

            if (JPAPreviousValueAwareEntityUpdateEvent.class.isAssignableFrom(eventClass)) {
                Constructor<? extends JPAEntityUpdateEvent<?>> ctor = eventClass.getConstructor(entity.getClass(),
                        Map.class, Map.class);
                event = ctor.newInstance(entity, newValues, oldValues);
            } else {
                Constructor<? extends JPAEntityUpdateEvent<?>> ctor = eventClass.getConstructor(entity.getClass(),
                        Map.class);
                event = ctor.newInstance(entity, newValues);
            }

            broker.dispatchEvent(event);

        } catch (SecurityException e) {
            log.error("Declared event " + eventClass.getName() + " does not have an accessible constructor", e);
            throw new EventException(e);
        } catch (NoSuchMethodException e) {
            log.error("Declared event " + eventClass.getName()
                    + " does not have a proper default constructor. Please extend the default constructor of JPAEntityUpdateEvent or JPAPreviousValueAwareEntityUpdateEvent",
                    e);
            throw new EventException(e);
        } catch (IllegalArgumentException e) {
            log.error("Invocation of event constructor failed", e);
            throw new EventException(e);
        } catch (java.lang.InstantiationException e) {
            log.error("Invocation of event constructor failed", e);
            throw new EventException(e);
        } catch (IllegalAccessException e) {
            log.error("Invocation of event constructor failed", e);
            throw new EventException(e);
        } catch (InvocationTargetException e) {
            log.error("Invocation of event constructor failed", e);
            throw new EventException(e);
        }
    }

    /**
     * @see org.hibernate.EmptyInterceptor#onLoad(java.lang.Object,
     *      java.io.Serializable, java.lang.Object[], java.lang.String[],
     *      org.hibernate.type.Type[])
     */
    @Override
    public boolean onLoad(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) {
        if (entity != null) {
            Class<?> entityClass = entity.getClass();
            FortuityEntity metadata = entityClass.getAnnotation(FortuityEntity.class);

            if (metadata != null) {
                for (Class<? extends JPAEntityLoadEvent<?>> eventClass : getLoadEvents(metadata)) {
                    dispatchLoadEvent(entity, eventClass);
                }
            }
        }

        return chainedInterceptor.onLoad(entity, id, state, propertyNames, types);
    }

    /**
     * @param entity
     * @param eventClass
     */
    private void dispatchLoadEvent(Object entity, Class<? extends JPAEntityLoadEvent<?>> eventClass) {
        try {
            Constructor<? extends JPAEntityLoadEvent<?>> ctor = eventClass.getConstructor(entity.getClass());
            JPAEntityLoadEvent<?> event = ctor.newInstance(entity);
            broker.dispatchEvent(event);

        } catch (SecurityException e) {
            log.error("Declared event " + eventClass.getName() + " does not have an accessible constructor", e);
            throw new EventException(e);
        } catch (NoSuchMethodException e) {
            log.error("Declared event " + eventClass.getName()
                    + " does not have a proper default constructor. Please extend the default constructor of JPAEntityLoadEvent",
                    e);
            throw new EventException(e);
        } catch (IllegalArgumentException e) {
            log.error("Invocation of event constructor failed", e);
            throw new EventException(e);
        } catch (java.lang.InstantiationException e) {
            log.error("Invocation of event constructor failed", e);
            throw new EventException(e);
        } catch (IllegalAccessException e) {
            log.error("Invocation of event constructor failed", e);
            throw new EventException(e);
        } catch (InvocationTargetException e) {
            log.error("Invocation of event constructor failed", e);
            throw new EventException(e);
        }
    }

    private void dispatchDeleteEvent(Object entity, Class<? extends JPAEntityDeleteEvent<?>> eventClass) {
        try {
            Constructor<? extends JPAEntityDeleteEvent<?>> ctor = eventClass.getConstructor(entity.getClass());
            JPAEntityDeleteEvent<?> event = ctor.newInstance(entity);
            broker.dispatchEvent(event);

        } catch (SecurityException e) {
            log.error("Declared event " + eventClass.getName() + " does not have an accessible constructor", e);
            throw new EventException(e);
        } catch (NoSuchMethodException e) {
            log.error("Declared event " + eventClass.getName()
                    + " does not have a proper default constructor. Please extend the default constructor of JPAEntityDeleteEvent",
                    e);
            throw new EventException(e);
        } catch (IllegalArgumentException e) {
            log.error("Invocation of event constructor failed", e);
            throw new EventException(e);
        } catch (java.lang.InstantiationException e) {
            log.error("Invocation of event constructor failed", e);
            throw new EventException(e);
        } catch (IllegalAccessException e) {
            log.error("Invocation of event constructor failed", e);
            throw new EventException(e);
        } catch (InvocationTargetException e) {
            log.error("Invocation of event constructor failed", e);
            throw new EventException(e);
        }
    }

    private <T> void dispatchCreateEvent(Object entity, Class<? extends JPAEntityCreateEvent<?>> eventClass) {
        try {
            Constructor<? extends JPAEntityCreateEvent<?>> ctor = eventClass.getConstructor(entity.getClass());

            JPAEntityCreateEvent<?> event = ctor.newInstance(entity);
            broker.dispatchEvent(event);

        } catch (SecurityException e) {
            log.error("Declared event " + eventClass.getName() + " does not have an accessible constructor", e);
            throw new EventException(e);
        } catch (NoSuchMethodException e) {
            log.error("Declared event " + eventClass.getName()
                    + " does not have a proper default constructor. Please extend the default constructor of JPAEntityCreateEvent",
                    e);
            throw new EventException(e);
        } catch (IllegalArgumentException e) {
            log.error("Invocation of event constructor failed", e);
            throw new EventException(e);
        } catch (java.lang.InstantiationException e) {
            log.error("Invocation of event constructor failed", e);
            throw new EventException(e);
        } catch (IllegalAccessException e) {
            log.error("Invocation of event constructor failed", e);
            throw new EventException(e);
        } catch (InvocationTargetException e) {
            log.error("Invocation of event constructor failed", e);
            throw new EventException(e);
        }
    }

    /**
     * @see org.hibernate.Interceptor#afterTransactionBegin(org.hibernate.Transaction)
     */
    @Override
    public void afterTransactionBegin(Transaction tx) {
        chainedInterceptor.afterTransactionBegin(tx);
    }

    /**
     * @see org.hibernate.Interceptor#afterTransactionCompletion(org.hibernate.Transaction)
     */
    @Override
    public void afterTransactionCompletion(Transaction tx) {
        chainedInterceptor.afterTransactionCompletion(tx);
    }

    /**
     * @see org.hibernate.Interceptor#beforeTransactionCompletion(org.hibernate.Transaction)
     */
    @Override
    public void beforeTransactionCompletion(Transaction tx) {
        chainedInterceptor.beforeTransactionCompletion(tx);
    }

    /**
     * @see org.hibernate.Interceptor#findDirty(java.lang.Object,
     *      java.io.Serializable, java.lang.Object[], java.lang.Object[],
     *      java.lang.String[], org.hibernate.type.Type[])
     */
    @Override
    public int[] findDirty(Object entity, Serializable id, Object[] currentState, Object[] previousState,
            String[] propertyNames, Type[] types) {
        return chainedInterceptor.findDirty(entity, id, currentState, previousState, propertyNames, types);
    }

    /**
     * @see org.hibernate.Interceptor#getEntity(java.lang.String,
     *      java.io.Serializable)
     */
    @Override
    public Object getEntity(String entityName, Serializable id) throws CallbackException {
        return chainedInterceptor.getEntity(entityName, id);
    }

    /**
     * @see org.hibernate.Interceptor#getEntityName(java.lang.Object)
     */
    @Override
    public String getEntityName(Object object) throws CallbackException {
        return chainedInterceptor.getEntityName(object);
    }

    /**
     * @see org.hibernate.Interceptor#instantiate(java.lang.String,
     *      org.hibernate.EntityMode, java.io.Serializable)
     */
    @Override
    public Object instantiate(String entityName, EntityMode entityMode, Serializable id) throws CallbackException {
        Object entity = chainedInterceptor.instantiate(entityName, entityMode, id);

        return entity;
    }

    /**
     * @see org.hibernate.Interceptor#isTransient(java.lang.Object)
     */
    @Override
    public Boolean isTransient(Object entity) {
        return chainedInterceptor.isTransient(entity);
    }

    /**
     * @see org.hibernate.Interceptor#onCollectionRecreate(java.lang.Object,
     *      java.io.Serializable)
     */
    @Override
    public void onCollectionRecreate(Object collection, Serializable key) throws CallbackException {
        chainedInterceptor.onCollectionRecreate(collection, key);
    }

    /**
     * @see org.hibernate.Interceptor#onCollectionRemove(java.lang.Object,
     *      java.io.Serializable)
     */
    @Override
    public void onCollectionRemove(Object collection, Serializable key) throws CallbackException {
        chainedInterceptor.onCollectionRemove(collection, key);
    }

    /**
     * @see org.hibernate.Interceptor#onCollectionUpdate(java.lang.Object,
     *      java.io.Serializable)
     */
    @Override
    public void onCollectionUpdate(Object collection, Serializable key) throws CallbackException {
        chainedInterceptor.onCollectionUpdate(collection, key);
    }

    /**
     * @see org.hibernate.Interceptor#onPrepareStatement(java.lang.String)
     */
    @Override
    public String onPrepareStatement(String sql) {
        return chainedInterceptor.onPrepareStatement(sql);
    }

    /**
     * @see org.hibernate.Interceptor#postFlush(java.util.Iterator)
     */

    @Override
    public void postFlush(@SuppressWarnings("rawtypes") Iterator entities) throws CallbackException {
        chainedInterceptor.postFlush(entities);
    }

    /**
     * @see org.hibernate.Interceptor#preFlush(java.util.Iterator)
     */
    @Override
    public void preFlush(@SuppressWarnings("rawtypes") Iterator entities) throws CallbackException {
        chainedInterceptor.preFlush(entities);
    }

}