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

net.config.SqlFlattener.groovy Maven / Gradle / Ivy

package net.config

import groovy.sql.Sql
import net.config.client.ConfigLookup
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Map.Entry

/**
 * Loads from multiple configuration only databases given the following:
 *
 * 1) database url
 * 2) database user name
 * 3) database password (unencrypted)
 * 4) database driver class
 * 5) database table
 *
 * 
 * 
 *   Foo
 *   someUser
 *   unencryptedPassword
 *   some://url/with/db/driver
 *   someJdbcDriver
 * 
 * 
* * Already parsed and flattened into something like this: * * structures.dbconfigtable.name.bar.tablename : Foo * structures.dbconfigtable.name.bar.username : someUser * structures.dbconfigtable.name.bar.userpassword : unecryptedPassword * structures.dbconfigtable.name.bar.url : some://url/with/db/driver * structures.dbconfigtable.name.bar.driver : someJdbcDriver * * Notes: * These databases should be stand alone configuration databases that do not * contain sensitive information. Isolating them from the rest of persistent * data will make it easier for developers and business operations people to * update stored values. It will also reduce the overhead and influence of * company/dba selected databases for use. * * Parameters for DB tables with Customer information can certainly be listed * in the configuration files, sql tables, etc. However, they should have * previously encrypted values and rely upon a separate Client application * to decrypt/connect to the database with PCI information. * * Removes values from config map after generating key-value pairs * * @author dmillett * * Copyright 2011 David Millett * 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. */ class SqlFlattener { private static final def LOG = LoggerFactory.getLogger(SqlFlattener.class) public static final def DB_TABLE_PARAMS = ~/dbconfigtable.*/ public static final def TABLE = "tablename" public static final def USER = "username" public static final def PASSWORD = "userpassword" public static final def DB_URL = "url" public static final def DRIVER = "driver" /** * Loop through previously loaded configurations looking for any entries * related to database configurations. See DB_TABLE_PARAMS * * @param configs * @return */ def Map> buildMapFromDatabaseTables(Map> configs) { // Config map with all db config table params def dbParams = findDbConfigParams(configs) def dbTableConfigs = groupDbConfigParamsByTable(dbParams) return dbTableConfigs } /** * Not sure I like the code herein that groups -- sigh * * Responsible for looking up database configuration key:values and storing all * those key:values per table in resulting Map. * * @param dbConfigParams * @return */ protected def Map> groupDbConfigParamsByTable(Map dbConfigParams) { // Sorting on natural order should be enough def groupedMap = new TreeMap(dbConfigParams) def allDbConfigs = new HashMap>() def validDbConfigParams = validDbConfigParams(groupedMap) if ( !validDbConfigParams ) { LOG.warn("Cannot Load Database Configurations Due To Invalid Connection Parameters") return allDbConfigs } def index = 0 def increment = 5 def size = groupedMap.size(); def sqlRetriever = new SqlRetriever() while ( index < size ) { def end = index + increment def subMap = extractSubGroup(groupedMap.entrySet(), index, increment) def tableName = extractValue(subMap, TABLE) def sql = createSqlReader(subMap) def dbConfigs = sqlRetriever.loadFromDatabaseWithSelect(tableName, sql) if ( sql != null ) { sql.close() } for ( entry in dbConfigs ) { allDbConfigs.put(entry.key, entry.value) } index += increment } return allDbConfigs } /** * * @param entries * @param currentIndex * @param increment * @return */ protected def Map extractSubGroup(Set entries, int currentIndex, int increment) { def subMap = new HashMap(increment) def i = 0 for ( entry in entries ) { if ( i == increment ) { break } subMap.put(entry.key, entry.value) i++ } return subMap } protected def Sql createSqlReader(Map sqlTableInfos) { def user = extractValue(sqlTableInfos, USER) def password = extractValue(sqlTableInfos, PASSWORD) def url = extractValue(sqlTableInfos, DB_URL) def driver = extractValue(sqlTableInfos, DRIVER) return Sql.newInstance(url, user, password, driver) } /** * Pull a specific value for a dbconfig param from a sub-group table. * A subgroup consists of 5 values. See "loadFromDatabaseWithSelect()" * * @param subMap The five db config params needed to read a sql DB table * @param target The value to retrieve (url, user, etc) * @return The corresponding value for 'target' or 'null' */ protected def String extractValue(Map subMap, String target) { if ( subMap == null || target == null || target.empty ) { LOG.warn("Cannot Retrieve Database Connection Parameter $target") return null } for ( entry in subMap ) { if ( entry.key.endsWith(target.toLowerCase()) ) { return entry.value } } return null } /** * There should be 5 entries for each database configuration table to load configs from. * This relies on db config params extracted from the overall config map. * See "findDbConfigParameters()". This also ensures that if there are * 5 table configs present, then there will be 5 keys for the params below. * * 1) tablename * 2) username * 3) userpassword * 4) url * 5) driver * * @param dbConfigParams * @return true if the modulus of 'dbConfigParams % 5' is 0, otherwise false */ protected def boolean validDbConfigParams(Map dbConfigParams) { if ( dbConfigParams == null || dbConfigParams.empty ) { LOG.warn("Invalid DB Configs. Five Values Are Required For Each DB Connection (see constants)") return false } def modulus = dbConfigParams.size() % 5 if ( modulus != 0 ) { LOG.warn("Incorrect Number Of Config Table Params. 5 Are Required") return false } def dbTableCount = dbConfigParams.size() / 5 def helper = new ConfigLookup() def counts = new HashMap(5) counts.put(TABLE, helper.findByKeyPattern(dbConfigParams, helper.buildPattern(TABLE)).size()) counts.put(USER, helper.findByKeyPattern(dbConfigParams, helper.buildPattern(USER)).size()) counts.put(PASSWORD, helper.findByKeyPattern(dbConfigParams, helper.buildPattern(PASSWORD)).size()) counts.put(DB_URL, helper.findByKeyPattern(dbConfigParams, helper.buildPattern(DB_URL)).size()) counts.put(DRIVER, helper.findByKeyPattern(dbConfigParams, helper.buildPattern(DRIVER)).size()) for ( entry in counts ) { if ( entry.value != dbTableCount ) { LOG.warn("Incorrect Number Of DB Connection Params For ${entry.key}") return false; } } true } /** * Remove the db config parameters as they should not be used in a client application. If the Client * requires app specific databases, then those should be separate from the config database. That means * they should not have the dbConfigTable * * @param allConfigs * @param dbParams * @return */ def Map findNonDbConfigs(Map allConfigs, Map dbParams) { def dbcleanedConfig = new HashMap() for ( entry in allConfigs ) { if ( !dbParams.containsKey(entry.key) ) { dbcleanedConfig.put(entry.key, entry.value) } } return dbcleanedConfig } /** * Identify all of the database configs from the config map and then create a new * config map from the original config map without the database config parameters. * * @param allConfigEntries The config map * @return A new map based on 'allConfigEntries' but without any db config parameters */ def Map> purgeAllDbConfigParams(Map> allConfigEntries) { def dbConfigParams = findDbConfigParams(allConfigEntries) def purgedMap = new HashMap>() for ( entry in allConfigEntries.entrySet() ) { def nonDbConfigs = findNonDbConfigs(entry.value, dbConfigParams) if ( !nonDbConfigs.isEmpty() ) { purgedMap.put(entry.key, nonDbConfigs) } } return purgedMap } /** * Loop through each config file and check its config entries for database * table connection parameters. * * @param configMap All of the loaded file/url configs. * @return All database connection parameters for configs in the database */ def Map findDbConfigParams(Map> configMap) { def dbConfigTableParams = new HashMap(); def cfgLookup = new ConfigLookup() for ( configFileName in configMap.keySet() ) { def dbTableParams = cfgLookup.findByKeyPattern(configMap.get(configFileName), DB_TABLE_PARAMS) dbConfigTableParams.putAll(dbTableParams) } return dbConfigTableParams } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy