com.rapleaf.jack.DatabaseConnectionConfiguration Maven / Gradle / Ivy
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