com.logsniffer.config.BeanConfigFactoryManager.java Source code

Java tutorial

Introduction

Here is the source code for com.logsniffer.config.BeanConfigFactoryManager.java

Source

/*******************************************************************************
 * logsniffer, open source tool for viewing, monitoring and analysing log data.
 * Copyright (c) 2015 Scaleborn UG, www.scaleborn.com
 *
 * logsniffer is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * logsniffer 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *******************************************************************************/
package com.logsniffer.config;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.PostConstruct;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.type.filter.AssignableTypeFilter;

import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.DeserializationConfig;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.deser.BeanDeserializerModifier;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.logsniffer.config.ConfiguredBean.ConfiguredBeanDeserializer;
import com.logsniffer.util.value.ConfigInjector;

/**
 * Manages creating, serialization and deserialization of bean configs.
 * 
 * @author mbok
 * 
 */
public class BeanConfigFactoryManager implements ConfigBeanTypeResolver {
    private final Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired(required = false)
    private BeanPostConstructor<?>[] postConstructors;

    private final HashMap<Class<?>, BeanPostConstructor<?>> mappedPostConstrucors = new HashMap<Class<?>, BeanPostConstructor<?>>();

    @Autowired
    private ObjectMapper jsonMapper;

    @Autowired
    private ConfigInjector configInjector;

    private final Map<Class<? extends ConfiguredBean>, List<String>> configBeanNames = new HashMap<>();;

    @SuppressWarnings("unchecked")
    @PostConstruct
    private void initJsonMapper() {
        final SimpleModule module = new SimpleModule();
        module.setDeserializerModifier(new BeanDeserializerModifier() {
            @Override
            public JsonDeserializer<?> modifyDeserializer(final DeserializationConfig config,
                    final BeanDescription beanDesc, final JsonDeserializer<?> deserializer) {
                if (ConfiguredBean.class.isAssignableFrom(beanDesc.getBeanClass())) {
                    return new ConfiguredBeanDeserializer(deserializer);
                }
                return deserializer;
            }
        });
        jsonMapper.registerModule(module);
        if (postConstructors != null) {
            for (final BeanPostConstructor<?> bpc : postConstructors) {
                mappedPostConstrucors.put(bpc.getClass(), bpc);
            }
        }

        // Register sub beans
        final ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(
                false);
        final AssignableTypeFilter filter4configBenas = new AssignableTypeFilter(ConfiguredBean.class);
        scanner.addIncludeFilter(filter4configBenas);

        for (final BeanDefinition bd : scanner.findCandidateComponents("com.logsniffer")) {
            try {
                final Class<? extends ConfiguredBean> clazz = (Class<? extends ConfiguredBean>) Class
                        .forName(bd.getBeanClassName());
                final JsonTypeName jsonNameAnnotation = clazz.getAnnotation(JsonTypeName.class);
                final List<String> names = new ArrayList<String>();
                configBeanNames.put(clazz, names);
                if (jsonNameAnnotation != null) {
                    names.add(jsonNameAnnotation.value());
                    if (jsonNameAnnotation.deprecated() != null) {
                        for (final String dep : jsonNameAnnotation.deprecated()) {
                            names.add(dep);
                        }
                    }
                }
                names.add(clazz.getSimpleName());
                logger.debug("Registered JSON type {} for following names: {}", clazz, names);
            } catch (final ClassNotFoundException e) {
                logger.warn("Failed to register JSON type name for " + bd.getBeanClassName(), e);
            }
        }
    }

    /**
     * Serializes the bean to JSON.
     * 
     * @param config
     *            the config to serialize
     * @param json
     *            the JSON object to serialize config data to
     * @throws ConfigException
     *             in case of serialize errors
     */
    public <BeanType extends ConfiguredBean> String saveBeanToJSON(final BeanType bean) throws ConfigException {
        try {
            final String str = jsonMapper.writeValueAsString(bean);
            return str;
        } catch (final Exception e) {
            throw new ConfigException("Failed to serialize bean: " + bean, e);
        }
    }

    @SuppressWarnings({ "unchecked", "rawtypes" })
    protected <BeanType> void postConstruct(final ConfiguredBean bean) {
        if (bean == null) {
            return;
        }
        final PostConstructed pc = AnnotationUtils.findAnnotation(bean.getClass(), PostConstructed.class);
        if (bean instanceof BeanPostConstructor<?>
                && (pc == null || !mappedPostConstrucors.containsKey(bean.getClass()))) {
            ((BeanPostConstructor) bean).postConstruct(bean, this);
        }
        if (pc != null) {
            final BeanPostConstructor bpc = mappedPostConstrucors.get(pc.constructor());
            if (bpc != null) {
                bpc.postConstruct(bean, this);
            } else {
                logger.error("Unsatisfied bean construction of '{}' due to missing post constructor of type: {}",
                        bean, pc.getClass());
            }
        }
        configInjector.postProcessBeforeInitialization(bean, bean.getClass().getName());
    }

    /**
     * Creates a bean related to the given config. The creation is performed by
     * the corresponding bean factory.
     * 
     * @param clazz
     *            the bean class
     * @param config
     *            the config to use
     * @return desired configured bean
     * @throws ConfigException
     *             in case of errors
     */
    public <BeanType extends ConfiguredBean> BeanType createBeanFromJSON(final Class<BeanType> clazz,
            final String json) throws ConfigException {
        try {
            final BeanType bean = jsonMapper.readValue(json, clazz);
            return bean;
        } catch (final Exception e) {
            throw new ConfigException("Failed to deserialize bean: " + clazz, e);
        }
    }

    @Override
    public String resolveTypeName(final Class<? extends ConfiguredBean> clazz) throws ConfigException {
        if (configBeanNames.containsKey(clazz) && !configBeanNames.get(clazz).isEmpty()) {
            return configBeanNames.get(clazz).get(0);
        }
        throw new ConfigException("No name defined for type: " + clazz);
    }

    @SuppressWarnings("unchecked")
    @Override
    public <T extends ConfiguredBean> Class<? extends T> resolveTypeClass(final String searchNname,
            final Class<T> wantedSuperType) throws ConfigException {
        for (final Class<? extends ConfiguredBean> clazz : configBeanNames.keySet()) {
            if (wantedSuperType == null || wantedSuperType.isAssignableFrom(clazz)) {
                for (final String name : configBeanNames.get(clazz)) {
                    if (name.equalsIgnoreCase(searchNname)) {
                        return (Class<? extends T>) clazz;
                    }
                }
            }
        }
        throw new ConfigException(
                "Couldn't resolve type for name '" + searchNname + "' of type base: " + wantedSuperType);
    }
}