From 3aaa176dd4bbdf94f9b58ef10622fc3afbeebfdc Mon Sep 17 00:00:00 2001 From: Viktor Kolomeyko Date: Mon, 23 Apr 2018 13:55:40 +0100 Subject: [PATCH] CORDA-1355: Introduce a dedicated property which controls what is going to be in scope for classpath scanning (#2977) * CORDA-1355: Introduce a dedicated property which controls what is going to be in scope for classpath scanning * CORDA-1355: Update change log * CORDA-1355: Minor change to improve readability. * CORDA-1355: Custom serializers documentation update to mention new system property. --- docs/source/changelog.rst | 2 ++ docs/source/cordapp-custom-serializers.rst | 9 ++++++-- .../amqp/AMQPSerializationScheme.kt | 20 ++++++++++++---- .../net/corda/vega/SimmValuationTest.kt | 15 ++++++++++++ .../testing/node/internal/DriverDSLImpl.kt | 23 +++++++++++++++---- 5 files changed, 57 insertions(+), 12 deletions(-) diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst index f20cd009da..d1e7355e31 100644 --- a/docs/source/changelog.rst +++ b/docs/source/changelog.rst @@ -53,6 +53,8 @@ Unreleased * java.math.BigInteger serialization support added. +* Fix CORDA-1355: Reduce amount of classpath scanning during integration tests execution. + .. _changelog_v3.1: Version 3.1 diff --git a/docs/source/cordapp-custom-serializers.rst b/docs/source/cordapp-custom-serializers.rst index 4541d18f3d..969348fbad 100644 --- a/docs/source/cordapp-custom-serializers.rst +++ b/docs/source/cordapp-custom-serializers.rst @@ -17,11 +17,16 @@ Custom serializer classes should follow the rules for including classes found in Writing a Custom Serializer --------------------------- Serializers must - * Inherit from net.corda.core.serialization.SerializationCustomSerializer + * Inherit from ``net.corda.core.serialization.SerializationCustomSerializer`` * Provide a proxy class to transform the object to and from * Implement the ``toProxy`` and ``fromProxy`` methods + * Be either included into CorDapp Jar or made known to the running process via ``amqp.custom.serialization.scanSpec`` +system property. +This system property may be necessary to be able to discover custom serializer in the classpath. At a minimum the value +of the property should include comma separated set of packages where custom serializers located. Full syntax includes +scanning specification as defined by: `` -Serializers inheriting from SerializationCustomSerializer have to implement two methods and two types. +Serializers inheriting from ``SerializationCustomSerializer`` have to implement two methods and two types. Example ------- diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/AMQPSerializationScheme.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/AMQPSerializationScheme.kt index 4d0ca7b924..141f9af528 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/AMQPSerializationScheme.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/AMQPSerializationScheme.kt @@ -41,16 +41,26 @@ abstract class AbstractAMQPSerializationScheme( // TODO: This method of initialisation for the Whitelist and plugin serializers will have to change // when we have per-cordapp contexts and dynamic app reloading but for now it's the easiest way companion object { + + const val SCAN_SPEC_PROP_NAME = "amqp.custom.serialization.scanSpec" + private val serializationWhitelists: List by lazy { ServiceLoader.load(SerializationWhitelist::class.java, this::class.java.classLoader).toList() + DefaultWhitelist } private val customSerializers: List> by lazy { - FastClasspathScanner().addClassLoader(this::class.java.classLoader).scan() - .getNamesOfClassesImplementing(SerializationCustomSerializer::class.java) - .mapNotNull { this::class.java.classLoader.loadClass(it).asSubclass(SerializationCustomSerializer::class.java) } - .filterNot { Modifier.isAbstract(it.modifiers) } - .map { it.kotlin.objectOrNewInstance() } + + val scanSpec: String? = System.getProperty(SCAN_SPEC_PROP_NAME) + + if(scanSpec == null) { + emptyList() + } else { + FastClasspathScanner(scanSpec).addClassLoader(this::class.java.classLoader).scan() + .getNamesOfClassesImplementing(SerializationCustomSerializer::class.java) + .mapNotNull { this::class.java.classLoader.loadClass(it).asSubclass(SerializationCustomSerializer::class.java) } + .filterNot { Modifier.isAbstract(it.modifiers) } + .map { it.kotlin.objectOrNewInstance() } + } } } diff --git a/samples/simm-valuation-demo/src/integration-test/kotlin/net/corda/vega/SimmValuationTest.kt b/samples/simm-valuation-demo/src/integration-test/kotlin/net/corda/vega/SimmValuationTest.kt index 4516c0e540..22944e7553 100644 --- a/samples/simm-valuation-demo/src/integration-test/kotlin/net/corda/vega/SimmValuationTest.kt +++ b/samples/simm-valuation-demo/src/integration-test/kotlin/net/corda/vega/SimmValuationTest.kt @@ -2,7 +2,9 @@ package net.corda.vega import com.opengamma.strata.product.common.BuySell import net.corda.core.identity.CordaX500Name +import net.corda.core.internal.packageName import net.corda.core.utilities.getOrThrow +import net.corda.nodeapi.internal.serialization.amqp.AbstractAMQPSerializationScheme import net.corda.testing.core.DUMMY_BANK_A_NAME import net.corda.testing.core.DUMMY_BANK_B_NAME import net.corda.testing.driver.DriverParameters @@ -12,7 +14,10 @@ import net.corda.vega.api.PortfolioApi import net.corda.vega.api.PortfolioApiUtils import net.corda.vega.api.SwapDataModel import net.corda.vega.api.SwapDataView +import net.corda.vega.plugin.customserializers.CurrencyParameterSensitivitiesSerializer import org.assertj.core.api.Assertions.assertThat +import org.junit.After +import org.junit.Before import org.junit.Test import java.math.BigDecimal import java.time.LocalDate @@ -26,6 +31,16 @@ class SimmValuationTest { val testTradeId = "trade1" } + @Before + fun setup() { + System.setProperty(AbstractAMQPSerializationScheme.SCAN_SPEC_PROP_NAME, CurrencyParameterSensitivitiesSerializer::class.packageName) + } + + @After + fun tearDown() { + System.clearProperty(AbstractAMQPSerializationScheme.SCAN_SPEC_PROP_NAME) + } + @Test fun `runs SIMM valuation demo`() { driver(DriverParameters(isDebug = true, extraCordappPackagesToScan = listOf("net.corda.vega.contracts", "net.corda.vega.plugin.customserializers"))) { diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt index 08f7028bb3..36f6661364 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt @@ -39,6 +39,7 @@ import net.corda.nodeapi.internal.crypto.X509KeyStore import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.nodeapi.internal.network.NetworkParametersCopier import net.corda.nodeapi.internal.network.NodeInfoFilesCopier +import net.corda.nodeapi.internal.serialization.amqp.AbstractAMQPSerializationScheme import net.corda.testing.core.ALICE_NAME import net.corda.testing.core.BOB_NAME import net.corda.testing.core.DUMMY_BANK_A_NAME @@ -755,10 +756,11 @@ class DriverDSLImpl( val systemProperties = mutableMapOf( "name" to config.corda.myLegalName, "visualvm.display.name" to "corda-${config.corda.myLegalName}", - "java.io.tmpdir" to System.getProperty("java.io.tmpdir"), // Inherit from parent process "log4j2.debug" to if (debugPort != null) "true" else "false" ) + systemProperties += inheritFromParentProcess() + if (cordappPackages.isNotEmpty()) { systemProperties += Node.scanPackagesSystemProperty to cordappPackages.joinToString(Node.scanPackagesSeparator) } @@ -802,15 +804,26 @@ class DriverDSLImpl( className = className, // cannot directly get class for this, so just use string arguments = listOf("--base-directory", handle.baseDirectory.toString()), jdwpPort = debugPort, - extraJvmArguments = listOf( - "-Dname=node-${handle.p2pAddress}-webserver", - "-Djava.io.tmpdir=${System.getProperty("java.io.tmpdir")}" // Inherit from parent process - ), + extraJvmArguments = listOf("-Dname=node-${handle.p2pAddress}-webserver") + + inheritFromParentProcess().map { "-D${it.first}=${it.second}" }, workingDirectory = null, maximumHeapSize = maximumHeapSize ) } + private val propertiesInScope = setOf("java.io.tmpdir", AbstractAMQPSerializationScheme.SCAN_SPEC_PROP_NAME) + + private fun inheritFromParentProcess() : Iterable> { + return propertiesInScope.flatMap { propName -> + val propValue : String? = System.getProperty(propName) + if(propValue == null) { + emptySet() + } else { + setOf(Pair(propName, propValue)) + } + } + } + private fun NodeHandleInternal.toWebServerConfig(): Config { var config = ConfigFactory.empty()