From c05c1934cf46508ee262c928297ff62bf68b0eed Mon Sep 17 00:00:00 2001 From: Dan Newton Date: Wed, 19 Jan 2022 18:26:26 +0000 Subject: [PATCH] ENT-6547 Remove url classloader in shell loading (#7034) The `corda-shell` jar will already be installed if it exists in the the node's `/drivers` directory. There is no need to include a `URLClassLoader` to load its classes. Rely on the process's main classloader. --- .../net/corda/node/internal/AbstractNode.kt | 6 +- .../net/corda/node/internal/NodeStartup.kt | 3 +- .../node/internal/shell/InteractiveShell.kt | 61 +++++++------------ 3 files changed, 24 insertions(+), 46 deletions(-) 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 23f52db502..623e434a27 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -100,7 +100,6 @@ import net.corda.node.services.api.WritableTransactionStorage import net.corda.node.services.attachments.NodeAttachmentTrustCalculator import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.config.rpc.NodeRpcOptions -import net.corda.node.services.config.shell.toShellConfigMap import net.corda.node.services.config.shouldInitCrashShell import net.corda.node.services.diagnostics.NodeDiagnosticsService import net.corda.node.services.events.NodeSchedulerService @@ -688,11 +687,10 @@ abstract class AbstractNode(val configuration: NodeConfiguration, open fun startShell() { if (configuration.shouldInitCrashShell()) { - val shellConfiguration = configuration.toShellConfigMap() - shellConfiguration["sshdPort"]?.let { + configuration.sshd?.port?.let { log.info("Binding Shell SSHD server on port $it.") } - InteractiveShell.startShellIfInstalled(configuration, shellConfiguration, cordappLoader) + InteractiveShell.startShellIfInstalled(configuration, cordappLoader) } } 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 22136e3d8d..97d4e5a1d1 100644 --- a/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt +++ b/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt @@ -263,9 +263,8 @@ open class NodeStartup : NodeStartupLogging { Node.printBasicNodeInfo("Node for \"$name\" started up and registered in $elapsed sec") // Don't start the shell if there's no console attached. - // Look for shell here?? if (node.configuration.shouldStartLocalShell()) { - InteractiveShell.runLocalShellIfInstalled(node.configuration.baseDirectory, node::stop) + InteractiveShell.runLocalShellIfInstalled(node::stop) } if (node.configuration.shouldStartSSHDaemon()) { Node.printBasicNodeInfo("SSH server listening on port", node.configuration.sshd!!.port.toString()) diff --git a/node/src/main/kotlin/net/corda/node/internal/shell/InteractiveShell.kt b/node/src/main/kotlin/net/corda/node/internal/shell/InteractiveShell.kt index 628f5639ff..0a98a8e4ab 100644 --- a/node/src/main/kotlin/net/corda/node/internal/shell/InteractiveShell.kt +++ b/node/src/main/kotlin/net/corda/node/internal/shell/InteractiveShell.kt @@ -2,12 +2,9 @@ package net.corda.node.internal.shell import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.config.shell.determineUnsafeUsers +import net.corda.node.services.config.shell.toShellConfigMap import net.corda.nodeapi.internal.cordapp.CordappLoader import org.slf4j.LoggerFactory -import java.io.File -import java.net.URLClassLoader -import java.nio.file.Path -import java.nio.file.Paths object InteractiveShell { @@ -20,13 +17,12 @@ object InteractiveShell { private const val RUN_LOCAL_SHELL_METHOD = "runLocalShell" private const val SET_USER_INFO_METHOD = "setUserInfo" - fun startShellIfInstalled(configuration: NodeConfiguration, shellConfiguration: Map, cordappLoader: CordappLoader) { - val shellJar = getSingleShellJarInDriversDirectory(configuration.baseDirectory) - if (shellJar != null) { + fun startShellIfInstalled(configuration: NodeConfiguration, cordappLoader: CordappLoader) { + if (isShellInstalled()) { try { - val classLoader = URLClassLoader(arrayOf(shellJar.toPath().toUri().toURL()), javaClass.classLoader) - setUnsafeUsers(classLoader, configuration) - startShell(classLoader, shellConfiguration, cordappLoader) + val shellConfiguration = configuration.toShellConfigMap() + setUnsafeUsers(configuration) + startShell(shellConfiguration, cordappLoader) } catch (e: Exception) { log.error("Shell failed to start", e) } @@ -36,63 +32,48 @@ object InteractiveShell { /** * Only call this after [startShellIfInstalled] has been called or the required classes will not be loaded into the current classloader. */ - fun runLocalShellIfInstalled(baseDirectory: Path, onExit: () -> Unit = {}) { - val shellJar = getSingleShellJarInDriversDirectory(baseDirectory) - if (shellJar != null) { + fun runLocalShellIfInstalled(onExit: () -> Unit = {}) { + if (isShellInstalled()) { try { - runLocalShell(javaClass.classLoader, onExit) + runLocalShell(onExit) } catch (e: Exception) { log.error("Shell failed to start", e) } } } - private fun getSingleShellJarInDriversDirectory(baseDirectory: Path): File? { - val uriToDriversDirectory = Paths.get("${baseDirectory}/drivers").toUri() - val files = File(uriToDriversDirectory) - .listFiles() - ?.filter { "corda-shell" in it.name } - ?.filter { "jar" == it.extension } - ?: emptyList() - - return if (files.isNotEmpty()) { - check(files.size == 1) { - ("More than one corda-shell jar installed in /drivers directory. " + - "Remove all corda-shell jars except for the one that should be used").also { - log.error(it) - } - } - files.single() - } else { - null + private fun isShellInstalled(): Boolean { + return try { + javaClass.classLoader.loadClass(INTERACTIVE_SHELL_CLASS) + true + } catch (e: ClassNotFoundException) { + false } } - private fun setUnsafeUsers(classLoader: ClassLoader, configuration: NodeConfiguration) { + private fun setUnsafeUsers(configuration: NodeConfiguration) { val unsafeUsers = determineUnsafeUsers(configuration) - val clazz = classLoader.loadClass(CRASH_COMMAND_CLASS) + val clazz = javaClass.classLoader.loadClass(CRASH_COMMAND_CLASS) clazz.getDeclaredMethod(SET_USER_INFO_METHOD, Set::class.java, Boolean::class.java, Boolean::class.java) .invoke(null, unsafeUsers, true, false) log.info("Setting unsafe users as: $unsafeUsers") } - private fun startShell(classLoader: ClassLoader, shellConfiguration: Map, cordappLoader: CordappLoader) { - val clazz = classLoader.loadClass(INTERACTIVE_SHELL_CLASS) + private fun startShell(shellConfiguration: Map, cordappLoader: CordappLoader) { + val clazz = javaClass.classLoader.loadClass(INTERACTIVE_SHELL_CLASS) val instance = clazz.getDeclaredConstructor() .apply { this.isAccessible = true } .newInstance() clazz.getDeclaredMethod(START_SHELL_METHOD, Map::class.java, ClassLoader::class.java, Boolean::class.java) .invoke(instance, shellConfiguration, cordappLoader.appClassLoader, false) - log.info("INTERACTIVE SHELL STARTED ABSTRACT NODE") } - private fun runLocalShell(classLoader: ClassLoader, onExit: () -> Unit = {}) { - val clazz = classLoader.loadClass(INTERACTIVE_SHELL_CLASS) + private fun runLocalShell(onExit: () -> Unit = {}) { + val clazz = javaClass.classLoader.loadClass(INTERACTIVE_SHELL_CLASS) // Gets the existing instance created by [startShell] as [InteractiveShell] is a static instance val instance = clazz.getDeclaredConstructor() .apply { this.isAccessible = true } .newInstance() clazz.getDeclaredMethod(RUN_LOCAL_SHELL_METHOD, Function0::class.java).invoke(instance, onExit) - log.info("INTERACTIVE SHELL STARTED") } } \ No newline at end of file