ar.com.zauber.commons.repository.utils.SpringInjectionInterceptor.java Source code

Java tutorial

Introduction

Here is the source code for ar.com.zauber.commons.repository.utils.SpringInjectionInterceptor.java

Source

/**
 * Copyright (c) 2005-2012 Zauber S.A. <http://www.zaubersoftware.com/>
 *
 * 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 ar.com.zauber.commons.repository.utils;

import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.UnhandledException;
import org.apache.commons.lang.Validate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.hibernate.EmptyInterceptor;
import org.hibernate.type.Type;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

/**
 * <p>
 * Hibernate interceptor used to inject dependencies from a spring context
 * to achieve a Domain Driven Design
 * </p>
 * <p>
 * The classes wishing being injected with this interceptor, must be
 * annotated with the annotation {@link Configurable}. And each field
 * that will be injected must be transient and annotated with {@link Qualifier}.
 * The qualifier name can be used to set the bean name (else it uses the field name)
 * </p>
 * Example:
 * <pre>
 *  @Entity
 *  @Configurable
 *  public class DomainEntityExample implements Persistible {
 *      @Id
 *      private Long id;
 *      
 *      @Qualifier(value = "someService")
 *      private transient SomeService service;
 *      @Qualifier
 *      private transient SomeService someService;
 *     
 *      public final SomeService getService() {
 *          return service;
 *      }
 *      public final SomeService getSomeService() {
 *          return someService;
 *      }
 *      ....
 *  }
 *  
 * 
 * </pre>
 * @author Juan F. Codagnone
 * @since Mar 12, 2009
 */
public class SpringInjectionInterceptor extends EmptyInterceptor
        implements ApplicationContextAware, InitializingBean {
    private static final long serialVersionUID = 2501930754071241444L;
    private ApplicationContext ctx;
    private final Map<Class<?>, DependencyInjection> dependencyCache = new HashMap<Class<?>, DependencyInjection>();
    private final Logger log = LoggerFactory.getLogger(SpringInjectionInterceptor.class);

    /**
     * @param persistibleClasses persistible classes that may requiere dependency
     *                           injection
     */
    public SpringInjectionInterceptor(final List<Class<?>> persistibleClasses) {
        Validate.noNullElements(persistibleClasses);

        for (final Class<?> clazz : persistibleClasses) {
            final List<Entry<Field, String>> fields = new LinkedList<Entry<Field, String>>();
            for (final Annotation annotation : clazz.getAnnotations()) {
                if (annotation instanceof Configurable) {
                    final List<Field> clazzFields = new LinkedList<Field>();
                    Class<?> c = clazz;
                    while (c != null) {
                        clazzFields.addAll(Arrays.asList(c.getDeclaredFields()));
                        c = c.getSuperclass();
                    }

                    for (final Field field : clazzFields) {
                        for (final Annotation a : field.getAnnotations()) {
                            if (a instanceof Qualifier) {
                                String n = ((Qualifier) a).value();
                                if (StringUtils.isBlank(n)) {
                                    n = field.getName();
                                }
                                final String name = n;
                                fields.add(new Entry<Field, String>() {
                                    public Field getKey() {
                                        return field;
                                    }

                                    public String getValue() {
                                        return name;
                                    }

                                    public String setValue(final String value) {
                                        return null;
                                    }
                                });
                            }
                        }
                    }
                }
            }

            if (fields.size() > 0) {
                dependencyCache.put(clazz,
                        new DependencyInjection(fields, InitializingBean.class.isAssignableFrom(clazz)));
            }
        }
    }

    /** @see ApplicationContextAware#setApplicationContext(ApplicationContext) */
    public final void setApplicationContext(final ApplicationContext applicationContext) throws BeansException {
        Validate.notNull(applicationContext);
        this.ctx = applicationContext;
    }

    /** @see InitializingBean#afterPropertiesSet() */
    public final void afterPropertiesSet() throws Exception {
        Validate.notNull(ctx, "the application context is missing ");
    }

    /** @see EmptyInterceptor#onLoad(Object, Serializable, Object[], String[], 
     *                              Type[]) */
    @Override
    public final boolean onLoad(final Object entity, final Serializable id, final Object[] state,
            final String[] propertyNames, final Type[] types) {

        final DependencyInjection d = dependencyCache.get(entity.getClass());
        if (d != null) {
            d.inject(entity);
        }
        //embeeded objects may need to be injected
        if (state != null) {
            for (final Object o : state) {
                if (o != null) {
                    final DependencyInjection dio = dependencyCache.get(o.getClass());
                    if (dio != null) {
                        dio.inject(o);
                    }
                }
            }
        }
        return super.onLoad(entity, id, state, propertyNames, types);
    }

    /** injection */
    class DependencyInjection {
        private final List<Entry<Field, String>> fields;
        private final boolean initializingBean;

        /**  
         * @param fields list of fields and bean name to inject 
         * @param initializingBean <code>true</code> if the class implements
         *   {@link InitializingBean}. 
         */
        public DependencyInjection(final List<Entry<Field, String>> fields, final boolean initializingBean) {
            Validate.noNullElements(fields);

            this.fields = fields;
            this.initializingBean = initializingBean;
        }

        /** inject the dependency */
        public void inject(final Object o) {
            for (final Entry<Field, String> field : fields) {
                final String beanName = field.getValue();
                try {
                    final Object dependency = ctx.getBean(beanName);

                    final Field f = field.getKey();
                    final boolean lastAccessible = f.isAccessible();
                    try {
                        f.setAccessible(true);
                        f.set(o, dependency);
                    } catch (final IllegalArgumentException e) {
                        throw new UnhandledException(e);
                    } catch (final IllegalAccessException e) {
                        throw new UnhandledException(e);
                    } finally {
                        f.setAccessible(lastAccessible);
                    }
                } catch (final NoSuchBeanDefinitionException e) {
                    log.error("unable to inject bean named `" + beanName + "' to class " + o.getClass().getName(),
                            e);
                }
            }

            if (initializingBean) {
                try {
                    ((InitializingBean) o).afterPropertiesSet();
                } catch (final Exception e) {
                    throw new UnhandledException(e);
                }
            }
        }
    }
}