Java tutorial
/* * 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); } } }