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.
This commit is contained in:
Dan Newton 2022-01-19 18:26:26 +00:00 committed by GitHub
parent bb55d93915
commit c05c1934cf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 24 additions and 46 deletions

View File

@ -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<S>(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)
}
}

View File

@ -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())

View File

@ -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<String, Any?>, 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<String, Any?>, cordappLoader: CordappLoader) {
val clazz = classLoader.loadClass(INTERACTIVE_SHELL_CLASS)
private fun startShell(shellConfiguration: Map<String, Any?>, 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")
}
}