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 60f2f28cb0..0535729286 100644 --- a/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt +++ b/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt @@ -67,64 +67,87 @@ open class NodeStartup: CordaCliWrapper("corda", "Runs a Corda Node") { */ override fun runProgram(): Int { val startTime = System.currentTimeMillis() - if (!canNormalizeEmptyPath()) { - println("You are using a version of Java that is not supported (${System.getProperty("java.version")}). Please upgrade to the latest supported version.") - println("Corda will now exit...") - return ExitCodes.FAILURE - } + // Step 1. Check for supported Java version. + if (!isValidJavaVersion()) return ExitCodes.FAILURE - val registrationMode = checkRegistrationMode() - - // TODO: Reconsider if automatic re-registration should be applied when something failed during initial registration. - // There might be cases where the node user should investigate what went wrong before registering again. - if (registrationMode && !cmdLineOptions.isRegistration) { - println("Node was started before with `--initial-registration`, but the registration was not completed.\nResuming registration.") - // Pretend that the node was started with `--initial-registration` to help prevent user error. - cmdLineOptions.isRegistration = true - } - - // We do the single node check before we initialise logging so that in case of a double-node start it + // Step 2. We do the single node check before we initialise logging so that in case of a double-node start it // doesn't mess with the running node's logs. enforceSingleNodeIsRunning(cmdLineOptions.baseDirectory) + // Step 3. Initialise logging. initLogging() - // Register all cryptography [Provider]s. + + // Step 4. Register all cryptography [Provider]s. // Required to install our [SecureRandom] before e.g., UUID asks for one. // This needs to go after initLogging(netty clashes with our logging). Crypto.registerProviders() + // Step 5. Print banner and basic node info. val versionInfo = getVersionInfo() - drawBanner(versionInfo) Node.printBasicNodeInfo(LOGS_CAN_BE_FOUND_IN_STRING, System.getProperty("log-path")) + // Step 6. Load and validate node configuration. val configuration = (attempt { loadConfiguration() }.doOnException(handleConfigurationLoadingError(cmdLineOptions.configFile)) as? Try.Success)?.let(Try.Success::value) ?: return ExitCodes.FAILURE - val errors = configuration.validate() if (errors.isNotEmpty()) { logger.error("Invalid node configuration. Errors were:${System.lineSeparator()}${errors.joinToString(System.lineSeparator())}") return ExitCodes.FAILURE } + // Step 7. Configuring special serialisation requirements, i.e., bft-smart relies on Java serialization. attempt { banJavaSerialisation(configuration) }.doOnException { error -> error.logAsUnexpected("Exception while configuring serialisation") } as? Try.Success ?: return ExitCodes.FAILURE + // Step 8. Any actions required before starting up the Corda network layer. attempt { preNetworkRegistration(configuration) }.doOnException(handleRegistrationError) as? Try.Success ?: return ExitCodes.FAILURE - cmdLineOptions.nodeRegistrationOption?.let { - // Null checks for [compatibilityZoneURL], [rootTruststorePath] and [rootTruststorePassword] has been done in [CmdLineOptions.loadConfig] - attempt { registerWithNetwork(configuration, versionInfo, it) }.doOnException(handleRegistrationError) as? Try.Success ?: return ExitCodes.FAILURE - // At this point the node registration was successful. We can delete the marker file. - deleteNodeRegistrationMarker(cmdLineOptions.baseDirectory) - return ExitCodes.SUCCESS + // Step 9. Check if in registration mode. + checkAndRunRegistrationMode(configuration, versionInfo)?.let { + return if (it) ExitCodes.SUCCESS + else ExitCodes.FAILURE } + // Step 10. Log startup info. logStartupInfo(versionInfo, configuration) + // Step 11. Start node: create the node, check for other command-line options, add extra logging etc. attempt { startNode(configuration, versionInfo, startTime) }.doOnSuccess { logger.info("Node exiting successfully") }.doOnException(handleStartError) as? Try.Success ?: return ExitCodes.FAILURE return ExitCodes.SUCCESS } + private fun checkAndRunRegistrationMode(configuration: NodeConfiguration, versionInfo: VersionInfo): Boolean? { + checkUnfinishedRegistration() + cmdLineOptions.nodeRegistrationOption?.let { + // Null checks for [compatibilityZoneURL], [rootTruststorePath] and [rootTruststorePassword] has been done in [CmdLineOptions.loadConfig] + attempt { registerWithNetwork(configuration, versionInfo, it) }.doOnException(handleRegistrationError) as? Try.Success + ?: return false + // At this point the node registration was successful. We can delete the marker file. + deleteNodeRegistrationMarker(cmdLineOptions.baseDirectory) + return true + } + return null + } + + private fun isValidJavaVersion(): Boolean { + if (!canNormalizeEmptyPath()) { + println("You are using a version of Java that is not supported (${System.getProperty("java.version")}). Please upgrade to the latest supported version.") + println("Corda will now exit...") + return false + } + return true + } + + // TODO: Reconsider if automatic re-registration should be applied when something failed during initial registration. + // There might be cases where the node user should investigate what went wrong before registering again. + private fun checkUnfinishedRegistration() { + if (checkRegistrationMode() && !cmdLineOptions.isRegistration) { + println("Node was started before with `--initial-registration`, but the registration was not completed.\nResuming registration.") + // Pretend that the node was started with `--initial-registration` to help prevent user error. + cmdLineOptions.isRegistration = true + } + } + private fun attempt(action: () -> RESULT): Try = Try.on(action) private fun Exception.isExpectedWhenStartingNode() = startNodeExpectedErrors.any { error -> error.isInstance(this) } @@ -272,6 +295,7 @@ open class NodeStartup: CordaCliWrapper("corda", "Runs a Corda Node") { logLoadedCorDapps(node.services.cordappProvider.cordapps) node.nodeReadyFuture.thenMatch({ + // Elapsed time in seconds. We used 10 / 100.0 and not directly / 1000.0 to only keep two decimal digits. val elapsed = (System.currentTimeMillis() - startTime) / 10 / 100.0 val name = nodeInfo.legalIdentitiesAndCerts.first().name.organisation Node.printBasicNodeInfo("Node for \"$name\" started up and registered in $elapsed sec")