diff --git a/core/src/main/kotlin/net/corda/core/internal/ClassGraphUtils.kt b/core/src/main/kotlin/net/corda/core/internal/ClassGraphUtils.kt new file mode 100644 index 0000000000..b5f70c0823 --- /dev/null +++ b/core/src/main/kotlin/net/corda/core/internal/ClassGraphUtils.kt @@ -0,0 +1,19 @@ +@file:DeleteForDJVM + +package net.corda.core.internal + +import co.paralleluniverse.strands.concurrent.ReentrantLock +import io.github.classgraph.ClassGraph +import io.github.classgraph.ScanResult +import net.corda.core.DeleteForDJVM +import kotlin.concurrent.withLock + +private val pooledScanMutex = ReentrantLock() + +/** + * Use this rather than the built in implementation of [scan] on [ClassGraph]. The built in implementation of [scan] creates + * a thread pool every time resulting in too many threads. This one uses a mutex to restrict concurrency. + */ +fun ClassGraph.pooledScan(): ScanResult { + return pooledScanMutex.withLock { this@pooledScan.scan() } +} diff --git a/core/src/main/kotlin/net/corda/core/internal/ClassLoadingUtils.kt b/core/src/main/kotlin/net/corda/core/internal/ClassLoadingUtils.kt index fb0886e7ea..530c1383aa 100644 --- a/core/src/main/kotlin/net/corda/core/internal/ClassLoadingUtils.kt +++ b/core/src/main/kotlin/net/corda/core/internal/ClassLoadingUtils.kt @@ -19,7 +19,7 @@ import net.corda.core.StubOutForDJVM fun createInstancesOfClassesImplementing(classloader: ClassLoader, clazz: Class): Set { return ClassGraph().addClassLoader(classloader) .enableClassInfo() - .scan() + .pooledScan() .use { it.getClassesImplementing(clazz.name) .filterNot { it.isAbstract } @@ -38,4 +38,4 @@ fun executeWithThreadContextClassLoader(classloader: ClassLoader, fn: Thread.currentThread().contextClassLoader = threadClassLoader } -} \ No newline at end of file +} diff --git a/core/src/main/kotlin/net/corda/core/serialization/internal/AttachmentsClassLoader.kt b/core/src/main/kotlin/net/corda/core/serialization/internal/AttachmentsClassLoader.kt index e2c525c17c..77e9c27ecc 100644 --- a/core/src/main/kotlin/net/corda/core/serialization/internal/AttachmentsClassLoader.kt +++ b/core/src/main/kotlin/net/corda/core/serialization/internal/AttachmentsClassLoader.kt @@ -2,7 +2,6 @@ package net.corda.core.serialization.internal import net.corda.core.CordaException import net.corda.core.KeepForDJVM -import net.corda.core.internal.createInstancesOfClassesImplementing import net.corda.core.contracts.Attachment import net.corda.core.contracts.ContractAttachment import net.corda.core.contracts.TransactionVerificationException.OverlappingAttachmentsException @@ -10,10 +9,6 @@ import net.corda.core.crypto.SecureHash import net.corda.core.crypto.sha256 import net.corda.core.internal.* import net.corda.core.internal.cordapp.targetPlatformVersion -import net.corda.core.serialization.CordaSerializable -import net.corda.core.serialization.SerializationCustomSerializer -import net.corda.core.serialization.SerializationFactory -import net.corda.core.serialization.SerializationWhitelist import net.corda.core.serialization.* import net.corda.core.serialization.internal.AttachmentURLStreamHandlerFactory.toUrl import net.corda.core.utilities.contextLogger @@ -187,7 +182,7 @@ internal object AttachmentsClassLoaderBuilder { private const val CACHE_SIZE = 1000 // This runs in the DJVM so it can't use caffeine. - private val cache: MutableMap, SerializationContext> = createSimpleCache(CACHE_SIZE) + private val cache: MutableMap, SerializationContext> = createSimpleCache, SerializationContext>(CACHE_SIZE).toSynchronised() fun withAttachmentsClassloaderContext(attachments: List, block: (ClassLoader) -> T): T { val attachmentIds = attachments.map { it.id }.toSet() diff --git a/experimental/behave/src/scenario/kotlin/net/corda/behave/scenarios/StepsContainer.kt b/experimental/behave/src/scenario/kotlin/net/corda/behave/scenarios/StepsContainer.kt index dc5a0762e3..73983a0542 100644 --- a/experimental/behave/src/scenario/kotlin/net/corda/behave/scenarios/StepsContainer.kt +++ b/experimental/behave/src/scenario/kotlin/net/corda/behave/scenarios/StepsContainer.kt @@ -6,6 +6,7 @@ import net.corda.behave.scenarios.api.StepsBlock import net.corda.behave.scenarios.api.StepsProvider import net.corda.behave.scenarios.steps.* import net.corda.core.internal.objectOrNewInstance +import net.corda.core.internal.pooledScan import net.corda.core.utilities.contextLogger @Suppress("KDocMissingDocumentation") @@ -18,7 +19,7 @@ class StepsContainer(val state: ScenarioState) : En { ClassGraph() .addClassLoader(this::class.java.classLoader) .enableAllInfo() - .scan() + .pooledScan() .use { it.getClassesImplementing(StepsProvider::class.java.name).loadClasses(StepsProvider::class.java) } .map { it.kotlin.objectOrNewInstance() } } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/ContractsScanning.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/ContractsScanning.kt index 5daef6bc22..63543aaa66 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/ContractsScanning.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/ContractsScanning.kt @@ -9,7 +9,6 @@ import net.corda.core.crypto.SecureHash import net.corda.core.internal.* import org.slf4j.LoggerFactory import java.io.InputStream -import java.net.URLClassLoader import java.nio.file.Files import java.nio.file.Path import java.nio.file.StandardCopyOption @@ -28,7 +27,7 @@ class ContractsJarFile(private val file: Path) : ContractsJar { override val hash: SecureHash by lazy(LazyThreadSafetyMode.NONE, file::hash) override fun scan(): List { - val scanResult = ClassGraph().overrideClasspath(singleton(file)).enableClassInfo().scan() + val scanResult = ClassGraph().overrideClasspath(singleton(file)).enableClassInfo().pooledScan() return scanResult.use { result -> coreContractClasses diff --git a/node/src/main/kotlin/net/corda/node/internal/cordapp/JarScanningCordappLoader.kt b/node/src/main/kotlin/net/corda/node/internal/cordapp/JarScanningCordappLoader.kt index b883258466..5518e985f1 100644 --- a/node/src/main/kotlin/net/corda/node/internal/cordapp/JarScanningCordappLoader.kt +++ b/node/src/main/kotlin/net/corda/node/internal/cordapp/JarScanningCordappLoader.kt @@ -280,7 +280,7 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths: private fun scanCordapp(cordappJarPath: RestrictedURL): RestrictedScanResult { logger.info("Scanning CorDapp in ${cordappJarPath.url}") return cachedScanResult.computeIfAbsent(cordappJarPath) { - val scanResult = ClassGraph().addClassLoader(appClassLoader).overrideClasspath(cordappJarPath.url).enableAllInfo().scan() + val scanResult = ClassGraph().addClassLoader(appClassLoader).overrideClasspath(cordappJarPath.url).enableAllInfo().pooledScan() RestrictedScanResult(scanResult, cordappJarPath.qualifiedNamePrefix) } } diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/AMQPSerializationScheme.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/AMQPSerializationScheme.kt index 360689f62d..d39997f162 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/AMQPSerializationScheme.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/AMQPSerializationScheme.kt @@ -7,10 +7,7 @@ import net.corda.core.DeleteForDJVM import net.corda.core.KeepForDJVM import net.corda.core.StubOutForDJVM import net.corda.core.cordapp.Cordapp -import net.corda.core.internal.isAbstractClass -import net.corda.core.internal.objectOrNewInstance -import net.corda.core.internal.toSynchronised -import net.corda.core.internal.uncheckedCast +import net.corda.core.internal.* import net.corda.core.serialization.* import net.corda.core.utilities.ByteSequence import net.corda.core.utilities.contextLogger @@ -90,7 +87,7 @@ abstract class AbstractAMQPSerializationScheme( .whitelistPackages(scanSpec) .addClassLoader(cl) .enableAllInfo() - .scan() + .pooledScan() .use { val serializerClass = SerializationCustomSerializer::class.java it.getClassesImplementing(serializerClass.name).loadClasses(serializerClass) diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/CustomCordapp.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/CustomCordapp.kt index aa96e1549b..5730468390 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/CustomCordapp.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/CustomCordapp.kt @@ -52,7 +52,7 @@ data class CustomCordapp( val scanResult = classGraph .whitelistPackages(*packages.toTypedArray()) .whitelistClasses(*classes.map { it.name }.toTypedArray()) - .scan() + .pooledScan() scanResult.use { JarOutputStream(file.outputStream()).use { jos -> diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/TestCordappImpl.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/TestCordappImpl.kt index c0d9fcbd16..0190bb2bf4 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/TestCordappImpl.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/TestCordappImpl.kt @@ -55,7 +55,7 @@ data class TestCordappImpl(val scanPackage: String, override val config: Map