py.una.pol.karaku.dao.entity.interceptors.InterceptorHandler.java Source code

Java tutorial

Introduction

Here is the source code for py.una.pol.karaku.dao.entity.interceptors.InterceptorHandler.java

Source

/*-
 * Copyright (c)
 *
 *       2012-2014, Facultad Politcnica, Universidad Nacional de Asuncin.
 *       2012-2014, Facultad de Ciencias Mdicas, Universidad Nacional de Asuncin.
 *       2012-2013, Centro Nacional de Computacin, Universidad Nacional de Asuncin.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301  USA
 */
package py.una.pol.karaku.dao.entity.interceptors;

import static py.una.pol.karaku.util.Checker.notNull;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.Nonnull;
import javax.persistence.CascadeType;
import javax.persistence.OneToMany;
import javax.persistence.Transient;
import org.hibernate.Hibernate;
import org.slf4j.Logger;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.ReflectionUtils.FieldCallback;
import py.una.pol.karaku.dao.entity.Operation;
import py.una.pol.karaku.log.Log;
import py.una.pol.karaku.util.ListHelper;

/**
 * 
 * @author Arturo Volpe
 * @since 1.0
 * @version 1.0 Oct 1, 2013
 * 
 */
@Component
public class InterceptorHandler implements InitializingBean {

    @Log
    private transient Logger log;

    @Autowired
    private List<Interceptor> interceptors;

    private transient int interceptorsCount;

    private Map<Class<?>, Set<Interceptor>> byType;
    private Map<Class<?>, Set<Interceptor>> byAnnotation;

    /**
     * {@inheritDoc}
     */
    @Override
    public void afterPropertiesSet() {

        this.log.info("Building InterceptorHandler");
        this.interceptorsCount = interceptors.size();
        for (Interceptor bi : interceptors) {
            this.log.info("Add: {}", bi.getClass().getSimpleName());
            for (Class<?> type : bi.getObservedTypes()) {
                // Cuando type == null, entonces se agregara al grupo de
                // interceptors asociados con void.class!.
                this.getInterceptorsByType(type).add(bi);
            }
            for (Class<?> annon : bi.getObservedAnnotations()) {
                // Cuando annon == null, entonces se agregara al grupo de
                // interceptors asociados con la anotacin void.class (osea
                // todas).
                this.getInterceptorsByAnnotation(annon).add(bi);
            }
        }

    }

    /**
     * Intercepta un bean especifico.
     * 
     * <p>
     * Intercepta todos los campos de un objeto, buscando aquellos que tengan
     * algn interceptor definido.
     * </p>
     * <p>
     * Reglas:
     * <ol>
     * <li>Si el item es un atributo normal, invocar a su respectivo
     * interceptor.
     * </p>
     * <li>Si es una coleccin, y tiene tiene la anotacin {@link OneToMany}, y
     * su {@link CascadeType} es {@link CascadeType#ALL}, entonces se propaga la
     * intercepcin a los miembros de la coleccin. </p>
     * 
     * @param op
     *            Operacin actual.
     * @param bean
     *            objeto que esta siendo interceptado.
     */
    public void intercept(@Nonnull final Operation op, final Object bean) {

        ReflectionUtils.doWithFields(bean.getClass(), new FieldCallback() {

            @Override
            public void doWith(Field field) throws IllegalAccessException {

                InterceptorHandler.this.intercept(op, notNull(field), bean);
            }
        });
    }

    /**
     * Intercepta un atributo de un bean especifico.
     * 
     * <p>
     * Reglas:
     * <ol>
     * <li>Si el item es un atributo normal, invocar a su respectivo
     * interceptor.
     * </p>
     * <li>Si es una coleccin, y tiene tiene la anotacin {@link OneToMany}, y
     * su {@link CascadeType} es {@link CascadeType#ALL}, entonces se propaga la
     * intercepcin a los miembros de la coleccin. </p>
     * 
     * @param op
     *            Operacin actual.
     * @param field
     *            campo sobre el cual se esta ejecutando.
     * @param bean
     *            objeto que esta siendo interceptado.
     */
    private void intercept(@Nonnull Operation op, @Nonnull Field field, @Nonnull Object bean) {

        if (field.getAnnotation(OneToMany.class) != null) {
            OneToMany otm = field.getAnnotation(OneToMany.class);
            CascadeType[] cascade = otm.cascade();
            if (cascade != null && ListHelper.contains(cascade, CascadeType.ALL)) {
                field.setAccessible(true);
                Collection<?> c = (Collection<?>) ReflectionUtils.getField(field, bean);
                if (Hibernate.isInitialized(c) && ListHelper.hasElements(c)) {
                    for (Object o : c) {
                        this.intercept(op, o);
                    }
                }
            }
            return;
        }
        field.setAccessible(true);
        Class<?> type = field.getType();

        Set<Interceptor> typeInterceptors = addAll(byType.get(void.class), byType.get(type));

        Annotation[] annons = field.getAnnotations();

        Set<Interceptor> annonInterceptors = new HashSet<Interceptor>();
        if (byAnnotation.get(void.class) != null) {
            annonInterceptors.addAll(byAnnotation.get(void.class));
        }

        for (Annotation an : annons) {
            if (byAnnotation.get(an.annotationType()) != null) {
                annonInterceptors.addAll(byAnnotation.get(an.annotationType()));
            }
        }

        typeInterceptors.retainAll(annonInterceptors);

        for (Interceptor bi : typeInterceptors) {
            if (this.isAssignable(field) && bi.interceptable(op, field, bean)) {
                bi.intercept(op, field, bean);
            }
        }
    }

    private Set<Interceptor> addAll(Set<Interceptor> base, Set<Interceptor> toAdd) {

        Set<Interceptor> toRet;
        if (base == null) {
            toRet = new HashSet<Interceptor>();
        } else {
            toRet = new HashSet<Interceptor>(base);
        }
        if (toAdd != null) {
            toRet.addAll(toAdd);
        }
        return toRet;
    }

    private Set<Interceptor> getInterceptorsByType(Class<?> type) {

        Set<Interceptor> toRet = this.getByType().get(type);
        if (toRet == null) {
            toRet = new HashSet<Interceptor>();
            this.getByType().put(type, toRet);
        }

        return toRet;
    }

    private Set<Interceptor> getInterceptorsByAnnotation(Class<?> type) {

        Set<Interceptor> toRet = this.getByAnnotation().get(type);
        if (toRet == null) {
            toRet = new HashSet<Interceptor>();
            this.getByAnnotation().put(type, toRet);
        }

        return toRet;
    }

    /**
     * @return byType
     */
    private Map<Class<?>, Set<Interceptor>> getByType() {

        if (this.byType == null) {
            this.byType = new HashMap<Class<?>, Set<Interceptor>>();

        }

        return this.byType;
    }

    /**
     * @return byAnnotation
     */
    private Map<Class<?>, Set<Interceptor>> getByAnnotation() {

        if (this.byAnnotation == null) {
            this.byAnnotation = new HashMap<Class<?>, Set<Interceptor>>();
        }

        return this.byAnnotation;
    }

    /**
     * Retorna el nmero de {@link Interceptor} registrados por este componente.
     * 
     * @return interceptorsCount
     */
    public int getInterceptorsCount() {

        return this.interceptorsCount;
    }

    /**
     * Verifica si un field puede ser asignable. Se define un fiel asignable
     * como aquel que no es estatico, final.
     * 
     * Como nota general tener en cuenta que un campo que no es String es
     * inmediatamente no asignable
     * 
     * @param field
     *            a ser evaluado
     * @return <code>true</code> si es asignable, <code>false</code> en caso
     *         contrario
     */
    private boolean isAssignable(Field field) {

        int modifier = field.getModifiers();
        if (Modifier.isFinal(modifier)) {
            return false;
        }
        if (Modifier.isStatic(modifier)) {
            return false;
        }
        if (Modifier.isTransient(modifier)) {
            return false;
        }
        return field.getAnnotation(Transient.class) == null;
    }
}