diff --git a/node/src/main/kotlin/net/corda/node/internal/DataSourceFactory.kt b/node/src/main/kotlin/net/corda/node/internal/DataSourceFactory.kt index b9f99dff37..7304fa22cc 100644 --- a/node/src/main/kotlin/net/corda/node/internal/DataSourceFactory.kt +++ b/node/src/main/kotlin/net/corda/node/internal/DataSourceFactory.kt @@ -14,10 +14,14 @@ import com.zaxxer.hikari.HikariConfig import com.zaxxer.hikari.HikariDataSource import com.zaxxer.hikari.util.PropertyElf import net.corda.core.internal.declaredField +import net.corda.core.internal.div import org.h2.engine.Database import org.h2.engine.Engine import org.slf4j.LoggerFactory import java.lang.reflect.Modifier +import java.net.URLClassLoader +import java.nio.file.Files +import java.nio.file.Path import java.util.* 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) + } +} \ No newline at end of file diff --git a/tools/dbmigration/build.gradle b/tools/dbmigration/build.gradle index 0bf49b19f9..99c49304f9 100644 --- a/tools/dbmigration/build.gradle +++ b/tools/dbmigration/build.gradle @@ -37,6 +37,7 @@ dependencies{ shadowJar { transform(de.sebastianboegl.gradle.plugins.shadow.transformers.Log4j2PluginsFileTransformer) + archiveName = "migration-tool-${version}.jar" } - +task buildMigrationTool(dependsOn: shadowJar) \ No newline at end of file diff --git a/tools/dbmigration/src/main/kotlin/com/r3/corda/dbmigration/Launcher.kt b/tools/dbmigration/src/main/kotlin/com/r3/corda/dbmigration/Launcher.kt index e6a3d08a2d..84162ec370 100644 --- a/tools/dbmigration/src/main/kotlin/com/r3/corda/dbmigration/Launcher.kt +++ b/tools/dbmigration/src/main/kotlin/com/r3/corda/dbmigration/Launcher.kt @@ -13,8 +13,7 @@ package com.r3.corda.dbmigration import com.typesafe.config.ConfigFactory -import com.zaxxer.hikari.HikariConfig -import com.zaxxer.hikari.HikariDataSource +import com.zaxxer.hikari.util.PropertyElf import joptsimple.OptionException import joptsimple.OptionParser import joptsimple.OptionSet @@ -23,6 +22,7 @@ import net.corda.core.internal.MigrationHelpers import net.corda.core.internal.copyTo import net.corda.core.internal.div import net.corda.core.schemas.MappedSchema +import net.corda.node.internal.DataSourceFactory.createDatasourceFromDriverJars import net.corda.node.internal.cordapp.CordappLoader import net.corda.node.services.config.ConfigHelper import net.corda.node.services.config.parseAsNodeConfiguration @@ -37,6 +37,7 @@ import java.io.FileWriter import java.io.PrintWriter import java.io.Writer import java.net.URLClassLoader +import java.nio.file.Files import java.nio.file.Path import java.nio.file.Paths import java.text.SimpleDateFormat @@ -71,7 +72,7 @@ private fun initOptionParser(): OptionParser = OptionParser().apply { .defaultsTo(Mode.NODE) 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.") .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) { 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)) } 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() } options.has(DRY_RUN) -> { @@ -165,7 +166,7 @@ private fun handleCommand(options: OptionSet, baseDirectory: Path, configFile: P fun generateMigrationFileForSchema(schemaClass: String) { logger.info("Creating database migration files for schema: $schemaClass into ${baseDirectory / "migration"}") try { - runWithDataSource(config) { + runWithDataSource(config, baseDirectory, classLoader) { MigrationExporter(baseDirectory, config.dataSourceProperties, classLoader, it).generateMigrationForCorDapp(schemaClass) } } catch (e: Exception) { @@ -211,12 +212,9 @@ private fun getMigrationOutput(baseDirectory: Path, options: OptionSet): Writer } } -private fun runWithDataSource(config: Configuration, withDatasource: (DataSource) -> Unit) { - val cfg = HikariConfig(config.dataSourceProperties) - cfg.maximumPoolSize = 1 - return HikariDataSource(cfg).use { dataSource -> - withDatasource(dataSource) - } +private fun runWithDataSource(config: Configuration, baseDirectory: Path, classLoader: ClassLoader, withDatasource: (DataSource) -> Unit) { + val driversFolder = baseDirectory / "drivers" + return withDatasource(createDatasourceFromDriverJars(config.dataSourceProperties, classLoader, driversFolder)) } private fun errorAndExit(message: String?) {