mirror of
https://github.com/corda/corda.git
synced 2025-06-13 04:38:19 +00:00
There's a bug with the ServiceLoader which leaks a file handle to the app jar on shutdown. This causes an issue if a mock node is restarted in Windows. To avoid the problem completely we no longer overwrite any existing jars, as the jar to be copied will be same anyway.
(cherry picked from commit 0038a86481
)
This commit is contained in:
@ -25,12 +25,14 @@ import net.corda.nodeapi.internal.coreContractClasses
|
|||||||
import net.corda.serialization.internal.DefaultWhitelist
|
import net.corda.serialization.internal.DefaultWhitelist
|
||||||
import org.apache.commons.collections4.map.LRUMap
|
import org.apache.commons.collections4.map.LRUMap
|
||||||
import java.lang.reflect.Modifier
|
import java.lang.reflect.Modifier
|
||||||
|
import java.math.BigInteger
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import java.net.URLClassLoader
|
import java.net.URLClassLoader
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.jar.JarInputStream
|
import java.util.jar.JarInputStream
|
||||||
import java.util.jar.Manifest
|
import java.util.jar.Manifest
|
||||||
|
import java.util.zip.ZipInputStream
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
import kotlin.streams.toList
|
import kotlin.streams.toList
|
||||||
|
|
||||||
@ -142,7 +144,7 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths:
|
|||||||
findServiceFlows(this),
|
findServiceFlows(this),
|
||||||
findSchedulableFlows(this),
|
findSchedulableFlows(this),
|
||||||
findServices(this),
|
findServices(this),
|
||||||
findPlugins(url),
|
findWhitelists(url),
|
||||||
findSerializers(this),
|
findSerializers(this),
|
||||||
findCustomSchemas(this),
|
findCustomSchemas(this),
|
||||||
findAllFlows(this),
|
findAllFlows(this),
|
||||||
@ -265,7 +267,7 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths:
|
|||||||
return contractClasses
|
return contractClasses
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun findPlugins(cordappJarPath: RestrictedURL): List<SerializationWhitelist> {
|
private fun findWhitelists(cordappJarPath: RestrictedURL): List<SerializationWhitelist> {
|
||||||
val whitelists = URLClassLoader(arrayOf(cordappJarPath.url)).use {
|
val whitelists = URLClassLoader(arrayOf(cordappJarPath.url)).use {
|
||||||
ServiceLoader.load(SerializationWhitelist::class.java, it).toList()
|
ServiceLoader.load(SerializationWhitelist::class.java, it).toList()
|
||||||
}
|
}
|
||||||
@ -292,8 +294,6 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private fun <T : Any> loadClass(className: String, type: KClass<T>): Class<out T>? {
|
private fun <T : Any> loadClass(className: String, type: KClass<T>): Class<out T>? {
|
||||||
return try {
|
return try {
|
||||||
appClassLoader.loadClass(className).asSubclass(type.java)
|
appClassLoader.loadClass(className).asSubclass(type.java)
|
||||||
@ -306,6 +306,7 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO Remove this class as rootPackageName is never non-null.
|
||||||
/** @property rootPackageName only this package and subpackages may be extracted from [url], or null to allow all packages. */
|
/** @property rootPackageName only this package and subpackages may be extracted from [url], or null to allow all packages. */
|
||||||
private data class RestrictedURL(val url: URL, val rootPackageName: String?) {
|
private data class RestrictedURL(val url: URL, val rootPackageName: String?) {
|
||||||
val qualifiedNamePrefix: String get() = rootPackageName?.let { "$it." } ?: ""
|
val qualifiedNamePrefix: String get() = rootPackageName?.let { "$it." } ?: ""
|
||||||
@ -359,14 +360,14 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths:
|
|||||||
|
|
||||||
fun getAllStandardClasses(): List<String> {
|
fun getAllStandardClasses(): List<String> {
|
||||||
return scanResult
|
return scanResult
|
||||||
.getAllStandardClasses()
|
.allStandardClasses
|
||||||
.names
|
.names
|
||||||
.filter { it.startsWith(qualifiedNamePrefix) }
|
.filter { it.startsWith(qualifiedNamePrefix) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getAllInterfaces(): List<String> {
|
fun getAllInterfaces(): List<String> {
|
||||||
return scanResult
|
return scanResult
|
||||||
.getAllInterfaces()
|
.allInterfaces
|
||||||
.names
|
.names
|
||||||
.filter { it.startsWith(qualifiedNamePrefix) }
|
.filter { it.startsWith(qualifiedNamePrefix) }
|
||||||
}
|
}
|
||||||
@ -386,13 +387,32 @@ class MultipleCordappsForFlowException(message: String) : Exception(message)
|
|||||||
class CordappInvalidVersionException(msg: String) : Exception(msg)
|
class CordappInvalidVersionException(msg: String) : Exception(msg)
|
||||||
|
|
||||||
abstract class CordappLoaderTemplate : CordappLoader {
|
abstract class CordappLoaderTemplate : CordappLoader {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
private val logger = contextLogger()
|
||||||
|
}
|
||||||
|
|
||||||
override val flowCordappMap: Map<Class<out FlowLogic<*>>, Cordapp> by lazy {
|
override val flowCordappMap: Map<Class<out FlowLogic<*>>, Cordapp> by lazy {
|
||||||
cordapps.flatMap { corDapp -> corDapp.allFlows.map { flow -> flow to corDapp } }
|
cordapps.flatMap { corDapp -> corDapp.allFlows.map { flow -> flow to corDapp } }
|
||||||
.groupBy { it.first }
|
.groupBy { it.first }
|
||||||
.mapValues { entry ->
|
.mapValues { entry ->
|
||||||
if (entry.value.size > 1) {
|
if (entry.value.size > 1) {
|
||||||
|
logger.error("There are multiple CorDapp JARs on the classpath for flow " +
|
||||||
|
"${entry.value.first().first.name}: [ ${entry.value.joinToString { it.second.jarPath.toString() }} ].")
|
||||||
|
entry.value.forEach { (_, cordapp) ->
|
||||||
|
ZipInputStream(cordapp.jarPath.openStream()).use { zip ->
|
||||||
|
val ident = BigInteger(64, Random()).toString(36)
|
||||||
|
logger.error("Contents of: ${cordapp.jarPath} will be prefaced with: $ident")
|
||||||
|
var e = zip.nextEntry
|
||||||
|
while (e != null) {
|
||||||
|
logger.error("$ident\t ${e.name}")
|
||||||
|
e = zip.nextEntry
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
throw MultipleCordappsForFlowException("There are multiple CorDapp JARs on the classpath for flow " +
|
throw MultipleCordappsForFlowException("There are multiple CorDapp JARs on the classpath for flow " +
|
||||||
"${entry.value.first().first.name}: [ ${entry.value.joinToString { it.second.name }} ].")
|
"${entry.value.first().first.name}: [ ${entry.value.joinToString { it.second.jarPath.toString() }} ].")
|
||||||
}
|
}
|
||||||
entry.value.single().second
|
entry.value.single().second
|
||||||
}
|
}
|
||||||
|
@ -6,8 +6,8 @@ import net.corda.core.internal.createDirectories
|
|||||||
import net.corda.core.internal.div
|
import net.corda.core.internal.div
|
||||||
import net.corda.core.internal.writeText
|
import net.corda.core.internal.writeText
|
||||||
import net.corda.testing.node.TestCordapp
|
import net.corda.testing.node.TestCordapp
|
||||||
|
import java.nio.file.FileAlreadyExistsException
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.nio.file.StandardCopyOption.REPLACE_EXISTING
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extends the public [TestCordapp] API with internal extensions for use within the testing framework and for internal testing of the platform.
|
* Extends the public [TestCordapp] API with internal extensions for use within the testing framework and for internal testing of the platform.
|
||||||
@ -36,7 +36,11 @@ abstract class TestCordappInternal : TestCordapp() {
|
|||||||
val configDir = (cordappsDir / "config").createDirectories()
|
val configDir = (cordappsDir / "config").createDirectories()
|
||||||
|
|
||||||
jarToCordapp.forEach { jar, cordapp ->
|
jarToCordapp.forEach { jar, cordapp ->
|
||||||
jar.copyToDirectory(cordappsDir, REPLACE_EXISTING)
|
try {
|
||||||
|
jar.copyToDirectory(cordappsDir)
|
||||||
|
} catch (e: FileAlreadyExistsException) {
|
||||||
|
// Ignore if the node already has the same CorDapp jar. This can happen if the node is being restarted.
|
||||||
|
}
|
||||||
val configString = ConfigValueFactory.fromMap(cordapp.config).toConfig().root().render()
|
val configString = ConfigValueFactory.fromMap(cordapp.config).toConfig().root().render()
|
||||||
(configDir / "${jar.fileName.toString().removeSuffix(".jar")}.conf").writeText(configString)
|
(configDir / "${jar.fileName.toString().removeSuffix(".jar")}.conf").writeText(configString)
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user