From 42a2ed98e20c86e90ed36f9f9bc11b3da98ffb87 Mon Sep 17 00:00:00 2001 From: Viktor Kolomeyko Date: Fri, 24 Jan 2020 10:20:08 +0000 Subject: [PATCH] CORDA-2942: Allow exception from `CordaService` creation to propagate (#5884) * CORDA-2942: Allow exception from `CordaService` creation to propagate It will ultimately be thrown from Node's `start()` method terminating the node start-up sequence. * CORDA-2942: Be lenient when retrievign the name of the Notary Some tests setup such that they do nto have Notary running. --- detekt-baseline.xml | 6 +----- docs/source/api-service-classes.rst | 3 ++- .../node/services/CordaServiceIssueOnceAtStartupTests.kt | 2 +- .../src/main/kotlin/net/corda/node/internal/AbstractNode.kt | 4 +++- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/detekt-baseline.xml b/detekt-baseline.xml index e777ed7ac8..109c042ca7 100644 --- a/detekt-baseline.xml +++ b/detekt-baseline.xml @@ -1906,7 +1906,6 @@ MaxLineLength:CordaRPCOps.kt$sorting: Sort = Sort(emptySet()) MaxLineLength:CordaRPCOpsImplTest.kt$CordaRPCOpsImplTest$assertThatCode { rpc.startFlow(::SoftLock, cash.ref, Duration.ofSeconds(1)).returnValue.getOrThrow() }.doesNotThrowAnyException() MaxLineLength:CordaRPCOpsImplTest.kt$CordaRPCOpsImplTest$val cash = rpc.startFlow(::CashIssueFlow, 10.DOLLARS, issuerRef, notary).returnValue.getOrThrow().stx.tx.outRefsOfType<Cash.State>().single() - MaxLineLength:CordaSSHAuthInfo.kt$CordaSSHAuthInfo : AuthInfo MaxLineLength:CordaServiceTest.kt$CordaServiceTest$mockNet = MockNetwork(MockNetworkParameters(threadPerNode = true, cordappsForAllNodes = listOf(FINANCE_CONTRACTS_CORDAPP, enclosedCordapp()))) MaxLineLength:CordaServiceTest.kt$CordaServiceTest.CordaServiceThatRequiresThreadContextClassLoader$assertNotNull(Thread.currentThread().contextClassLoader, "thread context classloader should not be null during service initialisation") MaxLineLength:CordaUtils.kt$ private fun owns(packageName: String, fullClassName: String): Boolean @@ -3889,6 +3888,7 @@ SpreadOperator:WithContracts.kt$WithContracts$(owner, magicNumber, *others) SpreadOperator:X509Utilities.kt$X509Utilities$(*certificates) ThrowsCount:AMQPTypeIdentifierParser.kt$AMQPTypeIdentifierParser$// Make sure our inputs aren't designed to blow things up. private fun validate(typeString: String) + ThrowsCount:AbstractNode.kt$AbstractNode$private fun installCordaServices() ThrowsCount:AbstractNode.kt$fun CordaPersistence.startHikariPool(hikariProperties: Properties, databaseConfig: DatabaseConfig, schemas: Set<MappedSchema>, metricRegistry: MetricRegistry? = null, cordappLoader: CordappLoader? = null, currentDir: Path? = null, ourName: CordaX500Name) ThrowsCount:ArtemisMessagingServer.kt$ArtemisMessagingServer$// TODO: Maybe wrap [IOException] on a key store load error so that it's clearly splitting key store loading from // Artemis IO errors @Throws(IOException::class, AddressBindingException::class, KeyStoreException::class) private fun configureAndStartServer() ThrowsCount:BrokerJaasLoginModule.kt$BaseBrokerJaasLoginModule$@Suppress("DEPRECATION") // should use java.security.cert.X509Certificate protected fun getUsernamePasswordAndCerts(): Triple<String, String, Array<javax.security.cert.X509Certificate>?> @@ -4406,9 +4406,6 @@ WildcardImport:CertificateRevocationListNodeTests.kt$import net.corda.nodeapi.internal.crypto.* WildcardImport:CertificateRevocationListNodeTests.kt$import org.bouncycastle.asn1.x509.* WildcardImport:CertificatesUtils.kt$import net.corda.nodeapi.internal.crypto.* - WildcardImport:CheckpointDumperImpl.kt$import com.fasterxml.jackson.databind.* - WildcardImport:CheckpointDumperImpl.kt$import net.corda.core.internal.* - WildcardImport:CheckpointDumperImpl.kt$import net.corda.node.services.statemachine.* WildcardImport:CheckpointSerializationAPI.kt$import net.corda.core.serialization.* WildcardImport:ClassCarpenter.kt$import org.objectweb.asm.Opcodes.* WildcardImport:ClassCarpenterTestUtils.kt$import net.corda.serialization.internal.amqp.* @@ -4561,7 +4558,6 @@ WildcardImport:FlowFrameworkTests.kt$import net.corda.core.flows.* WildcardImport:FlowFrameworkTests.kt$import net.corda.testing.node.internal.* WildcardImport:FlowFrameworkTripartyTests.kt$import net.corda.testing.node.internal.* - WildcardImport:FlowLogic.kt$import net.corda.core.internal.* WildcardImport:FlowLogicRefFactoryImpl.kt$import net.corda.core.flows.* WildcardImport:FlowMatchers.kt$import net.corda.testing.internal.matchers.* WildcardImport:FlowOverrideTests.kt$import net.corda.core.flows.* diff --git a/docs/source/api-service-classes.rst b/docs/source/api-service-classes.rst index 2216e85694..a5da2cf6d0 100644 --- a/docs/source/api-service-classes.rst +++ b/docs/source/api-service-classes.rst @@ -8,7 +8,8 @@ API: Service Classes ==================== Service classes are long-lived instances that can trigger or be triggered by flows from within a node. A Service class is limited to a -single instance per node. During startup, the node handles the creation of the service. +single instance per node. During startup, the node handles the creation of the service. If there is problem when instantiating service +the node will report in the log what the problem was and terminate. Services allow related, reusable, functions to be separated into their own class where their functionality is grouped together. These functions can then be called from other services or flows. diff --git a/node/src/integration-test/kotlin/net/corda/node/services/CordaServiceIssueOnceAtStartupTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/CordaServiceIssueOnceAtStartupTests.kt index f3ae99b4dc..6917786c41 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/CordaServiceIssueOnceAtStartupTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/CordaServiceIssueOnceAtStartupTests.kt @@ -80,7 +80,7 @@ class CordaServiceIssueOnceAtStartupTests { // Without the "secret" property service upon instantiation will be subscribed to lifecycle events which would be unwanted. // Also do not do this for Notary val myName = services.myInfo.legalIdentities.single().name - val notaryName = services.networkMapCache.notaryIdentities.single().name + val notaryName = services.networkMapCache.notaryIdentities.firstOrNull()?.name if(java.lang.Boolean.getBoolean(armedPropName) && myName != notaryName) { services.register(observer = MyServiceLifecycleObserver()) } else { 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 84530a3c15..33c5f112b6 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -757,7 +757,6 @@ abstract class AbstractNode(val configuration: NodeConfiguration, // This sets the Cordapp classloader on the contextClassLoader of the current thread, prior to initializing services // Needed because of bug CORDA-2653 - some Corda services can utilise third-party libraries that require access to // the Thread context class loader - val oldContextClassLoader: ClassLoader? = Thread.currentThread().contextClassLoader try { Thread.currentThread().contextClassLoader = cordappLoader.appClassLoader @@ -768,14 +767,17 @@ abstract class AbstractNode(val configuration: NodeConfiguration, } catch (e: NoSuchMethodException) { log.error("${it.name}, as a Corda service, must have a constructor with a single parameter of type " + ServiceHub::class.java.name) + throw e } catch (e: ServiceInstantiationException) { if (e.cause != null) { log.error("Corda service ${it.name} failed to instantiate. Reason was: ${e.cause?.rootMessage}", e.cause) } else { log.error("Corda service ${it.name} failed to instantiate", e) } + throw e } catch (e: Exception) { log.error("Unable to install Corda service ${it.name}", e) + throw e } } } finally {