package io.lumify.core.config; import io.lumify.core.exception.LumifyException; import io.lumify.core.util.ClassUtil; import java.io.*; import java.sql.*; import java.util.*; public class DatabaseConfigurationLoader extends ConfigurationLoader { /** * !!! DO NOT DEFINE A LOGGER here. This class get loaded very early in the process and we don't want to the logger to be initialized yet ** */ public static final String ENV_BOOTSTRAP_LOCATION = "LUMIFY_BOOTSTRAP_LOCATION"; public static final String DEFAULT_BOOTSTRAP_LOCATION = "/opt/lumify/config"; public static final String BOOTSTRAP_PREFIX = "databaseConfigurationLoader"; private DatabaseConfigurationLoaderConfig config = new DatabaseConfigurationLoaderConfig(); private Map<String, String> files; private Map<String, String> properties; public static void main(String[] args) { DatabaseConfigurationLoader databaseConfigurationLoader = new DatabaseConfigurationLoader(new HashMap()); Configuration configuration = databaseConfigurationLoader.createConfiguration(); System.out.println("PROPERTIES:"); System.out.println(configuration); System.out.println("FILES:"); for (String fileName : databaseConfigurationLoader.getFileNames()) { System.out.println(fileName); } } /** * note that local bootstrap properties will override values in the database * */ public DatabaseConfigurationLoader(Map initParameters) { super(initParameters); Map<String, String> bootstrapProperties = getBootstrapProperties(); Configuration bootstrapConfiguration = new Configuration(this, bootstrapProperties); bootstrapConfiguration.setConfigurables(config, BOOTSTRAP_PREFIX); Map<String, String> databaseValues = selectDatabaseValues(); files = new HashMap<String, String>(); properties = new HashMap<String, String>(); for (Map.Entry entry : databaseValues.entrySet()) { String key = (String) entry.getKey(); if (key.contains(config.configurationKeyFileIndicator)) { files.put(key.replaceFirst(config.configurationKeyFileIndicator + ".", ""), (String) entry.getValue()); } else { properties.put(key, (String) entry.getValue()); } } properties.putAll(bootstrapProperties); } @Override public Configuration createConfiguration() { return new Configuration(this, properties); } /** * note that local files in the bootstrap location will override values in the database */ @Override public File resolveFileName(String fileName) { File bootstrapLocation = getBootstrapLocation(); if (bootstrapLocation.isDirectory()) { File localFile = new File(bootstrapLocation, fileName); if (localFile.exists()) { return localFile; } } if (files.containsKey(fileName)) { try { File tempFile = File.createTempFile(DatabaseConfigurationLoader.class.getName() + "-", "-" + fileName); tempFile.deleteOnExit(); FileOutputStream fos = new FileOutputStream(tempFile); fos.write(files.get(fileName).getBytes()); fos.close(); return tempFile; } catch (IOException ioe) { throw new LumifyException("error creating temporary file for file: " + fileName, ioe); } } return null; } private Set<String> getFileNames() { return files.keySet(); } private File getBootstrapLocation() { String bootstrapLocationString = System.getenv(ENV_BOOTSTRAP_LOCATION); if (bootstrapLocationString == null) { bootstrapLocationString = DEFAULT_BOOTSTRAP_LOCATION; } if (bootstrapLocationString.startsWith("file://")) { bootstrapLocationString = bootstrapLocationString.substring("file://".length()); } return new File(bootstrapLocationString); } private Map<String, String> getBootstrapProperties() { Properties properties = new Properties(); File bootstrapLocation = getBootstrapLocation(); List<File> bootstrapPropertiesFiles; if (bootstrapLocation.isDirectory()) { File[] files = bootstrapLocation.listFiles(new FilenameFilter() { @Override public boolean accept(File dir, String name) { return name.endsWith(".properties"); } }); Arrays.sort(files, new Comparator<File>() { @Override public int compare(File o1, File o2) { return o1.getName().compareTo(o2.getName()); } }); bootstrapPropertiesFiles = Arrays.asList(files); } else { bootstrapPropertiesFiles = new ArrayList<File>(); bootstrapPropertiesFiles.add(bootstrapLocation); } for (File file : bootstrapPropertiesFiles) { try { FileInputStream fis = new FileInputStream(file); try { properties.load(fis); } finally { fis.close(); } } catch (IOException ioe) { throw new LumifyException("error loading bootstrap properties from file: " + file.getName(), ioe); } } Map<String, String> map = new HashMap<String, String>(); for (Map.Entry entry : properties.entrySet()) { map.put((String) entry.getKey(), (String) entry.getValue()); } return map; } private Map<String, String> selectDatabaseValues() { String query = String.format("select %s, %s from %s where %s = ? and %s = ? and %s like ?", config.configurationKeyColumn, config.configurationValueColumn, config.configurationTable, config.configurationEnvironmentColumn, config.configurationVersionColumn, config.configurationKeyColumn); Map<String, String> map = new HashMap<String, String>(); try { Class.forName(config.databaseDriverClass); // this shouldn't be required Connection conn = DriverManager.getConnection(config.databaseUrl, config.databaseUsername, config.databasePassword); try { PreparedStatement statement = conn.prepareStatement(query); try { statement.setString(1, config.configurationEnvironment); statement.setString(2, config.configurationVersion); statement.setString(3, config.configurationKeyPrefix + ".%"); ResultSet resultSet = statement.executeQuery(); while (resultSet.next()) { String key = resultSet.getString(config.configurationKeyColumn).replaceFirst(config.configurationKeyPrefix + ".", ""); String value = resultSet.getString(config.configurationValueColumn); map.put(key, value); } } finally { statement.close(); } } finally { conn.close(); } } catch (SQLException se) { throw new LumifyException("error selecting values from the database", se); } catch (ClassNotFoundException cnfe) { ClassUtil.logClasspath(this.getClass().getClassLoader(), System.err); throw new LumifyException("error loading database driver class: " + config.databaseDriverClass, cnfe); } return map; } }