All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.tsunami.common.config.TsunamiConfig Maven / Gradle / Ivy

There is a newer version: 0.0.26
Show newest version
/*
 * Copyright 2020 Google LLC
 *
 * 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.google.tsunami.common.config;

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

import com.google.common.base.CaseFormat;
import com.google.common.base.Converter;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableMap;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.Map;
import java.util.Optional;

/** A data holder for all Tsunami config data, including config files and Java system properties. */
public final class TsunamiConfig {
  private static final Converter FIELD_NAME_TO_LOWER_UNDERSCORE =
      CaseFormat.LOWER_CAMEL.converterTo(CaseFormat.LOWER_UNDERSCORE);
  private static final Splitter CONFIG_PATH_SPLITTER = Splitter.on('.').omitEmptyStrings();

  private final ImmutableMap rawConfigData;

  private TsunamiConfig(ImmutableMap rawConfigData) {
    this.rawConfigData = checkNotNull(rawConfigData);
  }

  public ImmutableMap getRawConfigData() {
    return rawConfigData;
  }

  public static TsunamiConfig fromYamlData(Map yamlConfig) {
    return new TsunamiConfig(
        yamlConfig == null ? ImmutableMap.of() : ImmutableMap.copyOf(yamlConfig));
  }

  public static Optional getSystemProperty(String propertyName) {
    return Optional.ofNullable(getSystemProperty(propertyName, null));
  }

  public static String getSystemProperty(String propertyName, String def) {
    return System.getProperty(propertyName, def);
  }

  /**
   * Get a config object with the given {@code configPrefix} and bind all config values to the
   * requested {@code clazz}.
   *
   * 

This code uses reflection to create the requested config object. The request type {@code T} * must provide a no-argument or default constructor. * * @param configPrefix the prefix of the config to be read from. * @param clazz the class of the returned config object. * @param actual config object type. * @return an object whose field values are filled by the config data under the given {@code * configPrefix}. */ public T getConfig(String configPrefix, Class clazz) { checkNotNull(configPrefix); checkNotNull(clazz); Map configValue = readConfigValue(configPrefix); return newConfigObject(clazz, configValue); } @SuppressWarnings("unchecked") // We know Map key is always String from yaml file. private ImmutableMap readConfigValue(String configPrefix) { Map retrievedData = rawConfigData; // Config prefixes are dot separated words list, e.g. example.config.prefix. for (String configKey : CONFIG_PATH_SPLITTER.split(configPrefix)) { // Requested data not found under configPrefix. if (!retrievedData.containsKey(configKey)) { return ImmutableMap.of(); } Object configData = retrievedData.get(configKey); if (!(configData instanceof Map)) { throw new ConfigException( String.format( "Unexpected data type for config '%s', expected '%s', got '%s'", configKey, Map.class, configData.getClass())); } retrievedData = (Map) configData; } return ImmutableMap.copyOf(retrievedData); } private static T newConfigObject(Class clazz, Map configValue) { try { Constructor configObjectCtor = clazz.getDeclaredConstructor(); // Always create an instance of the config data regardless of scope. configObjectCtor.setAccessible(true); T configObject = configObjectCtor.newInstance(); // Fill each field of the configObject from configValue using the field name as key. for (Field field : clazz.getDeclaredFields()) { String fieldName = field.getName(); if (configValue.containsKey(fieldName) || configValue.containsKey(FIELD_NAME_TO_LOWER_UNDERSCORE.convert(fieldName))) { Object fieldValue = Optional.ofNullable(configValue.get(fieldName)) .orElse(configValue.get(FIELD_NAME_TO_LOWER_UNDERSCORE.convert(fieldName))); field.setAccessible(true); field.set(configObject, fieldValue); } } return configObject; } catch (ReflectiveOperationException e) { // This is bad. Config objects cannot be created or config value cannot be assigned to the // field, we throw assertion error and fail the execution. throw new AssertionError( String.format( "Unable to create new instance of '%s' using config value '%s'", clazz, configValue), e); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy