From aa01ef638b3a3788def6fcad185a913d25d21a6c Mon Sep 17 00:00:00 2001 From: Rick Parker Date: Thu, 7 Jun 2018 09:12:25 +0100 Subject: [PATCH 1/2] =?UTF-8?q?CORDA-1589=20Flow=20hospital=20reports=20in?= =?UTF-8?q?correct=20number=20of=20patients=20and=20fix=E2=80=A6=20(#3315)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * CORDA-1589 Flow hospital reports incorrect number of patients and fix flakey associated test. * Compare before and after count, not against zero. There's a leak in killFlow that will be easier to address in or after Shams PR. --- .../corda/node/services/statemachine/StaffedFlowHospital.kt | 2 +- .../corda/node/services/statemachine/RetryFlowMockTest.kt | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/node/src/main/kotlin/net/corda/node/services/statemachine/StaffedFlowHospital.kt b/node/src/main/kotlin/net/corda/node/services/statemachine/StaffedFlowHospital.kt index 3f66c15c4b..eb7806e9b8 100644 --- a/node/src/main/kotlin/net/corda/node/services/statemachine/StaffedFlowHospital.kt +++ b/node/src/main/kotlin/net/corda/node/services/statemachine/StaffedFlowHospital.kt @@ -17,7 +17,7 @@ object StaffedFlowHospital : FlowHospital { private val patients = ConcurrentHashMap() - val numberOfPatients = patients.size + val numberOfPatients get() = patients.size class MedicalHistory { val records: MutableList = mutableListOf() diff --git a/node/src/test/kotlin/net/corda/node/services/statemachine/RetryFlowMockTest.kt b/node/src/test/kotlin/net/corda/node/services/statemachine/RetryFlowMockTest.kt index 710e545540..dbcd28796a 100644 --- a/node/src/test/kotlin/net/corda/node/services/statemachine/RetryFlowMockTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/statemachine/RetryFlowMockTest.kt @@ -89,8 +89,11 @@ class RetryFlowMockTest { @Test fun `Patient records do not leak in hospital`() { + val patientCountBefore = StaffedFlowHospital.numberOfPatients assertEquals(Unit, internalNodeA.startFlow(RetryFlow(1)).get()) - assertEquals(0, StaffedFlowHospital.numberOfPatients) + // Need to make sure the state machine has finished. Otherwise this test is flakey. + mockNet.waitQuiescent() + assertEquals(patientCountBefore, StaffedFlowHospital.numberOfPatients) assertEquals(2, RetryFlow.count) } } From c474c04503004f2b2f4c37e23ab4b6250b911651 Mon Sep 17 00:00:00 2001 From: Tudor Malene Date: Thu, 7 Jun 2018 14:05:28 +0100 Subject: [PATCH 2/2] ENT-1837 better error message on db failure (#3318) --- .../internal/persistence/CordaPersistence.kt | 4 +++- .../net/corda/node/internal/AbstractNode.kt | 23 +++++++++++++++---- .../net/corda/node/internal/NodeStartup.kt | 4 ++++ 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/CordaPersistence.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/CordaPersistence.kt index 850436efa6..48087de79b 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/CordaPersistence.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/CordaPersistence.kt @@ -264,4 +264,6 @@ private fun Throwable.hasSQLExceptionCause(): Boolean = null -> false is SQLException -> true else -> cause?.hasSQLExceptionCause() ?: false - } \ No newline at end of file + } + +class CouldNotCreateDataSourceException(override val message: String?, override val cause: Throwable? = null) : Exception() \ No newline at end of file diff --git a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt index 1cebff87fc..bfe1856851 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -3,6 +3,7 @@ package net.corda.node.internal import com.codahale.metrics.MetricRegistry import com.google.common.collect.MutableClassToInstanceMap import com.google.common.util.concurrent.MoreExecutors +import com.zaxxer.hikari.pool.HikariPool import net.corda.confidential.SwapIdentitiesFlow import net.corda.confidential.SwapIdentitiesHandler import net.corda.core.CordaException @@ -70,6 +71,7 @@ import net.corda.nodeapi.internal.NodeInfoAndSigned import net.corda.nodeapi.internal.SignedNodeInfo import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.nodeapi.internal.persistence.CordaPersistence +import net.corda.nodeapi.internal.persistence.CouldNotCreateDataSourceException import net.corda.nodeapi.internal.persistence.DatabaseConfig import net.corda.nodeapi.internal.persistence.HibernateConfiguration import net.corda.nodeapi.internal.storeLegalIdentity @@ -219,7 +221,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration, networkMapCache.clearNetworkMapCache() } } - + open fun start(): StartedNode { check(started == null) { "Node has already been started" } if (configuration.devMode) { @@ -1001,6 +1003,11 @@ class ConfigurationException(message: String) : CordaException(message) */ internal class NetworkMapCacheEmptyException : Exception() +/** + * Creates the connection pool to the database. + * + *@throws [CouldNotCreateDataSourceException] + */ fun configureDatabase(hikariProperties: Properties, databaseConfig: DatabaseConfig, wellKnownPartyFromX500Name: (CordaX500Name) -> Party?, @@ -1011,7 +1018,15 @@ fun configureDatabase(hikariProperties: Properties, // so we end up providing both descriptor and converter. We should re-examine this in later versions to see if // either Hibernate can be convinced to stop warning, use the descriptor by default, or something else. JavaTypeDescriptorRegistry.INSTANCE.addDescriptor(AbstractPartyDescriptor(wellKnownPartyFromX500Name, wellKnownPartyFromAnonymous)) - val dataSource = DataSourceFactory.createDataSource(hikariProperties) - val attributeConverters = listOf(AbstractPartyToX500NameAsStringConverter(wellKnownPartyFromX500Name, wellKnownPartyFromAnonymous)) - return CordaPersistence(dataSource, databaseConfig, schemaService.schemaOptions.keys, attributeConverters) + try { + val dataSource = DataSourceFactory.createDataSource(hikariProperties) + val attributeConverters = listOf(AbstractPartyToX500NameAsStringConverter(wellKnownPartyFromX500Name, wellKnownPartyFromAnonymous)) + return CordaPersistence(dataSource, databaseConfig, schemaService.schemaOptions.keys, attributeConverters) + } catch (ex: Exception) { + when { + ex is HikariPool.PoolInitializationException -> throw CouldNotCreateDataSourceException("Could not connect to the database. Please check your JDBC connection URL, or the connectivity to the database.") + ex.cause is ClassNotFoundException -> throw CouldNotCreateDataSourceException("Could not find the database driver class. Please add it to the 'drivers' folders. See: https://docs.corda.net/corda-configuration-file.html") + else -> throw CouldNotCreateDataSourceException("Could not create the DataSource: ${ex.message}", ex) + } + } } diff --git a/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt b/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt index 6da2426aed..1c0dd7bf77 100644 --- a/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt +++ b/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt @@ -27,6 +27,7 @@ import net.corda.node.utilities.registration.HTTPNetworkRegistrationService import net.corda.node.utilities.registration.NodeRegistrationHelper import net.corda.nodeapi.internal.addShutdownHook import net.corda.nodeapi.internal.config.UnknownConfigurationKeysException +import net.corda.nodeapi.internal.persistence.CouldNotCreateDataSourceException import net.corda.tools.shell.InteractiveShell import org.fusesource.jansi.Ansi import org.fusesource.jansi.AnsiConsole @@ -128,6 +129,9 @@ open class NodeStartup(val args: Array) { try { cmdlineOptions.baseDirectory.createDirectories() startNode(conf, versionInfo, startTime, cmdlineOptions) + } catch (e: CouldNotCreateDataSourceException) { + logger.error(e.message, e.cause) + return false } catch (e: CheckpointIncompatibleException) { logger.error(e.message) return false