Merge pull request #587 from corda/feature/ENT-1637/migration-tool-changes

ENT-1673 Update migration tool to use the drivers folder and created reusable "createDatasourceFromDriverJars" function
This commit is contained in:
Tudor Malene 2018-03-22 10:35:55 +00:00 committed by GitHub
commit 72b97be42c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 41 additions and 14 deletions

View File

@ -14,10 +14,14 @@ import com.zaxxer.hikari.HikariConfig
import com.zaxxer.hikari.HikariDataSource import com.zaxxer.hikari.HikariDataSource
import com.zaxxer.hikari.util.PropertyElf import com.zaxxer.hikari.util.PropertyElf
import net.corda.core.internal.declaredField import net.corda.core.internal.declaredField
import net.corda.core.internal.div
import org.h2.engine.Database import org.h2.engine.Database
import org.h2.engine.Engine import org.h2.engine.Engine
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import java.lang.reflect.Modifier import java.lang.reflect.Modifier
import java.net.URLClassLoader
import java.nio.file.Files
import java.nio.file.Path
import java.util.* import java.util.*
import javax.sql.DataSource import javax.sql.DataSource
@ -53,4 +57,28 @@ object DataSourceFactory {
} }
} }
} }
fun createDatasourceFromDriverJars(dataSourceProperties: Properties, baseClassLoader: ClassLoader, driverJarsPath: Path): DataSource {
return URLClassLoader(Files.newDirectoryStream(driverJarsPath, "*.jar").map { it.toUri().toURL() }.toTypedArray(), baseClassLoader).use { driversClassLoader ->
val dataSourceClassName = dataSourceProperties["dataSourceClassName"] as String?
val dataSourceClass = driversClassLoader.loadClass(dataSourceClassName)
val dataSourceInstance = dataSourceClass.newInstance() as DataSource
val props = Properties().also {
it.putAll(dataSourceProperties.propertyNames().toList()
.filter { name -> (name as String).startsWith("dataSource.") }
.map { name -> (name as String).substring("dataSource.".length) to (dataSourceProperties[name]) }.toMap())
}
PropertyElf.setTargetFromProperties(dataSourceInstance, props)
dataSourceInstance
}
}
fun createHikariDatasourceFromDriverJars(dataSourceProperties: Properties, baseClassLoader: ClassLoader, driverJarsPath: Path): DataSource {
val dataSource = createDatasourceFromDriverJars(dataSourceProperties, baseClassLoader, driverJarsPath)
val cfg = HikariConfig(dataSourceProperties)
cfg.dataSource = dataSource
return HikariDataSource(cfg)
}
} }

View File

@ -37,6 +37,7 @@ dependencies{
shadowJar { shadowJar {
transform(de.sebastianboegl.gradle.plugins.shadow.transformers.Log4j2PluginsFileTransformer) transform(de.sebastianboegl.gradle.plugins.shadow.transformers.Log4j2PluginsFileTransformer)
archiveName = "migration-tool-${version}.jar"
} }
task buildMigrationTool(dependsOn: shadowJar)

View File

@ -13,8 +13,7 @@
package com.r3.corda.dbmigration package com.r3.corda.dbmigration
import com.typesafe.config.ConfigFactory import com.typesafe.config.ConfigFactory
import com.zaxxer.hikari.HikariConfig import com.zaxxer.hikari.util.PropertyElf
import com.zaxxer.hikari.HikariDataSource
import joptsimple.OptionException import joptsimple.OptionException
import joptsimple.OptionParser import joptsimple.OptionParser
import joptsimple.OptionSet import joptsimple.OptionSet
@ -23,6 +22,7 @@ import net.corda.core.internal.MigrationHelpers
import net.corda.core.internal.copyTo import net.corda.core.internal.copyTo
import net.corda.core.internal.div import net.corda.core.internal.div
import net.corda.core.schemas.MappedSchema import net.corda.core.schemas.MappedSchema
import net.corda.node.internal.DataSourceFactory.createDatasourceFromDriverJars
import net.corda.node.internal.cordapp.CordappLoader import net.corda.node.internal.cordapp.CordappLoader
import net.corda.node.services.config.ConfigHelper import net.corda.node.services.config.ConfigHelper
import net.corda.node.services.config.parseAsNodeConfiguration import net.corda.node.services.config.parseAsNodeConfiguration
@ -37,6 +37,7 @@ import java.io.FileWriter
import java.io.PrintWriter import java.io.PrintWriter
import java.io.Writer import java.io.Writer
import java.net.URLClassLoader import java.net.URLClassLoader
import java.nio.file.Files
import java.nio.file.Path import java.nio.file.Path
import java.nio.file.Paths import java.nio.file.Paths
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
@ -71,7 +72,7 @@ private fun initOptionParser(): OptionParser = OptionParser().apply {
.defaultsTo(Mode.NODE) .defaultsTo(Mode.NODE)
accepts(BASE_DIRECTORY, "The node or doorman directory") accepts(BASE_DIRECTORY, "The node or doorman directory")
.withRequiredArg() .withRequiredArg().required()
accepts(CONFIG, "The name of the config file. By default 'node.conf' for a simple node and 'network-management.conf' for a doorman.") accepts(CONFIG, "The name of the config file. By default 'node.conf' for a simple node and 'network-management.conf' for a doorman.")
.withOptionalArg() .withOptionalArg()
@ -141,12 +142,12 @@ private fun runCommand(options: OptionSet, parser: OptionParser) {
private fun handleCommand(options: OptionSet, baseDirectory: Path, configFile: Path, mode: Mode, classLoader: ClassLoader, schemas: Set<MappedSchema>) { private fun handleCommand(options: OptionSet, baseDirectory: Path, configFile: Path, mode: Mode, classLoader: ClassLoader, schemas: Set<MappedSchema>) {
val config = ConfigFactory.parseFile(configFile.toFile()).resolve().parseAs(Configuration::class, false) val config = ConfigFactory.parseFile(configFile.toFile()).resolve().parseAs(Configuration::class, false)
fun runMigrationCommand(withMigration: (SchemaMigration) -> Unit): Unit = runWithDataSource(config) { dataSource -> fun runMigrationCommand(withMigration: (SchemaMigration) -> Unit): Unit = runWithDataSource(config, baseDirectory, classLoader) { dataSource ->
withMigration(SchemaMigration(schemas, dataSource, true, config.database, classLoader)) withMigration(SchemaMigration(schemas, dataSource, true, config.database, classLoader))
} }
when { when {
options.has(RELEASE_LOCK) -> runWithDataSource(ConfigFactory.parseFile(configFile.toFile()).resolve().parseAs(Configuration::class)) { options.has(RELEASE_LOCK) -> runWithDataSource(ConfigFactory.parseFile(configFile.toFile()).resolve().parseAs(Configuration::class), baseDirectory, classLoader) {
SchemaMigration(emptySet(), it, true, config.database, Thread.currentThread().contextClassLoader).forceReleaseMigrationLock() SchemaMigration(emptySet(), it, true, config.database, Thread.currentThread().contextClassLoader).forceReleaseMigrationLock()
} }
options.has(DRY_RUN) -> { options.has(DRY_RUN) -> {
@ -165,7 +166,7 @@ private fun handleCommand(options: OptionSet, baseDirectory: Path, configFile: P
fun generateMigrationFileForSchema(schemaClass: String) { fun generateMigrationFileForSchema(schemaClass: String) {
logger.info("Creating database migration files for schema: $schemaClass into ${baseDirectory / "migration"}") logger.info("Creating database migration files for schema: $schemaClass into ${baseDirectory / "migration"}")
try { try {
runWithDataSource(config) { runWithDataSource(config, baseDirectory, classLoader) {
MigrationExporter(baseDirectory, config.dataSourceProperties, classLoader, it).generateMigrationForCorDapp(schemaClass) MigrationExporter(baseDirectory, config.dataSourceProperties, classLoader, it).generateMigrationForCorDapp(schemaClass)
} }
} catch (e: Exception) { } catch (e: Exception) {
@ -211,12 +212,9 @@ private fun getMigrationOutput(baseDirectory: Path, options: OptionSet): Writer
} }
} }
private fun runWithDataSource(config: Configuration, withDatasource: (DataSource) -> Unit) { private fun runWithDataSource(config: Configuration, baseDirectory: Path, classLoader: ClassLoader, withDatasource: (DataSource) -> Unit) {
val cfg = HikariConfig(config.dataSourceProperties) val driversFolder = baseDirectory / "drivers"
cfg.maximumPoolSize = 1 return withDatasource(createDatasourceFromDriverJars(config.dataSourceProperties, classLoader, driversFolder))
return HikariDataSource(cfg).use { dataSource ->
withDatasource(dataSource)
}
} }
private fun errorAndExit(message: String?) { private fun errorAndExit(message: String?) {