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

com.rapleaf.jack.DatabaseConnectionConfiguration Maven / Gradle / Ivy

There is a newer version: 1.8
Show newest version
package com.rapleaf.jack;

import java.io.FileReader;
import java.io.Reader;
import java.io.StringReader;
import java.util.Map;

import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.collect.Maps;
import org.jvyaml.YAML;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class DatabaseConnectionConfiguration {

  private final static Logger LOG = LoggerFactory.getLogger(DatabaseConnectionConfiguration.class);

  public static final String ADAPTER_PROP_PREFIX = "jack.db.adapter";
  public static final String HOST_PROP_PREFIX = "jack.db.host";
  public static final String NAME_PROP_PREFIX = "jack.db.name";
  public static final String PORT_PROP_PREFIX = "jack.db.port";
  public static final String PARALLEL_TEST_PROP_PREFIX = "jack.db.parallel.test";
  public static final String USERNAME_PROP_PREFIX = "jack.db.username";
  public static final String PASSWORD_PROP_PREFIX = "jack.db.password";
  public static final String DATABASE_YML_PROP = "jack.db.database.yml";
  public static final String ENVIRONMENT_YML_PROP = "jack.db.environment.yml";
  public static final String DATABASE_PATH_PROP = "jack.db.database.config.path";
  public static final String ENVIRONMENT_PATH_PROP = "jack.db.environment.config.path";
  public static final String CONNECTION_MAX_RETRIES = "jack.db.connection_max_retries";
  public static final String CONNECTION_VALIDATION_TIMEOUT = "jack.db.connection_validation_timeout";


  private String adapter;
  private String host;
  private String dbName;
  private Optional port;
  private Optional parallelTest;
  private Optional username;
  private Optional password;
  private Optional connectionMaxRetries;
  private Optional connectionValidationTimeout;

  public DatabaseConnectionConfiguration(String adapter, String host, String dbName, Optional port, Optional parallelTest, Optional username, Optional password) {
    this(
        adapter,
        host,
        dbName,
        port,
        parallelTest,
        username,
        password,
        Optional.absent(),
        Optional.absent()
    );
  }

  public DatabaseConnectionConfiguration(String adapter, String host, String dbName, Optional port, Optional parallelTest, Optional username, Optional password, Optional connectionMaxRetries, Optional connectionValidationTimeout) {
    this.adapter = adapter;
    this.host = host;
    this.dbName = dbName;
    this.port = port;
    this.parallelTest = parallelTest;
    this.username = username;
    this.password = password;
    this.connectionMaxRetries = connectionMaxRetries;
    this.connectionValidationTimeout = connectionValidationTimeout;
  }

  public static DatabaseConnectionConfiguration loadFromEnvironment(String dbNameKey) {
    Map envInfo;
    Map dbInfo;
    // load database info from some file - first check env, then props, then default location
    envInfo = fetchInfoMap(
        "environment",
        new EnvVarProvider(envVar(ENVIRONMENT_YML_PROP)),
        new PropertyProvider(ENVIRONMENT_YML_PROP),
        new FileReaderProvider(System.getenv(envVar(ENVIRONMENT_PATH_PROP))),
        new FileReaderProvider(System.getProperty(ENVIRONMENT_PATH_PROP)),
        new FileReaderProvider("config/environment.yml"),
        new FileReaderProvider("environment.yml"));

    String db_info_name = (String)envInfo.get(dbNameKey);

    dbInfo = (Map)fetchInfoMap(
        "database",
        new EnvVarProvider(envVar(DATABASE_YML_PROP)),
        new PropertyProvider(DATABASE_YML_PROP),
        new FileReaderProvider(System.getenv(envVar(DATABASE_PATH_PROP))),
        new FileReaderProvider(System.getProperty(DATABASE_PATH_PROP)),
        new FileReaderProvider("config/database.yml"),
        new FileReaderProvider("database.yml")).get(db_info_name);

    String adapter = load("adapter", dbInfo, "adapter", "database",
        envVar(ADAPTER_PROP_PREFIX, dbNameKey), prop(ADAPTER_PROP_PREFIX, dbNameKey), new StringIdentity());

    String host = load("host", dbInfo, "host", "database",
        envVar(HOST_PROP_PREFIX, dbNameKey), prop(HOST_PROP_PREFIX, dbNameKey), new StringIdentity());

    String dbName = load("database name", dbInfo, "database", "database",
        envVar(NAME_PROP_PREFIX, dbNameKey), prop(NAME_PROP_PREFIX, dbNameKey), new StringIdentity());

    Optional port = loadOpt(dbInfo, "port",
        envVar(PORT_PROP_PREFIX, dbNameKey), prop(PORT_PROP_PREFIX, dbNameKey), new ToInteger());

    Optional parallelTesting = loadOpt(envInfo, "enable_parallel_tests",
        envVar(PARALLEL_TEST_PROP_PREFIX, dbNameKey), prop(PARALLEL_TEST_PROP_PREFIX, dbNameKey), new ToBoolean());

    Optional username = loadOpt(dbInfo, "username",
        envVar(USERNAME_PROP_PREFIX, dbNameKey), prop(USERNAME_PROP_PREFIX, dbNameKey), new StringIdentity());

    Optional password = loadOpt(dbInfo, "password",
        envVar(PASSWORD_PROP_PREFIX, dbNameKey), prop(PASSWORD_PROP_PREFIX, dbNameKey), new StringIdentity());

    Optional connectionMaxRetries = loadOpt(dbInfo, "connection_max_retries",
        envVar(CONNECTION_MAX_RETRIES, dbNameKey), prop(CONNECTION_MAX_RETRIES, dbNameKey), new ToLong())
        /**
         * This transform is necessary because the underlying type parsed by
         * {@link #loadOpt(Map, String, String, String, Function)} is actually a Long. This is a
         * result of the YAML parser loading all numbers as Longs, not Integers. The only reason we
         * don't see this issue with the port config as well is because we never actually treat it
         * as an int later on. Without doing this, we'll get a {@link ClassCastException} when we
         * attempt to unwrap this into an int later on.
         */
        .transform(Long::intValue);

    Optional connectionValidationTimeout = loadOpt(dbInfo, "connection_validation_timeout",
        envVar(CONNECTION_VALIDATION_TIMEOUT, dbNameKey), prop(CONNECTION_VALIDATION_TIMEOUT, dbNameKey), new ToLong())
        .transform(Long::intValue);

    return new DatabaseConnectionConfiguration(adapter, host, dbName, port, parallelTesting, username, password, connectionMaxRetries, connectionValidationTimeout);
  }

  private static Map fetchInfoMap(String configName, ReaderProvider... readers) {
    for (ReaderProvider reader : readers) {
      try {
        Optional readerOptional = reader.get();
        if (readerOptional.isPresent()) {
          return (Map)YAML.load(readerOptional.get());
        }
      } catch (Exception e) {
        //move to next reader
      }
    }
    LOG.error("no yaml found for config: " + configName);
    return Maps.newHashMap();
  }

  private interface ReaderProvider {
    Optional get() throws Exception;
  }

  public static String envVar(String propertyPrefix, String dbNameKey) {
    return envVar(propertyPrefix) + "_" + dbNameKey.toUpperCase();
  }

  public static String envVar(String property) {
    return property.replace('.', '_').toUpperCase();
  }

  private static String prop(String baseProp, String dbNameKey) {
    return baseProp + "." + dbNameKey;
  }

  private static  T load(
      String readableName,
      Map map,
      String mapKey,
      String mapYmlFile,
      String envVar,
      String javaProp,
      Function fromString) {

    Optional result = loadOpt(map, mapKey, envVar, javaProp, fromString);
    if (result.isPresent()) {
      return result.get();
    } else {
      throw new RuntimeException(
          "Unable to find required configuration " + readableName + ". Please set using one of:\n" +
              "Environment Variable: " + envVar + "\n" +
              "Java System Property: " + javaProp + "\n" +
              "Entry in config/" + mapYmlFile + ".yml: " + mapKey + "\n" +
              "Found following keys: " + map.keySet());
    }
  }

  private static  Optional loadOpt(
      Map map,
      String mapKey,
      String envVar,
      String javaProp,
      Function fromString) {
    if (System.getenv(envVar) != null) {
      return Optional.fromNullable(fromString.apply(System.getenv(envVar)));
    }
    if (System.getProperty(javaProp) != null) {
      return Optional.fromNullable(fromString.apply(System.getProperty(javaProp)));
    }
    if (map != null && map.containsKey(mapKey)) {
      return Optional.fromNullable((T)map.get(mapKey));
    }
    return Optional.absent();
  }


  public String getAdapter() {
    return adapter;
  }

  public String getHost() {
    return host;
  }

  public Optional getPort() {
    return port;
  }

  public String getDatabaseName() {
    return dbName;
  }

  public Boolean enableParallelTests() {
    return parallelTest.isPresent() ? parallelTest.get() : false;
  }

  public Optional getUsername() {
    return username;
  }

  public Optional getPassword() {
    return password;
  }

  public Optional getConnectionMaxRetries() {
    return connectionMaxRetries;
  }

  public Optional getConnectionValidationTimeout() {
    return connectionValidationTimeout;
  }

  private static class StringIdentity implements Function {
    public String apply(String s) {
      return s;
    }
  }

  private static class ToInteger implements Function {
    public Integer apply(String s) {
      return Integer.parseInt(s);
    }
  }

  private static class ToLong implements Function {
    public Long apply(String s) {
      return Long.parseLong(s);
    }
  }

  private static class ToBoolean implements Function {
    public Boolean apply(String s) {
      return Boolean.parseBoolean(s);
    }
  }

  private static class FileReaderProvider implements ReaderProvider {

    private String file;

    public FileReaderProvider(String file) {
      this.file = file;
    }

    @Override
    public Optional get() throws Exception {
      if (file != null) {
        return Optional.of(new FileReader(file));
      } else {
        return Optional.absent();
      }
    }
  }

  private static class EnvVarProvider implements ReaderProvider {

    private String envVar;

    public EnvVarProvider(String envVar) {
      this.envVar = envVar;
    }

    @Override
    public Optional get() throws Exception {
      if (System.getenv(envVar) != null) {
        return Optional.of(new StringReader(System.getenv(envVar)));
      } else {
        return Optional.absent();
      }
    }
  }

  private static class PropertyProvider implements ReaderProvider {

    private String property;

    public PropertyProvider(String property) {
      this.property = property;
    }

    @Override
    public Optional get() throws Exception {
      if (System.getProperty(property) != null) {
        return Optional.of(new StringReader(System.getProperty(property)));
      } else {
        return Optional.absent();
      }
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy