com.htmlhifive.pitalium.core.config.PtlTestConfig.java Source code

Java tutorial

Introduction

Here is the source code for com.htmlhifive.pitalium.core.config.PtlTestConfig.java

Source

/*
 * Copyright (C) 2015-2016 NS Solutions Corporation
 *
 * 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.htmlhifive.pitalium.core.config;

import java.io.File;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Strings;
import com.htmlhifive.pitalium.common.exception.JSONException;
import com.htmlhifive.pitalium.common.exception.TestRuntimeException;
import com.htmlhifive.pitalium.common.util.JSONUtils;

/**
 * ????
 */
public class PtlTestConfig {

    private static final Logger LOG = LoggerFactory.getLogger(PtlTestConfig.class);
    private static final String SYSTEM_STARTUP_ARGUMENTS_PREFIX = "com.htmlhifive.pitalium.";

    private static PtlTestConfig instance;
    private final Map<String, String> startupArguments;
    private final Map<String, Object> configs = new HashMap<String, Object>();
    private EnvironmentConfig environment;
    private PersisterConfig persisterConfig;
    private TestAppConfig testApp;

    /**
     * 
     */
    private PtlTestConfig() {
        this(getSystemStartupArguments());
    }

    /**
     * 
     * 
     * @param startupArguments 
     */
    @VisibleForTesting
    PtlTestConfig(Map<String, String> startupArguments) {
        this.startupArguments = Collections.unmodifiableMap(startupArguments);
    }

    /**
     * JVM???????
     * 
     * @return ?
     */
    static Map<String, String> getSystemStartupArguments() {
        Properties properties = System.getProperties();
        Map<String, String> results = new HashMap<String, String>();
        int prefixLength = SYSTEM_STARTUP_ARGUMENTS_PREFIX.length();
        for (Object key : properties.keySet()) {
            String propertyKey = (String) key;
            if (!propertyKey.startsWith(SYSTEM_STARTUP_ARGUMENTS_PREFIX)) {
                continue;
            }

            String value = properties.getProperty(propertyKey);
            results.put(propertyKey.substring(prefixLength), value);
        }

        LOG.debug("System startup arguments: {}", results);
        return results;
    }

    /**
     * {@link PtlTestConfig}?????
     * 
     * @return 
     */
    public static synchronized PtlTestConfig getInstance() {
        if (instance != null) {
            return instance;
        }

        instance = new PtlTestConfig();
        return instance;
    }

    /**
     * ????
     * 
     * @param clss ??????
     * @param <T> ????
     * @return 
     */
    public <T> T getConfig(Class<T> clss) {
        return getConfig(clss, clss.getSimpleName());
    }

    /**
     * ????
     * 
     * @param clss ??????
     * @param <T> ????
     * @param name ??
     * @return 
     */
    @SuppressWarnings("unchecked")
    public <T> T getConfig(Class<T> clss, String name) {
        synchronized (configs) {
            // Check cached
            if (configs.containsKey(name)) {
                LOG.trace("[Load config] use cached ({}).", name);
                return (T) configs.get(name);
            }

            // Load configuration file
            PtlConfiguration configuration = clss.getAnnotation(PtlConfiguration.class);
            if (configuration == null) {
                throw new TestRuntimeException("Configuration class must be annotated with @PtlConfiguration");
            }

            String argumentName = configuration.argumentName();
            if (Strings.isNullOrEmpty(argumentName)) {
                argumentName = StringUtils.uncapitalize(clss.getSimpleName());
            }
            String defaultFileName = configuration.defaultFileName();
            if (Strings.isNullOrEmpty(defaultFileName)) {
                defaultFileName = StringUtils.uncapitalize(clss.getSimpleName()) + ".json";
            }

            T config = loadConfig(clss, startupArguments.get(argumentName), defaultFileName);
            LOG.trace("[Load config] original values ({}): {}", name, config);
            fillConfigProperties(config, startupArguments);
            LOG.debug("[Load config] ({}): {}", name, config);

            configs.put(name, config);
            LOG.trace("[Load config] config cached ({})", name);
            return config;
        }
    }

    /**
     * {@link PtlConfigurationProperty}?????????
     * 
     * @param object ?
     * @param arguments ?
     */
    private static void fillConfigProperties(Object object, Map<String, String> arguments) {
        // Collect all fields include super classes
        Class clss = object.getClass();
        List<Field> fields = new ArrayList<Field>();
        Collections.addAll(fields, clss.getDeclaredFields());
        while ((clss = clss.getSuperclass()) != Object.class) {
            Collections.addAll(fields, clss.getDeclaredFields());
        }

        for (Field field : fields) {
            PtlConfigurationProperty propertyConfig = field.getAnnotation(PtlConfigurationProperty.class);
            if (propertyConfig == null) {
                PtlConfiguration config = field.getAnnotation(PtlConfiguration.class);
                if (config == null) {
                    continue;
                }

                // Field is nested config class
                try {
                    field.setAccessible(true);
                    Object prop = field.get(object);
                    if (prop != null) {
                        fillConfigProperties(prop, arguments);
                    }
                } catch (TestRuntimeException e) {
                    throw e;
                } catch (Exception e) {
                    throw new TestRuntimeException(e);
                }

                continue;
            }

            String value = arguments.get(propertyConfig.value());
            if (value == null) {
                continue;
            }

            try {
                Object applyValue = convertFromString(field.getType(), value);
                field.setAccessible(true);
                field.set(object, applyValue);
                LOG.trace("[Load config] override property ({}). [{} => {}]", clss.getSimpleName(), field.getName(),
                        applyValue);
            } catch (TestRuntimeException e) {
                throw e;
            } catch (Exception e) {
                throw new TestRuntimeException("ConfigurationProperty convert error", e);
            }
        }
    }

    /**
     * ?????????
     * 
     * @param type ??String, int(Integer), double(Double),Enum?????
     * @param value ??
     * @return ??
     * @throws IllegalArgumentException Enum???????
     * @throws TestRuntimeException ??????????
     */
    static Object convertFromString(Class<?> type, String value)
            throws IllegalArgumentException, TestRuntimeException {
        if (type == String.class) {
            return value;
        } else if (type == int.class || type == Integer.class) {
            return Integer.parseInt(value);
        } else if (type == double.class || type == Double.class) {
            return Double.parseDouble(value);
        } else if (type.isEnum()) {
            for (Object o : type.getEnumConstants()) {
                if (((Enum) o).name().equals(value)) {
                    return o;
                }
            }
            throw new IllegalArgumentException();
        } else {
            throw new TestRuntimeException("Cannot convert type \"" + type.getName() + "\"");
        }
    }

    /**
     * ????????
     * 
     * @return 
     */
    public EnvironmentConfig getEnvironment() {
        synchronized (this) {
            if (environment != null) {
                return environment;
            }

            environment = getConfig(EnvironmentConfig.class);
            return environment;
        }
    }

    /**
     * ??????
     * 
     * @return ?
     */
    public TestAppConfig getTestAppConfig() {
        synchronized (this) {
            if (testApp != null) {
                return testApp;
            }

            testApp = getConfig(TestAppConfig.class);
            return testApp;
        }
    }

    /**
     * ?????????
     * 
     * @return ?????
     */
    public PersisterConfig getPersisterConfig() {
        synchronized (this) {
            if (persisterConfig != null) {
                return persisterConfig;
            }

            persisterConfig = getConfig(PersisterConfig.class);
            return persisterConfig;
        }
    }

    /**
     * ?????
     * 
     * @param <T> ???
     * @param clss ??
     * @param fileName ??
     * @param defaultFileName ???
     * @return ????????
     */
    private <T> T loadConfig(Class<T> clss, String fileName, String defaultFileName) {
        LOG.trace("[Load config] ({}). FileName: {}, DefaultFileName: {}", clss.getSimpleName(), fileName,
                defaultFileName);

        // ??????????
        // ??????
        if (!Strings.isNullOrEmpty(fileName)) {
            try {
                return JSONUtils.readValue(new File(fileName), clss);
            } catch (JSONException e) {
                try {
                    return JSONUtils.readValue(PtlTestConfig.class.getClassLoader().getResourceAsStream(fileName),
                            clss);
                } catch (JSONException e1) {
                    LOG.error("[Load config] failed to load config file. (class: {}, file: {})",
                            clss.getSimpleName(), fileName, e1);
                    throw e1;
                }
            }
        }

        // ??????????????
        // ????????
        try {
            return JSONUtils.readValue(PtlTestConfig.class.getClassLoader().getResourceAsStream(defaultFileName),
                    clss);
        } catch (JSONException e) {
            LOG.debug("[Load config] failed to load config file. ({}) (class: {}, file: {})", e.getMessage(),
                    clss.getSimpleName(), defaultFileName, e);
        }

        // ??
        try {
            return clss.newInstance();
        } catch (Exception e) {
            String message = String.format("Config \"%s\" must have default constructor.", clss.getSimpleName());
            LOG.error(message, e);
            throw new TestRuntimeException(message, e);
        }
    }

}