org.nmdp.service.common.domain.ConfigurationModule.java Source code

Java tutorial

Introduction

Here is the source code for org.nmdp.service.common.domain.ConfigurationModule.java

Source

/*
    
service-common  Common libraries and utilities for services modules.
Copyright (c) 2014-2015 National Marrow Donor Program (NMDP)
    
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 3 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; with out 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA.
    
> http://www.gnu.org/licenses/lgpl.html
    
*/

package org.nmdp.service.common.domain;

import static com.google.common.base.Preconditions.checkNotNull;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Stack;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Function;
import com.google.common.collect.Collections2;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.Sets;
import com.google.common.collect.Sets.SetView;
import com.google.inject.AbstractModule;
import com.google.inject.BindingAnnotation;

/**
 * Configuration module.
 */
public class ConfigurationModule extends AbstractModule {

    final List<Object> config;
    final Set<Class<? extends Annotation>> annos;
    Logger log = LoggerFactory.getLogger(getClass());
    Stack<Object> seen = new Stack<Object>();

    /**
     * Create a new configuration module with the specified configuration objects.
     *
     * @param config variable number of configuration objects, must not be null
     */
    public ConfigurationModule(Object... config) {
        checkNotNull(config);
        this.config = Collections.unmodifiableList(Arrays.asList(config));
        this.annos = Collections.unmodifiableSet(findConfigAnnotations(config));
    }

    @Override
    protected void configure() {
        for (Object o : config) {
            try {
                bindObjectConfig(o);
            } catch (Exception e) {
                binder().addError(e);
            }
        }
    }

    private Set<Class<? extends Annotation>> findConfigAnnotations(Object... config) {
        final Set<Class<? extends Annotation>> annos = new HashSet<Class<? extends Annotation>>();
        for (Object o : config) {
            if (o instanceof Class) {
                Class<?> clazz = (Class<?>) o;
                if (clazz.isAnnotation() && clazz.getAnnotation(BindingAnnotation.class) != null) {
                    annos.add((Class<? extends Annotation>) clazz);
                }
                annos.addAll(findConfigAnnotations(clazz.getDeclaredClasses()));
            }
        }
        return annos;
    }

    private static final Set<Class<? extends Serializable>> stopClasses = new HashSet<>(Arrays.asList(Boolean.class,
            Short.class, Integer.class, Long.class, Float.class, Double.class, String.class, Class.class));

    private void bindObjectConfig(Object value, Annotation... bindAnnos) throws Exception {
        if (null == value || value instanceof Class || seen.contains(value))
            return;
        seen.push(value);
        log.trace("stack size: " + seen.size());
        try {
            Class clazz = value.getClass();
            if (null != bindAnnos && bindAnnos.length > 0) {
                Set<Class<? extends Annotation>> bindAnnoClasses = FluentIterable.from(Arrays.asList(bindAnnos))
                        .transform(new Function<Annotation, Class<? extends Annotation>>() {
                            @Override
                            public Class<? extends Annotation> apply(Annotation anno) {
                                return anno.annotationType();
                            }
                        }).toSet();
                SetView<Class<? extends Annotation>> matchedAnnos = Sets.intersection(annos,
                        Sets.newHashSet(bindAnnoClasses));
                for (Class<? extends Annotation> anno : matchedAnnos) {
                    log.info("binding property " + anno.getSimpleName() + ": " + value);
                    bind(clazz).annotatedWith(anno).toInstance(clazz.cast(value));
                }
            }
            if (stopClasses.contains(value.getClass()))
                return;
            bindBeanProperties(value);
            bindJavaFields(value);
        } finally {
            seen.pop();
        }
    }

    private void bindBeanProperties(Object o) throws Exception {
        // bind config in bean properties
        BeanInfo cbi = Introspector.getBeanInfo(o.getClass(), Object.class);
        PropertyDescriptor[] pda = cbi.getPropertyDescriptors();
        if (null != pda) {
            for (PropertyDescriptor pd : pda) {
                Class pt = pd.getPropertyType();
                Method pr = pd.getReadMethod();
                if (null == pr)
                    continue;
                Object pv = pr.invoke(o, null);
                if (null != pv) {
                    Annotation[] pas = pr.getAnnotations();
                    log.trace("scanning bean property: " + pd.getName() + " (" + pd.getPropertyType() + ")");
                    bindObjectConfig(pv, pas);
                }
            }
        }
    }

    private void bindJavaFields(Object o) throws Exception {
        Field[] pfa = o.getClass().getDeclaredFields(); // no inherited types considered
        for (Field pf : pfa) {
            Object pv = null;
            try {
                pv = pf.get(o);
            } catch (Exception e) {
            }
            if (null != pv) {
                Annotation[] pas = pf.getAnnotations();
                log.trace("scanning java field: " + pf.getName() + " (" + pf.getType() + ")");
                bindObjectConfig(pv, pas);
            }
        }
    }

    private Class<? extends Annotation>[] annoToClass(Annotation... anno) {
        Class<? extends Annotation>[] annoClasses = new Class[anno.length];
        int i = 0;
        for (Annotation a : anno) {
            annoClasses[i++] = a.annotationType();
        }
        return annoClasses;
    }

}