diff --git a/.idea/compiler.xml b/.idea/compiler.xml index 518069b359..d62eaf2220 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -14,6 +14,8 @@ + + @@ -189,4 +191,4 @@ - \ No newline at end of file + diff --git a/build.gradle b/build.gradle index d1aaa2a74a..1d81060ce1 100644 --- a/build.gradle +++ b/build.gradle @@ -93,6 +93,7 @@ buildscript { ext.eaagentloader_version = '1.0.3' ext.curator_version = '4.0.0' ext.jsch_version = '0.1.54' + ext.commons_cli_version = '1.4' // Update 121 is required for ObjectInputFilter and at time of writing 131 was latest: ext.java8_minUpdateVersion = '131' diff --git a/core/src/test/kotlin/net/corda/core/contracts/TransactionVerificationExceptionSerialisationTests.kt b/core/src/test/kotlin/net/corda/core/contracts/TransactionVerificationExceptionSerialisationTests.kt index b958602c3f..15bf4a7ea2 100644 --- a/core/src/test/kotlin/net/corda/core/contracts/TransactionVerificationExceptionSerialisationTests.kt +++ b/core/src/test/kotlin/net/corda/core/contracts/TransactionVerificationExceptionSerialisationTests.kt @@ -12,6 +12,7 @@ package net.corda.core.contracts import net.corda.core.crypto.SecureHash import net.corda.core.transactions.LedgerTransaction +import net.corda.nodeapi.internal.serialization.AMQP_RPC_CLIENT_CONTEXT import net.corda.nodeapi.internal.serialization.AllWhitelist import net.corda.nodeapi.internal.serialization.amqp.DeserializationInput import net.corda.nodeapi.internal.serialization.amqp.SerializationOutput @@ -29,13 +30,17 @@ class TransactionVerificationExceptionSerialisationTests { ClassLoader.getSystemClassLoader() ) + private val context get() = AMQP_RPC_CLIENT_CONTEXT + private val txid = SecureHash.allOnesHash private val factory = defaultFactory() @Test fun contractConstraintRejectionTest() { val excp = TransactionVerificationException.ContractConstraintRejection(txid, "This is only a test") - val excp2 = DeserializationInput(factory).deserialize(SerializationOutput(factory).serialize(excp)) + val excp2 = DeserializationInput(factory).deserialize( + SerializationOutput(factory).serialize(excp, context), + context) assertEquals(excp.message, excp2.message) assertEquals(excp.cause, excp2.cause) @@ -52,7 +57,9 @@ class TransactionVerificationExceptionSerialisationTests { val cause = Throwable("wibble") val exception = TransactionVerificationException.ContractRejection(txid, contract, cause) - val exception2 = DeserializationInput(factory).deserialize(SerializationOutput(factory).serialize(exception)) + val exception2 = DeserializationInput(factory).deserialize( + SerializationOutput(factory).serialize(exception, context), + context) assertEquals(exception.message, exception2.message) assertEquals(exception.cause?.message, exception2.cause?.message) @@ -62,7 +69,9 @@ class TransactionVerificationExceptionSerialisationTests { @Test fun missingAttachmentRejectionTest() { val exception = TransactionVerificationException.MissingAttachmentRejection(txid, "Some contract class") - val exception2 = DeserializationInput(factory).deserialize(SerializationOutput(factory).serialize(exception)) + val exception2 = DeserializationInput(factory).deserialize( + SerializationOutput(factory).serialize(exception, context), + context) assertEquals(exception.message, exception2.message) assertEquals(exception.cause?.message, exception2.cause?.message) @@ -72,7 +81,9 @@ class TransactionVerificationExceptionSerialisationTests { @Test fun conflictingAttachmentsRejectionTest() { val exception = TransactionVerificationException.ContractConstraintRejection(txid, "Some contract class") - val exception2 = DeserializationInput(factory).deserialize(SerializationOutput(factory).serialize(exception)) + val exception2 = DeserializationInput(factory).deserialize( + SerializationOutput(factory).serialize(exception, context), + context) assertEquals(exception.message, exception2.message) assertEquals(exception.cause?.message, exception2.cause?.message) @@ -83,7 +94,9 @@ class TransactionVerificationExceptionSerialisationTests { fun contractCreationErrorTest() { val cause = Throwable("wibble") val exception = TransactionVerificationException.ContractCreationError(txid, "Some contract class", cause) - val exception2 = DeserializationInput(factory).deserialize(SerializationOutput(factory).serialize(exception)) + val exception2 = DeserializationInput(factory).deserialize( + SerializationOutput(factory).serialize(exception, context), + context) assertEquals(exception.message, exception2.message) assertEquals(exception.cause?.message, exception2.cause?.message) @@ -94,7 +107,9 @@ class TransactionVerificationExceptionSerialisationTests { fun transactionMissingEncumbranceTest() { val exception = TransactionVerificationException.TransactionMissingEncumbranceException( txid, 12, TransactionVerificationException.Direction.INPUT) - val exception2 = DeserializationInput(factory).deserialize(SerializationOutput(factory).serialize(exception)) + val exception2 = DeserializationInput(factory).deserialize( + SerializationOutput(factory).serialize(exception, context), + context) assertEquals(exception.message, exception2.message) assertEquals(exception.cause?.message, exception2.cause?.message) @@ -109,7 +124,9 @@ class TransactionVerificationExceptionSerialisationTests { val factory = defaultFactory() factory.register(PublicKeySerializer) val exception = TransactionVerificationException.NotaryChangeInWrongTransactionType(txid, dummyBankA, dummyNotary) - val exception2 = DeserializationInput(factory).deserialize(SerializationOutput(factory).serialize(exception)) + val exception2 = DeserializationInput(factory).deserialize( + SerializationOutput(factory).serialize(exception, context), + context) assertEquals(exception.message, exception2.message) assertEquals(exception.cause?.message, exception2.cause?.message) diff --git a/docs/source/api-testing.rst b/docs/source/api-testing.rst index b2662c1acf..5169f7b3b5 100644 --- a/docs/source/api-testing.rst +++ b/docs/source/api-testing.rst @@ -165,6 +165,40 @@ Nodes are created on the ``MockNetwork`` using: } } +Nodes added using ``createPartyNode`` are provided a default set of node parameters. However, it is also possible to +provide different parameters to each node using the following methods on ``MockNetwork``: + +.. container:: codeset + + .. sourcecode:: kotlin + + /** + * Create a started node with the given parameters. + * + * @param legalName The node's legal name. + * @param forcedID A unique identifier for the node. + * @param entropyRoot The initial entropy value to use when generating keys. Defaults to an (insecure) random value, + * but can be overridden to cause nodes to have stable or colliding identity/service keys. + * @param configOverrides Add/override behaviour of the [NodeConfiguration] mock object. + * @param extraCordappPackages Extra CorDapp packages to add for this node. + */ + @JvmOverloads + fun createNode(legalName: CordaX500Name? = null, + forcedID: Int? = null, + entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()), + configOverrides: (NodeConfiguration) -> Any? = {}, + extraCordappPackages: List = emptyList() + ): StartedMockNode + + /** Create a started node with the given parameters. **/ + fun createNode(parameters: MockNodeParameters = MockNodeParameters()): StartedMockNode + +As you can see above, parameters can be added individually or encapsulated within a ``MockNodeParameters`` object. Of +particular interest are ``configOverrides`` which allow you to override any default config option specified within the +``NodeConfiguration`` object. Also, the ``extraCordappPackages`` parameter allows you to add extra CorDapps to a +specific node. This is useful when you wish for all nodes to load a common CorDapp but for a subset of nodes to load +CorDapps specific to their role in the network. + Running the network ^^^^^^^^^^^^^^^^^^^ diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst index e1ef09876c..ef153d78ab 100644 --- a/docs/source/changelog.rst +++ b/docs/source/changelog.rst @@ -7,6 +7,9 @@ release, see :doc:`upgrade-notes`. Unreleased ========== +* Refactor AMQP Serializer to pass context object down the serialization call hierarchy. Will allow per thread + extensions to be set and used by the RPC work (Observable Context Key) + * Refactor RPC Server Kryo observable serializer into it's own sub module * Refactor RPC Client Kryo observable serializer into it's own sub module diff --git a/experimental/blobinspector/build.gradle b/experimental/blobinspector/build.gradle new file mode 100644 index 0000000000..2862ff6fae --- /dev/null +++ b/experimental/blobinspector/build.gradle @@ -0,0 +1,52 @@ +apply plugin: 'java' +apply plugin: 'kotlin' +apply plugin: 'application' + +mainClassName = 'net.corda.blobinspector.MainKt' + +dependencies { + compile project(':core') + compile project(':node-api') + + compile "commons-cli:commons-cli:$commons_cli_version" + + testCompile project(':test-utils') + + testCompile "junit:junit:$junit_version" +} + +/** + * To run from within gradle use + * + * ./gradlew -PrunArgs=" " :experimental:blobinspector:run + * + * For example, to parse a file from the command line and print out the deserialized properties + * + * ./gradlew -PrunArgs="-f -d" :experimental:blobinspector:run + * + * at the command line. + */ +run { + if (project.hasProperty('runArgs')) { + args = [ project.findProperty('runArgs').toString().split(" ") ].flatten() + } + + if (System.properties.getProperty('consoleLogLevel') != null) { + logging.captureStandardOutput(LogLevel.valueOf(System.properties.getProperty('consoleLogLevel'))) + logging.captureStandardError(LogLevel.valueOf(System.properties.getProperty('consoleLogLevel'))) + systemProperty "consoleLogLevel", System.properties.getProperty('consoleLogLevel') + } +} + +/** + * Build a executable jar + */ +jar { + baseName 'blobinspector' + manifest { + attributes( + 'Automatic-Module-Name': 'net.corda.experimental.blobinspector', + 'Main-Class': 'net.corda.blobinspector.MainKt' + ) + } +} diff --git a/experimental/blobinspector/src/main/kotlin/net/corda/blobinspector/BlobInspector.kt b/experimental/blobinspector/src/main/kotlin/net/corda/blobinspector/BlobInspector.kt new file mode 100644 index 0000000000..8a30c2319f --- /dev/null +++ b/experimental/blobinspector/src/main/kotlin/net/corda/blobinspector/BlobInspector.kt @@ -0,0 +1,399 @@ +package net.corda.blobinspector + +import net.corda.core.crypto.SecureHash +import net.corda.core.serialization.EncodingWhitelist +import net.corda.core.serialization.SerializationEncoding +import net.corda.core.utilities.ByteSequence +import net.corda.nodeapi.internal.serialization.SerializationFactoryImpl +import net.corda.nodeapi.internal.serialization.amqp.* +import org.apache.qpid.proton.amqp.Binary +import org.apache.qpid.proton.amqp.DescribedType +import org.apache.qpid.proton.amqp.Symbol + +/** + * Print a string to the console only if the verbose config option is set. + */ +fun String.debug(config: Config) { + if (config.verbose) { + println(this) + } +} + +/** + * + */ +interface Stringify { + fun stringify(sb: IndentingStringBuilder) +} + +/** + * Makes classnames easier to read by stripping off the package names from the class and separating nested + * classes + * + * For example: + * + * net.corda.blobinspector.Class1 + * Class1 + * + * net.corda.blobinspector.Class1 + * Class1 + * + * net.corda.blobinspector.Class1> + * Class1 > + * + * net.corda.blobinspector.Class1> + * Class1 :: C > + */ +fun String.simplifyClass(): String { + + return if (this.endsWith('>')) { + val templateStart = this.indexOf('<') + val clazz = (this.substring(0, templateStart)) + val params = this.substring(templateStart+1, this.length-1).split(',').map { it.simplifyClass() }.joinToString() + + "${clazz.simplifyClass()} <$params>" + } + else { + substring(this.lastIndexOf('.') + 1).replace("$", " :: ") + } +} + +/** + * Represents the deserialized form of the property of an Object + * + * @param name + * @param type + */ +abstract class Property( + val name: String, + val type: String) : Stringify + +/** + * Derived class of [Property], represents properties of an object that are non compelex, such + * as any POD type or String + */ +class PrimProperty( + name: String, + type: String, + private val value: String) : Property(name, type) { + override fun toString(): String = "$name : $type : $value" + + override fun stringify(sb: IndentingStringBuilder) { + sb.appendln("$name : $type : $value") + } +} + +/** + * Derived class of [Property] that represents a binary blob. Specifically useful because printing + * a stream of bytes onto the screen isn't very use friendly + */ +class BinaryProperty( + name: String, + type: String, + val value: ByteArray) : Property(name, type) { + override fun toString(): String = "$name : $type : <<>>" + + override fun stringify(sb: IndentingStringBuilder) { + sb.appendln("$name : $type : <<>>") + } +} + +/** + * Derived class of [Property] that represent a list property. List could be either PoD types or + * composite types. + */ +class ListProperty( + name: String, + type: String, + private val values: MutableList = mutableListOf()) : Property(name, type) { + override fun stringify(sb: IndentingStringBuilder) { + sb.apply { + if (values.isEmpty()) { + appendln("$name : $type : [ << EMPTY LIST >> ]") + } else if (values.first() is Stringify) { + appendln("$name : $type : [") + values.forEach { + (it as Stringify).stringify(this) + } + appendln("]") + } else { + appendln("$name : $type : [") + values.forEach { + appendln(it.toString()) + } + appendln("]") + } + } + } +} + +class MapProperty( + name: String, + type: String, + private val map: MutableMap<*, *> +) : Property(name, type) { + override fun stringify(sb: IndentingStringBuilder) { + if (map.isEmpty()) { + sb.appendln("$name : $type : { << EMPTY MAP >> }") + return + } + + // TODO this will not produce pretty output + sb.apply { + appendln("$name : $type : {") + map.forEach { + try { + (it.key as Stringify).stringify(this) + } catch (e: ClassCastException) { + append (it.key.toString() + " : ") + } + try { + (it.value as Stringify).stringify(this) + } catch (e: ClassCastException) { + appendln("\"${it.value.toString()}\"") + } + } + appendln("}") + } + } +} + +/** + * Derived class of [Property] that represents class properties that are themselves instances of + * some complex type. + */ +class InstanceProperty( + name: String, + type: String, + val value: Instance) : Property(name, type) { + override fun stringify(sb: IndentingStringBuilder) { + sb.append("$name : ") + value.stringify(sb) + } +} + +/** + * Represents an instance of a composite type. + */ +class Instance( + val name: String, + val type: String, + val fields: MutableList = mutableListOf()) : Stringify { + override fun stringify(sb: IndentingStringBuilder) { + sb.apply { + appendln("${name.simplifyClass()} : {") + fields.forEach { + it.stringify(this) + } + appendln("}") + } + } +} + +/** + * + */ +fun inspectComposite( + config: Config, + typeMap: Map, + obj: DescribedType): Instance { + if (obj.described !is List<*>) throw MalformedBlob("") + + val name = (typeMap[obj.descriptor] as CompositeType).name + "composite: $name".debug(config) + + val inst = Instance( + typeMap[obj.descriptor]?.name ?: "", + typeMap[obj.descriptor]?.label ?: "") + + (typeMap[obj.descriptor] as CompositeType).fields.zip(obj.described as List<*>).forEach { + " field: ${it.first.name}".debug(config) + inst.fields.add( + if (it.second is DescribedType) { + " - is described".debug(config) + val d = inspectDescribed(config, typeMap, it.second as DescribedType) + + when (d) { + is Instance -> + InstanceProperty( + it.first.name, + it.first.type, + d) + is List<*> -> { + " - List".debug(config) + ListProperty( + it.first.name, + it.first.type, + d as MutableList) + } + is Map<*, *> -> { + MapProperty( + it.first.name, + it.first.type, + d as MutableMap<*, *>) + } + else -> { + " skip it".debug(config) + return@forEach + } + } + + } else { + " - is prim".debug(config) + when (it.first.type) { + // Note, as in the case of SHA256 we can treat particular binary types + // as different properties with a little coercion + "binary" -> { + if (name == "net.corda.core.crypto.SecureHash\$SHA256") { + PrimProperty( + it.first.name, + it.first.type, + SecureHash.SHA256((it.second as Binary).array).toString()) + } else { + BinaryProperty(it.first.name, it.first.type, (it.second as Binary).array) + } + } + else -> PrimProperty(it.first.name, it.first.type, it.second.toString()) + } + }) + } + + return inst +} + +fun inspectRestricted( + config: Config, + typeMap: Map, + obj: DescribedType): Any { + return when ((typeMap[obj.descriptor] as RestrictedType).source) { + "list" -> inspectRestrictedList(config, typeMap, obj) + "map" -> inspectRestrictedMap(config, typeMap, obj) + else -> throw NotImplementedError() + } +} + + +fun inspectRestrictedList( + config: Config, + typeMap: Map, + obj: DescribedType +) : List { + if (obj.described !is List<*>) throw MalformedBlob("") + + return mutableListOf().apply { + (obj.described as List<*>).forEach { + when (it) { + is DescribedType -> add(inspectDescribed(config, typeMap, it)) + is RestrictedType -> add(inspectRestricted(config, typeMap, it)) + else -> add (it.toString()) + } + } + } +} + +fun inspectRestrictedMap( + config: Config, + typeMap: Map, + obj: DescribedType +) : Map { + if (obj.described !is Map<*,*>) throw MalformedBlob("") + + return mutableMapOf().apply { + (obj.described as Map<*, *>).forEach { + val key = when (it.key) { + is DescribedType -> inspectDescribed(config, typeMap, it.key as DescribedType) + is RestrictedType -> inspectRestricted(config, typeMap, it.key as RestrictedType) + else -> it.key.toString() + } + + val value = when (it.value) { + is DescribedType -> inspectDescribed(config, typeMap, it.value as DescribedType) + is RestrictedType -> inspectRestricted(config, typeMap, it.value as RestrictedType) + else -> it.value.toString() + } + + this[key] = value + } + } +} + + +/** + * Every element of the blob stream will be a ProtonJ [DescribedType]. When inspecting the blob stream + * the two custom Corda types we're interested in are [CompositeType]'s, representing the instance of + * some object (class), and [RestrictedType]'s, representing containers and enumerations. + * + * @param config The configuration object that controls the behaviour of the BlobInspector + * @param typeMap + * @param obj + */ +fun inspectDescribed( + config: Config, + typeMap: Map, + obj: DescribedType): Any { + "${obj.descriptor} in typeMap? = ${obj.descriptor in typeMap}".debug(config) + + return when (typeMap[obj.descriptor]) { + is CompositeType -> { + "* It's composite".debug(config) + inspectComposite(config, typeMap, obj) + } + is RestrictedType -> { + "* It's restricted".debug(config) + inspectRestricted(config, typeMap, obj) + } + else -> { + "${typeMap[obj.descriptor]?.name} is neither Composite or Restricted".debug(config) + } + } + +} + +internal object NullEncodingWhitelist : EncodingWhitelist { + override fun acceptEncoding(encoding: SerializationEncoding) = false +} + +// TODO : Refactor to generically poerate on arbitrary blobs, not a single workflow +fun inspectBlob(config: Config, blob: ByteArray) { + val bytes = ByteSequence.of(blob) + + val headerSize = SerializationFactoryImpl.magicSize + + // TODO written to only understand one version, when we support multiple this will need to change + val headers = listOf(ByteSequence.of(amqpMagic.bytes)) + + val blobHeader = bytes.take(headerSize) + + if (blobHeader !in headers) { + throw MalformedBlob("Blob is not a Corda AMQP serialised object graph") + } + + + val e = DeserializationInput.getEnvelope(bytes, NullEncodingWhitelist) + + if (config.schema) { + println(e.schema) + } + + if (config.transforms) { + println(e.transformsSchema) + } + + val typeMap = e.schema.types.associateBy({ it.descriptor.name }, { it }) + + if (config.data) { + val inspected = inspectDescribed(config, typeMap, e.obj as DescribedType) + + println("\n${IndentingStringBuilder().apply { (inspected as Instance).stringify(this) }}") + + (inspected as Instance).fields.find { + it.type.startsWith("net.corda.core.serialization.SerializedBytes<") + }?.let { + "Found field of SerializedBytes".debug(config) + (it as InstanceProperty).value.fields.find { it.name == "bytes" }?.let { raw -> + inspectBlob(config, (raw as BinaryProperty).value) + } + } + } +} + diff --git a/experimental/blobinspector/src/main/kotlin/net/corda/blobinspector/BlobLoader.kt b/experimental/blobinspector/src/main/kotlin/net/corda/blobinspector/BlobLoader.kt new file mode 100644 index 0000000000..a027249079 --- /dev/null +++ b/experimental/blobinspector/src/main/kotlin/net/corda/blobinspector/BlobLoader.kt @@ -0,0 +1,40 @@ +package net.corda.blobinspector + +import java.io.File +import java.net.URL + +/** + * + */ +class FileBlobHandler(config_: Config) : BlobHandler(config_) { + private val path = File(URL((config_ as FileConfig).file).toURI()) + + override fun getBytes(): ByteArray { + return path.readBytes() + } +} + +/** + * + */ +class InMemoryBlobHandler(config_: Config) : BlobHandler(config_) { + private val localBytes = (config_ as InMemoryConfig).blob?.bytes ?: kotlin.ByteArray(0) + override fun getBytes(): ByteArray = localBytes +} + +/** + * + */ +abstract class BlobHandler (val config: Config) { + companion object { + fun make(config: Config) : BlobHandler { + return when (config.mode) { + Mode.file -> FileBlobHandler(config) + Mode.inMem -> InMemoryBlobHandler(config) + } + } + } + + abstract fun getBytes() : ByteArray +} + diff --git a/experimental/blobinspector/src/main/kotlin/net/corda/blobinspector/Config.kt b/experimental/blobinspector/src/main/kotlin/net/corda/blobinspector/Config.kt new file mode 100644 index 0000000000..376331ec2b --- /dev/null +++ b/experimental/blobinspector/src/main/kotlin/net/corda/blobinspector/Config.kt @@ -0,0 +1,137 @@ +package net.corda.blobinspector + +import org.apache.commons.cli.CommandLine +import net.corda.core.serialization.SerializedBytes +import org.apache.commons.cli.Option +import org.apache.commons.cli.Options + +/** + * Enumeration of the modes in which the blob inspector can be run. + * + * @property make lambda function that takes no parameters and returns a specific instance of the configuration + * object for that mode. + * + * @property options A lambda function that takes no parameters and returns an [Options] instance that define + * the command line flags related to this mode. For example ``file`` mode would have an option to pass in + * the name of the file to read. + * + */ +enum class Mode( + val make : () -> Config, + val options : (Options) -> Unit +) { + file( + { + FileConfig(Mode.file) + }, + { o -> + o.apply{ + addOption( + Option ("f", "file", true, "path to file").apply { + isRequired = true + } + ) + } + } + ), + inMem( + { + InMemoryConfig(Mode.inMem) + }, + { + // The in memory only mode has no specific option assocaited with it as it's intended for + // testing purposes only within the unit test framework and not use on the command line + } + ) +} + +/** + * Configuration data class for the Blob Inspector. + * + * @property mode + */ +abstract class Config (val mode: Mode) { + var schema: Boolean = false + var transforms: Boolean = false + var data: Boolean = false + var verbose: Boolean = false + + abstract fun populateSpecific(cmdLine: CommandLine) + abstract fun withVerbose() : Config + + fun populate(cmdLine: CommandLine) { + schema = cmdLine.hasOption('s') + transforms = cmdLine.hasOption('t') + data = cmdLine.hasOption('d') + verbose = cmdLine.hasOption('v') + + populateSpecific(cmdLine) + } + + fun options() = Options().apply { + // install generic options + addOption(Option("s", "schema", false, "print the blob's schema").apply { + isRequired = false + }) + + addOption(Option("t", "transforms", false, "print the blob's transforms schema").apply { + isRequired = false + }) + + addOption(Option("d", "data", false, "Display the serialised data").apply { + isRequired = false + }) + + addOption(Option("v", "verbose", false, "Enable debug output").apply { + isRequired = false + }) + + // install the mode specific options + mode.options(this) + } +} + + +/** + * Configuration object when running in "File" mode, i.e. the object has been specified at + * the command line + */ +class FileConfig ( + mode: Mode +) : Config(mode) { + + var file: String = "unset" + + override fun populateSpecific(cmdLine : CommandLine) { + file = cmdLine.getParsedOptionValue("f") as String + } + + override fun withVerbose() : FileConfig { + return FileConfig(mode).apply { + this.schema = schema + this.transforms = transforms + this.data = data + this.verbose = true + } + } +} + + +/** + * Placeholder config objet used when running unit tests and the inspected blob is being fed in + * via some mechanism directly. Normally this will be the direct serialisation of an object in a unit + * test and then dumping that blob into the inspector for visual comparison of the output + */ +class InMemoryConfig ( + mode: Mode +) : Config(mode) { + var blob: SerializedBytes<*>? = null + + override fun populateSpecific(cmdLine: CommandLine) { + throw UnsupportedOperationException("In memory config is for testing only and cannot set specific flags") + } + + override fun withVerbose(): Config { + throw UnsupportedOperationException("In memory config is for testing headlessly, cannot be verbose") + } +} diff --git a/experimental/blobinspector/src/main/kotlin/net/corda/blobinspector/Errors.kt b/experimental/blobinspector/src/main/kotlin/net/corda/blobinspector/Errors.kt new file mode 100644 index 0000000000..888ef1e302 --- /dev/null +++ b/experimental/blobinspector/src/main/kotlin/net/corda/blobinspector/Errors.kt @@ -0,0 +1,3 @@ +package net.corda.blobinspector + +class MalformedBlob(msg: String) : Exception(msg) diff --git a/experimental/blobinspector/src/main/kotlin/net/corda/blobinspector/IndentingStringBuilder.kt b/experimental/blobinspector/src/main/kotlin/net/corda/blobinspector/IndentingStringBuilder.kt new file mode 100644 index 0000000000..48d81a8eb7 --- /dev/null +++ b/experimental/blobinspector/src/main/kotlin/net/corda/blobinspector/IndentingStringBuilder.kt @@ -0,0 +1,45 @@ +package net.corda.blobinspector + +/** + * Wrapper around a [StringBuilder] that automates the indenting of lines as they're appended to facilitate + * pretty printing of deserialized blobs. + * + * @property sb The wrapped [StringBuilder] + * @property indenting Boolean flag that indicates weather we need to pad the start of whatever text + * currently being added to the string. + * @property indent How deeply the next line should be offset from the first column + */ +class IndentingStringBuilder(s : String = "", private val offset : Int = 4) { + private val sb = StringBuilder(s) + private var indenting = true + private var indent = 0 + + private fun wrap(ln: String, appender: (String) -> Unit) { + if ((ln.endsWith("}") || ln.endsWith("]")) && indent > 0 && ln.length == 1) { + indent -= offset + } + + appender(ln) + + if (ln.endsWith("{") || ln.endsWith("[")){ + indent += offset + } + } + + fun appendln(ln: String) { + wrap(ln) { s -> sb.appendln("${"".padStart(if (indenting) indent else 0, ' ')}$s") } + + indenting = true + } + + + fun append(ln: String) { + indenting = false + + wrap(ln) { s -> sb.append("${"".padStart(indent, ' ')}$s") } + } + + override fun toString(): String { + return sb.toString() + } +} \ No newline at end of file diff --git a/experimental/blobinspector/src/main/kotlin/net/corda/blobinspector/Main.kt b/experimental/blobinspector/src/main/kotlin/net/corda/blobinspector/Main.kt new file mode 100644 index 0000000000..0e13b9e087 --- /dev/null +++ b/experimental/blobinspector/src/main/kotlin/net/corda/blobinspector/Main.kt @@ -0,0 +1,81 @@ +package net.corda.blobinspector + +import org.apache.commons.cli.* +import java.lang.IllegalArgumentException + +/** + * Mode isn't a required property as we default it to [Mode.file] + */ +private fun modeOption() = Option("m", "mode", true, "mode, file is the default").apply { + isRequired = false +} + +/** + * + * Parse the command line arguments looking for the main mode into which the application is + * being put. Note, this defaults to [Mode.file] if not set meaning we will look for a file path + * being passed as a parameter and parse that file. + * + * @param args reflects the command line arguments + * + * @return An instantiated but unpopulated [Config] object instance suitable for the mode into + * which we've been placed. This Config object should be populated via [loadModeSpecificOptions] + */ +fun getMode(args: Array) : Config { + // For now we only care what mode we're being put in, we can build the rest of the args and parse them + // later + val options = Options().apply { + addOption(modeOption()) + } + + val cmd = try { + DefaultParser().parse(options, args, true) + } catch (e: org.apache.commons.cli.ParseException) { + println (e) + HelpFormatter().printHelp("blobinspector", options) + throw IllegalArgumentException("OH NO!!!") + } + + return try { + Mode.valueOf(cmd.getParsedOptionValue("m") as? String ?: "file") + } catch (e: IllegalArgumentException) { + Mode.file + }.make() +} + +/** + * + * @param config an instance of a [Config] specialisation suitable for the mode into which + * the application has been put. + * @param args The command line arguments + */ +fun loadModeSpecificOptions(config: Config, args: Array) { + config.apply { + // load that modes specific command line switches, needs to include the mode option + val modeSpecificOptions = config.options().apply { + addOption(modeOption()) + } + + populate (try { + DefaultParser().parse(modeSpecificOptions, args, false) + } catch (e: org.apache.commons.cli.ParseException) { + println ("Error: ${e.message}") + HelpFormatter().printHelp("blobinspector", modeSpecificOptions) + System.exit(1) + return + }) + } +} + +/** + * Executable entry point + */ +fun main(args: Array) { + println ("<<< WARNING: this tool is experimental and under active development >>>") + getMode(args).let { mode -> + loadModeSpecificOptions(mode, args) + BlobHandler.make(mode) + }.apply { + inspectBlob(config, getBytes()) + } +} diff --git a/experimental/blobinspector/src/test/kotlin/net/corda/blobinspector/FileParseTests.kt b/experimental/blobinspector/src/test/kotlin/net/corda/blobinspector/FileParseTests.kt new file mode 100644 index 0000000000..a018baaf49 --- /dev/null +++ b/experimental/blobinspector/src/test/kotlin/net/corda/blobinspector/FileParseTests.kt @@ -0,0 +1,84 @@ +package net.corda.blobinspector + +import java.net.URI + +import org.junit.Test +import net.corda.testing.common.internal.ProjectStructure.projectRootDir + + +class FileParseTests { + @Suppress("UNUSED") + var localPath : URI = projectRootDir.toUri().resolve( + "tools/blobinspector/src/test/resources/net/corda/blobinspector") + + fun setupArgsWithFile(path: String) = Array(5) { + when (it) { + 0 -> "-m" + 1 -> "file" + 2 -> "-f" + 3 -> path + 4 -> "-d" + else -> "error" + } + } + + private val filesToTest = listOf ( + "FileParseTests.1Int", + "FileParseTests.2Int", + "FileParseTests.3Int", + "FileParseTests.1String", + "FileParseTests.1Composite", + "FileParseTests.2Composite", + "FileParseTests.IntList", + "FileParseTests.StringList", + "FileParseTests.MapIntString", + "FileParseTests.MapIntClass" + ) + + fun testFile(file : String) { + val path = FileParseTests::class.java.getResource(file) + val args = setupArgsWithFile(path.toString()) + + val handler = getMode(args).let { mode -> + loadModeSpecificOptions(mode, args) + BlobHandler.make(mode) + } + + inspectBlob(handler.config, handler.getBytes()) + } + + @Test + fun simpleFiles() { + filesToTest.forEach { testFile(it) } + } + + @Test + fun specificTest() { + testFile(filesToTest[4]) + testFile(filesToTest[5]) + testFile(filesToTest[6]) + } + + @Test + fun networkParams() { + val file = "networkParams" + val path = FileParseTests::class.java.getResource(file) + val verbose = false + + val args = verbose.let { + if (it) + Array(4) { when (it) { 0 -> "-f" ; 1 -> path.toString(); 2 -> "-d"; 3 -> "-vs"; else -> "error" } } + else + Array(3) { when (it) { 0 -> "-f" ; 1 -> path.toString(); 2 -> "-d"; else -> "error" } } + } + + val handler = getMode(args).let { mode -> + loadModeSpecificOptions(mode, args) + BlobHandler.make(mode) + } + + inspectBlob(handler.config, handler.getBytes()) + + } + +} diff --git a/experimental/blobinspector/src/test/kotlin/net/corda/blobinspector/InMemoryTests.kt b/experimental/blobinspector/src/test/kotlin/net/corda/blobinspector/InMemoryTests.kt new file mode 100644 index 0000000000..26313b1d3c --- /dev/null +++ b/experimental/blobinspector/src/test/kotlin/net/corda/blobinspector/InMemoryTests.kt @@ -0,0 +1,91 @@ +package net.corda.blobinspector + +import net.corda.core.serialization.SerializedBytes +import net.corda.nodeapi.internal.serialization.AllWhitelist +import net.corda.nodeapi.internal.serialization.amqp.SerializationOutput +import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory +import net.corda.nodeapi.internal.serialization.AMQP_P2P_CONTEXT +import org.junit.Test + + +class InMemoryTests { + private val factory = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader()) + + private fun inspect (b: SerializedBytes<*>) { + BlobHandler.make( + InMemoryConfig(Mode.inMem).apply { blob = b; data = true} + ).apply { + inspectBlob(config, getBytes()) + } + } + + @Test + fun test1() { + data class C (val a: Int, val b: Long, val c: String) + inspect (SerializationOutput(factory).serialize(C(100, 567L, "this is a test"), AMQP_P2P_CONTEXT)) + } + + @Test + fun test2() { + data class C (val i: Int, val c: C?) + inspect (SerializationOutput(factory).serialize(C(1, C(2, C(3, C(4, null)))), AMQP_P2P_CONTEXT)) + } + + @Test + fun test3() { + data class C (val a: IntArray, val b: Array) + + val a = IntArray(10) { i -> i } + val c = C(a, arrayOf("aaa", "bbb", "ccc")) + + inspect (SerializationOutput(factory).serialize(c, AMQP_P2P_CONTEXT)) + } + + @Test + fun test4() { + data class Elem(val e1: Long, val e2: String) + data class Wrapper (val name: String, val elementes: List) + + inspect (SerializationOutput(factory).serialize( + Wrapper("Outer Class", + listOf( + Elem(1L, "First element"), + Elem(2L, "Second element"), + Elem(3L, "Third element") + )), AMQP_P2P_CONTEXT)) + } + + @Test + fun test4b() { + data class Elem(val e1: Long, val e2: String) + data class Wrapper (val name: String, val elementes: List>) + + inspect (SerializationOutput(factory).serialize( + Wrapper("Outer Class", + listOf ( + listOf( + Elem(1L, "First element"), + Elem(2L, "Second element"), + Elem(3L, "Third element") + ), + listOf( + Elem(4L, "Fourth element"), + Elem(5L, "Fifth element"), + Elem(6L, "Sixth element") + ) + )), AMQP_P2P_CONTEXT)) + } + + @Test + fun test5() { + data class C (val a: Map) + + inspect (SerializationOutput(factory).serialize( + C(mapOf( + "a" to "a a a", + "b" to "b b b", + "c" to "c c c")), + AMQP_P2P_CONTEXT + )) + } +} \ No newline at end of file diff --git a/experimental/blobinspector/src/test/kotlin/net/corda/blobinspector/ModeParse.kt b/experimental/blobinspector/src/test/kotlin/net/corda/blobinspector/ModeParse.kt new file mode 100644 index 0000000000..9b69363386 --- /dev/null +++ b/experimental/blobinspector/src/test/kotlin/net/corda/blobinspector/ModeParse.kt @@ -0,0 +1,77 @@ +package net.corda.blobinspector + +import org.junit.Test +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue +import kotlin.test.assertFalse + +class ModeParse { + @Test + fun fileIsSetToFile() { + val opts1 = Array(2) { + when (it) { + 0 -> "-m" + 1 -> "file" + else -> "error" + } + } + + assertEquals(Mode.file, getMode(opts1).mode) + } + + @Test + fun nothingIsSetToFile() { + val opts1 = Array(0) { "" } + + assertEquals(Mode.file, getMode(opts1).mode) + } + + @Test + fun filePathIsSet() { + val opts1 = Array(4) { + when (it) { + 0 -> "-m" + 1 -> "file" + 2 -> "-f" + 3 -> "path/to/file" + else -> "error" + } + } + + val config = getMode(opts1) + assertTrue (config is FileConfig) + assertEquals(Mode.file, config.mode) + assertEquals("unset", (config as FileConfig).file) + + loadModeSpecificOptions(config, opts1) + + assertEquals("path/to/file", config.file) + } + + @Test + fun schemaIsSet() { + Array(2) { when (it) { 0 -> "-f"; 1 -> "path/to/file"; else -> "error" } }.let { options -> + getMode(options).apply { + loadModeSpecificOptions(this, options) + assertFalse (schema) + } + } + + Array(3) { when (it) { 0 -> "--schema"; 1 -> "-f"; 2 -> "path/to/file"; else -> "error" } }.let { + getMode(it).apply { + loadModeSpecificOptions(this, it) + assertTrue (schema) + } + } + + Array(3) { when (it) { 0 -> "-f"; 1 -> "path/to/file"; 2 -> "-s"; else -> "error" } }.let { + getMode(it).apply { + loadModeSpecificOptions(this, it) + assertTrue (schema) + } + } + + } + + +} \ No newline at end of file diff --git a/experimental/blobinspector/src/test/kotlin/net/corda/blobinspector/SimplifyClassTests.kt b/experimental/blobinspector/src/test/kotlin/net/corda/blobinspector/SimplifyClassTests.kt new file mode 100644 index 0000000000..10d470685b --- /dev/null +++ b/experimental/blobinspector/src/test/kotlin/net/corda/blobinspector/SimplifyClassTests.kt @@ -0,0 +1,28 @@ +package net.corda.blobinspector + +import org.junit.Test + +class SimplifyClassTests { + + @Test + fun test1() { + data class A(val a: Int) + + println (A::class.java.name) + println (A::class.java.name.simplifyClass()) + } + + @Test + fun test2() { + val p = this.javaClass.`package`.name + + println("$p.Class1<$p.Class2>") + println("$p.Class1<$p.Class2>".simplifyClass()) + println("$p.Class1<$p.Class2, $p.Class3>") + println("$p.Class1<$p.Class2, $p.Class3>".simplifyClass()) + println("$p.Class1<$p.Class2<$p.Class3>>") + println("$p.Class1<$p.Class2<$p.Class3>>".simplifyClass()) + println("$p.Class1<$p.Class2<$p.Class3>>") + println("$p.Class1\$C<$p.Class2<$p.Class3>>".simplifyClass()) + } +} \ No newline at end of file diff --git a/experimental/blobinspector/src/test/resources/net/corda/blobinspector/FileParseTests.1Composite b/experimental/blobinspector/src/test/resources/net/corda/blobinspector/FileParseTests.1Composite new file mode 100644 index 0000000000..450e6970da Binary files /dev/null and b/experimental/blobinspector/src/test/resources/net/corda/blobinspector/FileParseTests.1Composite differ diff --git a/experimental/blobinspector/src/test/resources/net/corda/blobinspector/FileParseTests.1Int b/experimental/blobinspector/src/test/resources/net/corda/blobinspector/FileParseTests.1Int new file mode 100644 index 0000000000..25dcb48d65 Binary files /dev/null and b/experimental/blobinspector/src/test/resources/net/corda/blobinspector/FileParseTests.1Int differ diff --git a/experimental/blobinspector/src/test/resources/net/corda/blobinspector/FileParseTests.1String b/experimental/blobinspector/src/test/resources/net/corda/blobinspector/FileParseTests.1String new file mode 100644 index 0000000000..9676f0375f Binary files /dev/null and b/experimental/blobinspector/src/test/resources/net/corda/blobinspector/FileParseTests.1String differ diff --git a/experimental/blobinspector/src/test/resources/net/corda/blobinspector/FileParseTests.2Composite b/experimental/blobinspector/src/test/resources/net/corda/blobinspector/FileParseTests.2Composite new file mode 100644 index 0000000000..0bf3a5c475 Binary files /dev/null and b/experimental/blobinspector/src/test/resources/net/corda/blobinspector/FileParseTests.2Composite differ diff --git a/experimental/blobinspector/src/test/resources/net/corda/blobinspector/FileParseTests.2Int b/experimental/blobinspector/src/test/resources/net/corda/blobinspector/FileParseTests.2Int new file mode 100644 index 0000000000..118a23f37b Binary files /dev/null and b/experimental/blobinspector/src/test/resources/net/corda/blobinspector/FileParseTests.2Int differ diff --git a/experimental/blobinspector/src/test/resources/net/corda/blobinspector/FileParseTests.3Int b/experimental/blobinspector/src/test/resources/net/corda/blobinspector/FileParseTests.3Int new file mode 100644 index 0000000000..9f00d59068 Binary files /dev/null and b/experimental/blobinspector/src/test/resources/net/corda/blobinspector/FileParseTests.3Int differ diff --git a/experimental/blobinspector/src/test/resources/net/corda/blobinspector/FileParseTests.IntList b/experimental/blobinspector/src/test/resources/net/corda/blobinspector/FileParseTests.IntList new file mode 100644 index 0000000000..d762a9e821 Binary files /dev/null and b/experimental/blobinspector/src/test/resources/net/corda/blobinspector/FileParseTests.IntList differ diff --git a/experimental/blobinspector/src/test/resources/net/corda/blobinspector/FileParseTests.MapIntClass b/experimental/blobinspector/src/test/resources/net/corda/blobinspector/FileParseTests.MapIntClass new file mode 100644 index 0000000000..175949d9aa Binary files /dev/null and b/experimental/blobinspector/src/test/resources/net/corda/blobinspector/FileParseTests.MapIntClass differ diff --git a/experimental/blobinspector/src/test/resources/net/corda/blobinspector/FileParseTests.MapIntString b/experimental/blobinspector/src/test/resources/net/corda/blobinspector/FileParseTests.MapIntString new file mode 100644 index 0000000000..67ba352ec4 Binary files /dev/null and b/experimental/blobinspector/src/test/resources/net/corda/blobinspector/FileParseTests.MapIntString differ diff --git a/experimental/blobinspector/src/test/resources/net/corda/blobinspector/FileParseTests.StringList b/experimental/blobinspector/src/test/resources/net/corda/blobinspector/FileParseTests.StringList new file mode 100644 index 0000000000..5758d9fa62 Binary files /dev/null and b/experimental/blobinspector/src/test/resources/net/corda/blobinspector/FileParseTests.StringList differ diff --git a/experimental/blobinspector/src/test/resources/net/corda/blobinspector/networkParams b/experimental/blobinspector/src/test/resources/net/corda/blobinspector/networkParams new file mode 100644 index 0000000000..dcdbaa7b5f Binary files /dev/null and b/experimental/blobinspector/src/test/resources/net/corda/blobinspector/networkParams differ diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/engine/ConnectionStateMachine.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/engine/ConnectionStateMachine.kt index e16446d2d8..dbf93ec26e 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/engine/ConnectionStateMachine.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/engine/ConnectionStateMachine.kt @@ -361,7 +361,7 @@ internal class ConnectionStateMachine(serverMode: Boolean, val connection = event.connection val channel = connection?.context as? Channel if (channel != null) { - val appProperties = HashMap(amqpMessage.applicationProperties.value) + val appProperties = HashMap(amqpMessage.applicationProperties.value as Map) appProperties["_AMQ_VALIDATED_USER"] = remoteLegalName val localAddress = channel.localAddress() as InetSocketAddress val remoteAddress = channel.remoteAddress() as InetSocketAddress @@ -438,7 +438,6 @@ internal class ConnectionStateMachine(serverMode: Boolean, } fun transportWriteMessage(msg: SendableMessageImpl) { - log.debug { "Queue application message write uuid: ${msg.applicationProperties["_AMQ_DUPL_ID"]} ${javax.xml.bind.DatatypeConverter.printHexBinary(msg.payload)}" } msg.buf = encodePayloadBytes(msg) val messageQueue = messageQueues.getOrPut(msg.topic, { LinkedList() }) messageQueue.offer(msg) diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/netty/AMQPChannelHandler.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/netty/AMQPChannelHandler.kt index 4c9bd812ab..ad5a22bae9 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/netty/AMQPChannelHandler.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/netty/AMQPChannelHandler.kt @@ -130,7 +130,6 @@ internal class AMQPChannelHandler(private val serverMode: Boolean, override fun channelRead(ctx: ChannelHandlerContext, msg: Any) { try { - log.debug { "Received $msg" } if (msg is ByteBuf) { eventProcessor!!.transportProcessInput(msg) } @@ -143,7 +142,6 @@ internal class AMQPChannelHandler(private val serverMode: Boolean, override fun write(ctx: ChannelHandlerContext, msg: Any, promise: ChannelPromise) { try { try { - log.debug { "Sent $msg" } when (msg) { // Transfers application packet into the AMQP engine. is SendableMessageImpl -> { diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/CordaClassResolver.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/CordaClassResolver.kt index b531fce895..6c72829fca 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/CordaClassResolver.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/CordaClassResolver.kt @@ -90,10 +90,10 @@ class CordaClassResolver(serializationContext: SerializationContext) : DefaultCl val objectInstance = try { targetType.declaredFields.singleOrNull { it.name == "INSTANCE" && - it.type == type && - Modifier.isStatic(it.modifiers) && - Modifier.isFinal(it.modifiers) && - Modifier.isPublic(it.modifiers) + it.type == type && + Modifier.isStatic(it.modifiers) && + Modifier.isFinal(it.modifiers) && + Modifier.isPublic(it.modifiers) }?.let { it.isAccessible = true type.cast(it.get(null)!!) @@ -172,7 +172,7 @@ object AllWhitelist : ClassWhitelist { override fun hasListed(type: Class<*>): Boolean = true } -sealed class AbstractMutableClassWhitelist(private val whitelist: MutableSet, private val delegate: ClassWhitelist) : MutableClassWhitelist { +sealed class AbstractMutableClassWhitelist(private val whitelist: MutableSet, private val delegate: ClassWhitelist) : MutableClassWhitelist { override fun hasListed(type: Class<*>): Boolean { /** diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/DefaultWhitelist.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/DefaultWhitelist.kt index e1550d296f..bda44ea16f 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/DefaultWhitelist.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/DefaultWhitelist.kt @@ -76,5 +76,5 @@ object DefaultWhitelist : SerializationWhitelist { // Implementation of X509Certificate. X509CertImpl::class.java, CRLReason::class.java - ) + ) } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/SerializationFormat.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/SerializationFormat.kt index 60004f0d44..d469cb688d 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/SerializationFormat.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/SerializationFormat.kt @@ -17,8 +17,8 @@ import net.corda.core.utilities.OpaqueBytes import net.corda.nodeapi.internal.serialization.OrdinalBits.OrdinalWriter import org.iq80.snappy.SnappyFramedInputStream import org.iq80.snappy.SnappyFramedOutputStream -import java.io.OutputStream import java.io.InputStream +import java.io.OutputStream import java.nio.ByteBuffer import java.util.zip.DeflaterOutputStream import java.util.zip.InflaterInputStream diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/SerializationScheme.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/SerializationScheme.kt index 1b5d3ed93d..47bb652013 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/SerializationScheme.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/SerializationScheme.kt @@ -49,13 +49,15 @@ data class SerializationContextImpl @JvmOverloads constructor(override val prefe */ override fun withAttachmentsClassLoader(attachmentHashes: List): SerializationContext { properties[attachmentsClassLoaderEnabledPropertyName] as? Boolean == true || return this - val serializationContext = properties[serializationContextKey] as? SerializeAsTokenContextImpl ?: return this // Some tests don't set one. + val serializationContext = properties[serializationContextKey] as? SerializeAsTokenContextImpl + ?: return this // Some tests don't set one. try { return withClassLoader(cache.get(attachmentHashes) { val missing = ArrayList() val attachments = ArrayList() attachmentHashes.forEach { id -> - serializationContext.serviceHub.attachments.openAttachment(id)?.let { attachments += it } ?: run { missing += id } + serializationContext.serviceHub.attachments.openAttachment(id)?.let { attachments += it } + ?: run { missing += id } } missing.isNotEmpty() && throw MissingAttachmentsException(missing) AttachmentsClassLoader(attachments, parent = deserializationClassLoader) @@ -90,7 +92,7 @@ data class SerializationContextImpl @JvmOverloads constructor(override val prefe open class SerializationFactoryImpl : SerializationFactory() { companion object { - private val magicSize = sequenceOf(kryoMagic, amqpMagic).map { it.size }.distinct().single() + val magicSize = sequenceOf(kryoMagic, amqpMagic).map { it.size }.distinct().single() } private val creator: List = Exception().stackTrace.asList() @@ -153,8 +155,6 @@ open class SerializationFactoryImpl : SerializationFactory() { } - - interface SerializationScheme { fun canDeserializeVersion(magic: CordaSerializationMagic, target: SerializationContext.UseCase): Boolean @Throws(NotSerializableException::class) diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/SerializeAsTokenContextImpl.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/SerializeAsTokenContextImpl.kt index 8cfd815c22..2ac9849a6e 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/SerializeAsTokenContextImpl.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/SerializeAsTokenContextImpl.kt @@ -62,5 +62,6 @@ class SerializeAsTokenContextImpl(override val serviceHub: ServiceHub, init: Ser } } - override fun getSingleton(className: String) = classNameToSingleton[className] ?: throw IllegalStateException("Unable to find tokenized instance of $className in context $this") + override fun getSingleton(className: String) = classNameToSingleton[className] + ?: throw IllegalStateException("Unable to find tokenized instance of $className in context $this") } \ No newline at end of file diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/UseCaseAwareness.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/UseCaseAwareness.kt index 605f098662..c2307bddbe 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/UseCaseAwareness.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/UseCaseAwareness.kt @@ -15,7 +15,8 @@ import net.corda.core.serialization.SerializationFactory import java.util.* internal fun checkUseCase(allowedUseCases: EnumSet) { - val currentContext: SerializationContext = SerializationFactory.currentFactory?.currentContext ?: throw IllegalStateException("Current context is not set") + val currentContext: SerializationContext = SerializationFactory.currentFactory?.currentContext + ?: throw IllegalStateException("Current context is not set") if (!allowedUseCases.contains(currentContext.useCase)) { throw IllegalStateException("UseCase '${currentContext.useCase}' is not within '$allowedUseCases'") } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/AMQPDescriptorRegistry.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/AMQPDescriptorRegistry.kt index 762d5bcb21..c3e8aaea5a 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/AMQPDescriptorRegistry.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/AMQPDescriptorRegistry.kt @@ -20,7 +20,7 @@ import org.apache.qpid.proton.amqp.UnsignedLong * Repeated here for brevity: * 50530 - R3 - Mike Hearn - mike&r3.com */ -const val DESCRIPTOR_TOP_32BITS: Long = 0xc562L shl(32 + 16) +const val DESCRIPTOR_TOP_32BITS: Long = 0xc562L shl (32 + 16) /** * AMQP descriptor ID's for our custom types. diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/AMQPPrimitiveSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/AMQPPrimitiveSerializer.kt index 081dfb8222..ce74965c01 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/AMQPPrimitiveSerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/AMQPPrimitiveSerializer.kt @@ -10,6 +10,7 @@ package net.corda.nodeapi.internal.serialization.amqp +import net.corda.core.serialization.SerializationContext import org.apache.qpid.proton.amqp.Binary import org.apache.qpid.proton.amqp.Symbol import org.apache.qpid.proton.codec.Data @@ -28,7 +29,14 @@ class AMQPPrimitiveSerializer(clazz: Class<*>) : AMQPSerializer { override fun writeClassInfo(output: SerializationOutput) { } - override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput, debugIndent: Int) { + override fun writeObject( + obj: Any, + data: Data, + type: Type, + output: SerializationOutput, + context: SerializationContext, + debugIndent: Int + ) { if (obj is ByteArray) { data.putObject(Binary(obj)) } else { @@ -39,5 +47,6 @@ class AMQPPrimitiveSerializer(clazz: Class<*>) : AMQPSerializer { override fun readObject( obj: Any, schemas: SerializationSchemas, - input: DeserializationInput): Any = (obj as? Binary)?.array ?: obj + input: DeserializationInput, + context: SerializationContext): Any = (obj as? Binary)?.array ?: obj } \ No newline at end of file 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 d2240540f2..88c9f1bbf5 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 @@ -16,8 +16,8 @@ import io.github.lukehutch.fastclasspathscanner.FastClasspathScanner import net.corda.core.cordapp.Cordapp import net.corda.core.internal.objectOrNewInstance import net.corda.core.serialization.* -import net.corda.nodeapi.internal.serialization.CordaSerializationMagic import net.corda.core.utilities.ByteSequence +import net.corda.nodeapi.internal.serialization.CordaSerializationMagic import net.corda.nodeapi.internal.serialization.DefaultWhitelist import net.corda.nodeapi.internal.serialization.MutableClassWhitelist import net.corda.nodeapi.internal.serialization.SerializationScheme @@ -41,12 +41,12 @@ fun SerializerFactory.addToWhitelist(vararg types: Class<*>) { open class SerializerFactoryFactory { open fun make(context: SerializationContext) = - SerializerFactory(context.whitelist, context.deserializationClassLoader) + SerializerFactory(context.whitelist, context.deserializationClassLoader) } abstract class AbstractAMQPSerializationScheme( val cordappLoader: List, - val sff : SerializerFactoryFactory = SerializerFactoryFactory() + val sff: SerializerFactoryFactory = SerializerFactoryFactory() ) : SerializationScheme { // 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 @@ -62,7 +62,7 @@ abstract class AbstractAMQPSerializationScheme( val scanSpec: String? = System.getProperty(SCAN_SPEC_PROP_NAME) - if(scanSpec == null) { + if (scanSpec == null) { emptyList() } else { FastClasspathScanner(scanSpec).addClassLoader(this::class.java.classLoader).scan() @@ -74,7 +74,7 @@ abstract class AbstractAMQPSerializationScheme( } } - private fun registerCustomSerializers(factory: SerializerFactory) { + private fun registerCustomSerializers(context: SerializationContext, factory: SerializerFactory) { with(factory) { register(publicKeySerializer) register(net.corda.nodeapi.internal.serialization.amqp.custom.PrivateKeySerializer) @@ -131,8 +131,7 @@ abstract class AbstractAMQPSerializationScheme( protected abstract fun rpcClientSerializerFactory(context: SerializationContext): SerializerFactory protected abstract fun rpcServerSerializerFactory(context: SerializationContext): SerializerFactory - open protected val publicKeySerializer: CustomSerializer.Implements - = net.corda.nodeapi.internal.serialization.amqp.custom.PublicKeySerializer + protected open val publicKeySerializer: CustomSerializer.Implements = net.corda.nodeapi.internal.serialization.amqp.custom.PublicKeySerializer private fun getSerializerFactory(context: SerializationContext): SerializerFactory { return serializerFactoriesForContexts.computeIfAbsent(Pair(context.whitelist, context.deserializationClassLoader)) { @@ -145,19 +144,20 @@ abstract class AbstractAMQPSerializationScheme( rpcServerSerializerFactory(context) else -> sff.make(context) }.also { - registerCustomSerializers(it) + registerCustomSerializers(context, it) } } } override fun deserialize(byteSequence: ByteSequence, clazz: Class, context: SerializationContext): T { val serializerFactory = getSerializerFactory(context) - return DeserializationInput(serializerFactory).deserialize(byteSequence, clazz) + return DeserializationInput(serializerFactory).deserialize(byteSequence, clazz, context) } override fun serialize(obj: T, context: SerializationContext): SerializedBytes { val serializerFactory = getSerializerFactory(context) - return SerializationOutput(serializerFactory).serialize(obj) + + return SerializationOutput(serializerFactory).serialize(obj, context) } protected fun canDeserializeVersion(magic: CordaSerializationMagic) = magic == amqpMagic diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/AMQPSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/AMQPSerializer.kt index 820c7f8951..458e4e47f6 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/AMQPSerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/AMQPSerializer.kt @@ -10,6 +10,7 @@ package net.corda.nodeapi.internal.serialization.amqp +import net.corda.core.serialization.SerializationContext import org.apache.qpid.proton.amqp.Symbol import org.apache.qpid.proton.codec.Data import java.lang.reflect.Type @@ -40,10 +41,11 @@ interface AMQPSerializer { /** * Write the given object, with declared type, to the output. */ - fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput, debugIndent: Int = 0) + fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput, + context: SerializationContext, debugIndent: Int = 0) /** * Read the given object from the input. The envelope is provided in case the schema is required. */ - fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput): T + fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput, context: SerializationContext): T } \ No newline at end of file diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/ArraySerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/ArraySerializer.kt index b65ab9e6f4..d7bd52cca5 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/ArraySerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/ArraySerializer.kt @@ -10,6 +10,7 @@ package net.corda.nodeapi.internal.serialization.amqp +import net.corda.core.serialization.SerializationContext import org.apache.qpid.proton.amqp.Symbol import org.apache.qpid.proton.codec.Data import java.io.NotSerializableException @@ -42,7 +43,8 @@ open class ArraySerializer(override val type: Type, factory: SerializerFactory) } override val typeDescriptor by lazy { - Symbol.valueOf("$DESCRIPTOR_DOMAIN:${factory.fingerPrinter.fingerprint(type)}") } + Symbol.valueOf("$DESCRIPTOR_DOMAIN:${factory.fingerPrinter.fingerprint(type)}") + } internal val elementType: Type by lazy { type.componentType() } internal open val typeName by lazy { calcTypeName(type) } @@ -56,20 +58,24 @@ open class ArraySerializer(override val type: Type, factory: SerializerFactory) } } - override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput, debugIndent: Int) { + override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput, + context: SerializationContext, debugIndent: Int + ) { // Write described data.withDescribed(typeNotation.descriptor) { withList { for (entry in obj as Array<*>) { - output.writeObjectOrNull(entry, this, elementType, debugIndent) + output.writeObjectOrNull(entry, this, elementType, context, debugIndent) } } } } - override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput): Any { + override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput, + context: SerializationContext + ): Any { if (obj is List<*>) { - return obj.map { input.readObjectOrNull(it, schemas, elementType) }.toArrayOfType(elementType) + return obj.map { input.readObjectOrNull(it, schemas, elementType, context) }.toArrayOfType(elementType) } else throw NotSerializableException("Expected a List but found $obj") } @@ -118,20 +124,24 @@ abstract class PrimArraySerializer(type: Type, factory: SerializerFactory) : Arr } } -class PrimIntArraySerializer(factory: SerializerFactory) : - PrimArraySerializer(IntArray::class.java, factory) { - override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput, debugIndent: Int) { +class PrimIntArraySerializer(factory: SerializerFactory) : PrimArraySerializer(IntArray::class.java, factory) { + override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput, + context: SerializationContext, debugIndent: Int + ) { localWriteObject(data) { - (obj as IntArray).forEach { output.writeObjectOrNull(it, data, elementType, debugIndent+1) } + (obj as IntArray).forEach { output.writeObjectOrNull(it, data, elementType, context, debugIndent + 1) } } } } -class PrimCharArraySerializer(factory: SerializerFactory) : - PrimArraySerializer(CharArray::class.java, factory) { - override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput, debugIndent: Int) { - localWriteObject(data) { (obj as CharArray).forEach { - output.writeObjectOrNull(it, data, elementType, debugIndent+1) } +class PrimCharArraySerializer(factory: SerializerFactory) : PrimArraySerializer(CharArray::class.java, factory) { + override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput, + context: SerializationContext, debugIndent: Int + ) { + localWriteObject(data) { + (obj as CharArray).forEach { + output.writeObjectOrNull(it, data, elementType, context, debugIndent + 1) + } } } @@ -145,47 +155,55 @@ class PrimCharArraySerializer(factory: SerializerFactory) : } } -class PrimBooleanArraySerializer(factory: SerializerFactory) : - PrimArraySerializer(BooleanArray::class.java, factory) { - override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput, debugIndent: Int) { +class PrimBooleanArraySerializer(factory: SerializerFactory) : PrimArraySerializer(BooleanArray::class.java, factory) { + override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput, + context: SerializationContext, debugIndent: Int + ) { localWriteObject(data) { - (obj as BooleanArray).forEach { output.writeObjectOrNull(it, data, elementType, debugIndent+1) } + (obj as BooleanArray).forEach { output.writeObjectOrNull(it, data, elementType, context, debugIndent + 1) } } } } class PrimDoubleArraySerializer(factory: SerializerFactory) : PrimArraySerializer(DoubleArray::class.java, factory) { - override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput, debugIndent: Int) { + override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput, + context: SerializationContext, debugIndent: Int + ) { localWriteObject(data) { - (obj as DoubleArray).forEach { output.writeObjectOrNull(it, data, elementType, debugIndent+1) } + (obj as DoubleArray).forEach { output.writeObjectOrNull(it, data, elementType, context, debugIndent + 1) } } } } class PrimFloatArraySerializer(factory: SerializerFactory) : PrimArraySerializer(FloatArray::class.java, factory) { - override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput, debugIndent: Int) { + override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput, + context: SerializationContext, debugIndent: Int) { localWriteObject(data) { - (obj as FloatArray).forEach { output.writeObjectOrNull(it, data, elementType, debugIndent+1) } + (obj as FloatArray).forEach { output.writeObjectOrNull(it, data, elementType, context, debugIndent + 1) } } } } class PrimShortArraySerializer(factory: SerializerFactory) : PrimArraySerializer(ShortArray::class.java, factory) { - override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput, debugIndent: Int) { + override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput, + context: SerializationContext, debugIndent: Int + ) { localWriteObject(data) { - (obj as ShortArray).forEach { output.writeObjectOrNull(it, data, elementType, debugIndent+1) } + (obj as ShortArray).forEach { output.writeObjectOrNull(it, data, elementType, context, debugIndent + 1) } } } } class PrimLongArraySerializer(factory: SerializerFactory) : PrimArraySerializer(LongArray::class.java, factory) { - override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput, debugIndent: Int) { + override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput, + context: SerializationContext, debugIndent: Int + ) { localWriteObject(data) { - (obj as LongArray).forEach { output.writeObjectOrNull(it, data, elementType, debugIndent+1) } + (obj as LongArray).forEach { output.writeObjectOrNull(it, data, elementType, context, debugIndent + 1) } } } } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/CollectionSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/CollectionSerializer.kt index 63ebf26045..d5f027e211 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/CollectionSerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/CollectionSerializer.kt @@ -11,6 +11,7 @@ package net.corda.nodeapi.internal.serialization.amqp import net.corda.core.internal.uncheckedCast +import net.corda.core.serialization.SerializationContext import net.corda.core.utilities.NonEmptySet import org.apache.qpid.proton.amqp.Symbol import org.apache.qpid.proton.codec.Data @@ -18,15 +19,14 @@ import java.io.NotSerializableException import java.lang.reflect.ParameterizedType import java.lang.reflect.Type import java.util.* -import kotlin.collections.Collection import kotlin.collections.LinkedHashSet -import kotlin.collections.Set /** * Serialization / deserialization of predefined set of supported [Collection] types covering mostly [List]s and [Set]s. */ class CollectionSerializer(val declaredType: ParameterizedType, factory: SerializerFactory) : AMQPSerializer { - override val type: Type = declaredType as? DeserializedParameterizedType ?: DeserializedParameterizedType.make(SerializerFactory.nameForType(declaredType)) + override val type: Type = declaredType as? DeserializedParameterizedType + ?: DeserializedParameterizedType.make(SerializerFactory.nameForType(declaredType)) override val typeDescriptor by lazy { Symbol.valueOf("$DESCRIPTOR_DOMAIN:${factory.fingerPrinter.fingerprint(type)}") } @@ -60,7 +60,8 @@ class CollectionSerializer(val declaredType: ParameterizedType, factory: Seriali } private fun deriveParametrizedType(declaredType: Type, collectionClass: Class>): ParameterizedType = - (declaredType as? ParameterizedType) ?: DeserializedParameterizedType(collectionClass, arrayOf(SerializerFactory.AnyType)) + (declaredType as? ParameterizedType) + ?: DeserializedParameterizedType(collectionClass, arrayOf(SerializerFactory.AnyType)) private fun findMostSuitableCollectionType(actualClass: Class<*>): Class> = @@ -83,12 +84,13 @@ class CollectionSerializer(val declaredType: ParameterizedType, factory: Seriali data: Data, type: Type, output: SerializationOutput, + context: SerializationContext, debugIndent: Int) = ifThrowsAppend({ declaredType.typeName }) { // Write described data.withDescribed(typeNotation.descriptor) { withList { for (entry in obj as Collection<*>) { - output.writeObjectOrNull(entry, this, declaredType.actualTypeArguments[0], debugIndent) + output.writeObjectOrNull(entry, this, declaredType.actualTypeArguments[0], context, debugIndent) } } } @@ -97,8 +99,11 @@ class CollectionSerializer(val declaredType: ParameterizedType, factory: Seriali override fun readObject( obj: Any, schemas: SerializationSchemas, - input: DeserializationInput): Any = ifThrowsAppend({ declaredType.typeName }) { + input: DeserializationInput, + context: SerializationContext): Any = ifThrowsAppend({ declaredType.typeName }) { // TODO: Can we verify the entries in the list? - concreteBuilder((obj as List<*>).map { input.readObjectOrNull(it, schemas, declaredType.actualTypeArguments[0]) }) + concreteBuilder((obj as List<*>).map { + input.readObjectOrNull(it, schemas, declaredType.actualTypeArguments[0], context) + }) } } \ No newline at end of file diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/CorDappCustomSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/CorDappCustomSerializer.kt index 5cf9fea470..e2038fb626 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/CorDappCustomSerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/CorDappCustomSerializer.kt @@ -11,6 +11,7 @@ package net.corda.nodeapi.internal.serialization.amqp import net.corda.core.internal.uncheckedCast +import net.corda.core.serialization.SerializationContext import net.corda.core.serialization.SerializationCustomSerializer import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory.Companion.nameForType import org.apache.qpid.proton.amqp.Symbol @@ -74,22 +75,25 @@ class CorDappCustomSerializer( override fun writeClassInfo(output: SerializationOutput) {} - override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput, debugIndent: Int) { + override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput, + context: SerializationContext, debugIndent: Int + ) { val proxy = uncheckedCast, SerializationCustomSerializer>(serializer).toProxy(obj) data.withDescribed(descriptor) { data.withList { - proxySerializer.propertySerializers.serializationOrder.forEach { - it.getter.writeProperty(proxy, this, output) + proxySerializer.propertySerializers.serializationOrder.forEach { + it.getter.writeProperty(proxy, this, output, context) } } } } - override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput) = - uncheckedCast, SerializationCustomSerializer>( - serializer).fromProxy(uncheckedCast(proxySerializer.readObject(obj, schemas, input)))!! + override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput, + context: SerializationContext + ) = uncheckedCast, SerializationCustomSerializer>( + serializer).fromProxy(uncheckedCast(proxySerializer.readObject(obj, schemas, input, context)))!! override fun isSerializerFor(clazz: Class<*>) = clazz == type } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/CustomSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/CustomSerializer.kt index c8eaef09eb..b6ace5113a 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/CustomSerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/CustomSerializer.kt @@ -11,6 +11,7 @@ package net.corda.nodeapi.internal.serialization.amqp import net.corda.core.internal.uncheckedCast +import net.corda.core.serialization.SerializationContext import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory.Companion.nameForType import org.apache.qpid.proton.amqp.Symbol import org.apache.qpid.proton.codec.Data @@ -50,13 +51,16 @@ abstract class CustomSerializer : AMQPSerializer, SerializerFor { */ override val revealSubclassesInSchema: Boolean get() = false - override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput, debugIndent: Int) { + override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput, + context: SerializationContext, debugIndent: Int + ) { data.withDescribed(descriptor) { - writeDescribedObject(uncheckedCast(obj), data, type, output) + writeDescribedObject(uncheckedCast(obj), data, type, output, context) } } - abstract fun writeDescribedObject(obj: T, data: Data, type: Type, output: SerializationOutput) + abstract fun writeDescribedObject(obj: T, data: Data, type: Type, output: SerializationOutput, + context: SerializationContext) /** * This custom serializer represents a sort of symbolic link from a subclass to a super class, where the super @@ -87,12 +91,16 @@ abstract class CustomSerializer : AMQPSerializer, SerializerFor { override val descriptor: Descriptor = Descriptor(typeDescriptor) - override fun writeDescribedObject(obj: T, data: Data, type: Type, output: SerializationOutput) { - superClassSerializer.writeDescribedObject(obj, data, type, output) + override fun writeDescribedObject(obj: T, data: Data, type: Type, output: SerializationOutput, + context: SerializationContext + ) { + superClassSerializer.writeDescribedObject(obj, data, type, output, context) } - override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput): T { - return superClassSerializer.readObject(obj, schemas, input) + override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput, + context: SerializationContext + ): T { + return superClassSerializer.readObject(obj, schemas, input, context) } } @@ -134,7 +142,12 @@ abstract class CustomSerializer : AMQPSerializer, SerializerFor { private val proxySerializer: ObjectSerializer by lazy { ObjectSerializer(proxyClass, factory) } override val schemaForDocumentation: Schema by lazy { - val typeNotations = mutableSetOf(CompositeType(nameForType(type), null, emptyList(), descriptor, (proxySerializer.typeNotation as CompositeType).fields)) + val typeNotations = mutableSetOf( + CompositeType( + nameForType(type), + null, + emptyList(), + descriptor, (proxySerializer.typeNotation as CompositeType).fields)) for (additional in additionalSerializers) { typeNotations.addAll(additional.schemaForDocumentation.types) } @@ -148,17 +161,21 @@ abstract class CustomSerializer : AMQPSerializer, SerializerFor { protected abstract fun fromProxy(proxy: P): T - override fun writeDescribedObject(obj: T, data: Data, type: Type, output: SerializationOutput) { + override fun writeDescribedObject(obj: T, data: Data, type: Type, output: SerializationOutput, + context: SerializationContext + ) { val proxy = toProxy(obj) data.withList { proxySerializer.propertySerializers.serializationOrder.forEach { - it.getter.writeProperty(proxy, this, output) + it.getter.writeProperty(proxy, this, output, context) } } } - override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput): T { - val proxy: P = uncheckedCast(proxySerializer.readObject(obj, schemas, input)) + override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput, + context: SerializationContext + ): T { + val proxy: P = uncheckedCast(proxySerializer.readObject(obj, schemas, input, context)) return fromProxy(proxy) } } @@ -186,11 +203,15 @@ abstract class CustomSerializer : AMQPSerializer, SerializerFor { SerializerFactory.primitiveTypeName(String::class.java)!!, descriptor, emptyList()))) - override fun writeDescribedObject(obj: T, data: Data, type: Type, output: SerializationOutput) { + override fun writeDescribedObject(obj: T, data: Data, type: Type, output: SerializationOutput, + context: SerializationContext + ) { data.putString(unmaker(obj)) } - override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput): T { + override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput, + context: SerializationContext + ): T { val proxy = obj as String return maker(proxy) } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializationInput.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializationInput.kt index 78d4402516..1c2cb29c44 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializationInput.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializationInput.kt @@ -14,6 +14,7 @@ import com.esotericsoftware.kryo.io.ByteBufferInputStream import net.corda.core.internal.VisibleForTesting import net.corda.core.internal.getStackTraceAsString import net.corda.core.serialization.EncodingWhitelist +import net.corda.core.serialization.SerializationContext import net.corda.core.serialization.SerializedBytes import net.corda.core.utilities.ByteSequence import net.corda.nodeapi.internal.serialization.CordaSerializationEncoding @@ -45,7 +46,7 @@ class DeserializationInput @JvmOverloads constructor(private val serializerFacto private val encodingWhitelist: EncodingWhitelist = NullEncodingWhitelist) { private val objectHistory: MutableList = mutableListOf() - internal companion object { + companion object { private val BYTES_NEEDED_TO_PEEK: Int = 23 fun peekSize(bytes: ByteArray): Int { @@ -70,9 +71,10 @@ class DeserializationInput @JvmOverloads constructor(private val serializerFacto @VisibleForTesting @Throws(NotSerializableException::class) - internal fun withDataBytes(byteSequence: ByteSequence, encodingWhitelist: EncodingWhitelist, task: (ByteBuffer) -> T): T { + fun withDataBytes(byteSequence: ByteSequence, encodingWhitelist: EncodingWhitelist, task: (ByteBuffer) -> T): T { // Check that the lead bytes match expected header - val amqpSequence = amqpMagic.consume(byteSequence) ?: throw NotSerializableException("Serialization header does not match.") + val amqpSequence = amqpMagic.consume(byteSequence) + ?: throw NotSerializableException("Serialization header does not match.") var stream: InputStream = ByteBufferInputStream(amqpSequence) try { while (true) { @@ -89,25 +91,27 @@ class DeserializationInput @JvmOverloads constructor(private val serializerFacto stream.close() } } - } - @Throws(NotSerializableException::class) - inline fun deserialize(bytes: SerializedBytes): T = deserialize(bytes, T::class.java) - - @Throws(NotSerializableException::class) - inline internal fun deserializeAndReturnEnvelope(bytes: SerializedBytes): ObjectAndEnvelope = - deserializeAndReturnEnvelope(bytes, T::class.java) - - @Throws(NotSerializableException::class) - internal fun getEnvelope(byteSequence: ByteSequence): Envelope { - return withDataBytes(byteSequence, encodingWhitelist) { dataBytes -> - val data = Data.Factory.create() - val expectedSize = dataBytes.remaining() - if (data.decode(dataBytes) != expectedSize.toLong()) throw NotSerializableException("Unexpected size of data") - Envelope.get(data) + @Throws(NotSerializableException::class) + fun getEnvelope(byteSequence: ByteSequence, encodingWhitelist: EncodingWhitelist = NullEncodingWhitelist): Envelope { + return withDataBytes(byteSequence, encodingWhitelist) { dataBytes -> + val data = Data.Factory.create() + val expectedSize = dataBytes.remaining() + if (data.decode(dataBytes) != expectedSize.toLong()) throw NotSerializableException("Unexpected size of data") + Envelope.get(data) + } } } + + @Throws(NotSerializableException::class) + fun getEnvelope(byteSequence: ByteSequence) = Companion.getEnvelope(byteSequence, encodingWhitelist) + + @Throws(NotSerializableException::class) + inline fun deserialize(bytes: SerializedBytes, context: SerializationContext): T = + deserialize(bytes, T::class.java, context) + + @Throws(NotSerializableException::class) private fun des(generator: () -> R): R { try { @@ -127,23 +131,37 @@ class DeserializationInput @JvmOverloads constructor(private val serializerFacto * be deserialized and a schema describing the types of the objects. */ @Throws(NotSerializableException::class) - fun deserialize(bytes: ByteSequence, clazz: Class): T = des { - val envelope = getEnvelope(bytes) - clazz.cast(readObjectOrNull(envelope.obj, SerializationSchemas(envelope.schema, envelope.transformsSchema), clazz)) - } + fun deserialize(bytes: ByteSequence, clazz: Class, context: SerializationContext): T = + des { + val envelope = getEnvelope(bytes, encodingWhitelist) + clazz.cast(readObjectOrNull(envelope.obj, SerializationSchemas(envelope.schema, envelope.transformsSchema), + clazz, context)) + } @Throws(NotSerializableException::class) - fun deserializeAndReturnEnvelope(bytes: SerializedBytes, clazz: Class): ObjectAndEnvelope = des { - val envelope = getEnvelope(bytes) + fun deserializeAndReturnEnvelope( + bytes: SerializedBytes, + clazz: Class, + context: SerializationContext + ): ObjectAndEnvelope = des { + val envelope = getEnvelope(bytes, encodingWhitelist) // Now pick out the obj and schema from the envelope. - ObjectAndEnvelope(clazz.cast(readObjectOrNull(envelope.obj, SerializationSchemas(envelope.schema, envelope.transformsSchema), clazz)), envelope) + ObjectAndEnvelope( + clazz.cast(readObjectOrNull( + envelope.obj, + SerializationSchemas(envelope.schema, envelope.transformsSchema), + clazz, + context)), + envelope) } - internal fun readObjectOrNull(obj: Any?, schema: SerializationSchemas, type: Type, offset: Int = 0): Any? { - return if (obj == null) null else readObject(obj, schema, type, offset) + internal fun readObjectOrNull(obj: Any?, schema: SerializationSchemas, type: Type, context: SerializationContext, + offset: Int = 0 + ): Any? { + return if (obj == null) null else readObject(obj, schema, type, context, offset) } - internal fun readObject(obj: Any, schemas: SerializationSchemas, type: Type, debugIndent: Int = 0): Any = + internal fun readObject(obj: Any, schemas: SerializationSchemas, type: Type, context: SerializationContext, debugIndent: Int = 0): Any = if (obj is DescribedType && ReferencedObject.DESCRIPTOR == obj.descriptor) { // It must be a reference to an instance that has already been read, cheaply and quickly returning it by reference. val objectIndex = (obj.described as UnsignedInteger).toInt() @@ -164,19 +182,20 @@ class DeserializationInput @JvmOverloads constructor(private val serializerFacto // Look up serializer in factory by descriptor val serializer = serializerFactory.get(obj.descriptor, schemas) if (SerializerFactory.AnyType != type && serializer.type != type && with(serializer.type) { - !isSubClassOf(type) && !materiallyEquivalentTo(type) - }) { + !isSubClassOf(type) && !materiallyEquivalentTo(type) + }) { throw NotSerializableException("Described type with descriptor ${obj.descriptor} was " + "expected to be of type $type but was ${serializer.type}") } - serializer.readObject(obj.described, schemas, this) + serializer.readObject(obj.described, schemas, this, context) } is Binary -> obj.array else -> obj // this will be the case for primitive types like [boolean] et al. } // Store the reference in case we need it later on. - // Skip for primitive types as they are too small and overhead of referencing them will be much higher than their content + // Skip for primitive types as they are too small and overhead of referencing them will be much higher + // than their content if (suitableForObjectReference(objectRead.javaClass)) { objectHistory.add(objectRead) } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/EnumEvolutionSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/EnumEvolutionSerializer.kt index d3d60ea93e..d45febb609 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/EnumEvolutionSerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/EnumEvolutionSerializer.kt @@ -11,6 +11,7 @@ package net.corda.nodeapi.internal.serialization.amqp import net.corda.core.internal.uncheckedCast +import net.corda.core.serialization.SerializationContext import org.apache.qpid.proton.amqp.Symbol import org.apache.qpid.proton.codec.Data import java.io.NotSerializableException @@ -75,7 +76,8 @@ class EnumEvolutionSerializer( new: AMQPSerializer, factory: SerializerFactory, schemas: SerializationSchemas): AMQPSerializer { - val wireTransforms = schemas.transforms.types[old.name] ?: EnumMap>(TransformTypes::class.java) + val wireTransforms = schemas.transforms.types[old.name] + ?: EnumMap>(TransformTypes::class.java) val localTransforms = TransformsSchema.get(old.name, factory) // remember, the longer the list the newer we're assuming the transform set it as we assume @@ -127,7 +129,9 @@ class EnumEvolutionSerializer( } } - override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput): Any { + override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput, + context: SerializationContext + ): Any { val enumName = (obj as List<*>)[0] as String if (enumName !in conversions) { @@ -141,7 +145,9 @@ class EnumEvolutionSerializer( throw UnsupportedOperationException("It should be impossible to write an evolution serializer") } - override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput, debugIndent: Int) { + override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput, + context: SerializationContext, debugIndent: Int + ) { throw UnsupportedOperationException("It should be impossible to write an evolution serializer") } } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/EnumSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/EnumSerializer.kt index e481950924..15b6fbfdb3 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/EnumSerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/EnumSerializer.kt @@ -10,6 +10,7 @@ package net.corda.nodeapi.internal.serialization.amqp +import net.corda.core.serialization.SerializationContext import org.apache.qpid.proton.amqp.Symbol import org.apache.qpid.proton.codec.Data import java.io.NotSerializableException @@ -38,7 +39,9 @@ class EnumSerializer(declaredType: Type, declaredClass: Class<*>, factory: Seria output.writeTypeNotations(typeNotation) } - override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput): Any { + override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput, + context: SerializationContext + ): Any { val enumName = (obj as List<*>)[0] as String val enumOrd = obj[1] as Int val fromOrd = type.asClass()!!.enumConstants[enumOrd] as Enum<*>? @@ -50,7 +53,9 @@ class EnumSerializer(declaredType: Type, declaredClass: Class<*>, factory: Seria return fromOrd } - override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput, debugIndent: Int) { + override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput, + context: SerializationContext, debugIndent: Int + ) { if (obj !is Enum<*>) throw NotSerializableException("Serializing $obj as enum when it isn't") data.withDescribed(typeNotation.descriptor) { diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/Envelope.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/Envelope.kt index 9b27e264c1..b8941c7143 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/Envelope.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/Envelope.kt @@ -39,7 +39,7 @@ data class Envelope(val obj: Any?, val schema: Schema, val transformsSchema: Tra fun get(data: Data): Envelope { val describedType = data.`object` as DescribedType if (describedType.descriptor != DESCRIPTOR) { - throw NotSerializableException("Unexpected descriptor ${describedType.descriptor}.") + throw NotSerializableException("Unexpected descriptor ${describedType.descriptor}, should be $DESCRIPTOR.") } val list = describedType.described as List<*> diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/EvolutionSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/EvolutionSerializer.kt index 6f6ddac708..f08f9dce2c 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/EvolutionSerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/EvolutionSerializer.kt @@ -11,10 +11,11 @@ package net.corda.nodeapi.internal.serialization.amqp import net.corda.core.serialization.DeprecatedConstructorForDeserialization +import net.corda.core.serialization.SerializationContext import net.corda.nodeapi.internal.serialization.carpenter.getTypeAsClass import org.apache.qpid.proton.codec.Data -import java.lang.reflect.Type import java.io.NotSerializableException +import java.lang.reflect.Type import kotlin.reflect.KFunction import kotlin.reflect.full.findAnnotation import kotlin.reflect.jvm.javaType @@ -49,12 +50,13 @@ abstract class EvolutionSerializer( * @param property object to read the actual property value */ data class OldParam(var resultsIndex: Int, val property: PropertySerializer) { - fun readProperty(obj: Any?, schemas: SerializationSchemas, input: DeserializationInput, new: Array) = - property.readProperty(obj, schemas, input).apply { - if(resultsIndex >= 0) { - new[resultsIndex] = this - } - } + fun readProperty(obj: Any?, schemas: SerializationSchemas, input: DeserializationInput, + new: Array, context: SerializationContext + ) = property.readProperty(obj, schemas, input, context).apply { + if (resultsIndex >= 0) { + new[resultsIndex] = this + } + } } companion object { @@ -106,7 +108,7 @@ abstract class EvolutionSerializer( "New parameter ${it.value.name} is mandatory, should be nullable for evolution to worK") } } - return EvolutionSerializerViaConstructor (new.type, factory, readersAsSerialized, constructor, constructorArgs) + return EvolutionSerializerViaConstructor(new.type, factory, readersAsSerialized, constructor, constructorArgs) } private fun makeWithSetters( @@ -118,7 +120,7 @@ abstract class EvolutionSerializer( val setters = propertiesForSerializationFromSetters(classProperties, new.type, factory).associateBy({ it.getter.name }, { it }) - return EvolutionSerializerViaSetters (new.type, factory, readersAsSerialized, constructor, setters) + return EvolutionSerializerViaSetters(new.type, factory, readersAsSerialized, constructor, setters) } /** @@ -153,14 +155,15 @@ abstract class EvolutionSerializer( return if (classProperties.isNotEmpty() && constructor.parameters.isEmpty()) { makeWithSetters(new, factory, constructor, readersAsSerialized, classProperties) - } - else { + } else { makeWithConstructor(new, factory, constructor, readersAsSerialized) } } } - override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput, debugIndent: Int) { + override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput, + context: SerializationContext, debugIndent: Int + ) { throw UnsupportedOperationException("It should be impossible to write an evolution serializer") } } @@ -170,7 +173,7 @@ class EvolutionSerializerViaConstructor( factory: SerializerFactory, oldReaders: Map, kotlinConstructor: KFunction?, - private val constructorArgs: Array) : EvolutionSerializer (clazz, factory, oldReaders, kotlinConstructor) { + private val constructorArgs: Array) : EvolutionSerializer(clazz, factory, oldReaders, kotlinConstructor) { /** * Unlike a normal [readObject] call where we simply apply the parameter deserialisers * to the object list of values we need to map that list, which is ordered per the @@ -180,15 +183,16 @@ class EvolutionSerializerViaConstructor( * * TODO: Object references */ - override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput): Any { + override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput, + context: SerializationContext + ): Any { if (obj !is List<*>) throw NotSerializableException("Body of described type is unexpected $obj") // *must* read all the parameters in the order they were serialized - oldReaders.values.zip(obj).map { it.first.readProperty(it.second, schemas, input, constructorArgs) } + oldReaders.values.zip(obj).map { it.first.readProperty(it.second, schemas, input, constructorArgs, context) } - return javaConstructor?.newInstance(*(constructorArgs)) ?: - throw NotSerializableException( - "Attempt to deserialize an interface: $clazz. Serialized form is invalid.") + return javaConstructor?.newInstance(*(constructorArgs)) ?: throw NotSerializableException( + "Attempt to deserialize an interface: $clazz. Serialized form is invalid.") } } @@ -201,18 +205,20 @@ class EvolutionSerializerViaSetters( factory: SerializerFactory, oldReaders: Map, kotlinConstructor: KFunction?, - private val setters: Map) : EvolutionSerializer (clazz, factory, oldReaders, kotlinConstructor) { + private val setters: Map) : EvolutionSerializer(clazz, factory, oldReaders, kotlinConstructor) { - override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput): Any { + override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput, + context: SerializationContext + ): Any { if (obj !is List<*>) throw NotSerializableException("Body of described type is unexpected $obj") - val instance : Any = javaConstructor?.newInstance() ?: throw NotSerializableException ( + val instance: Any = javaConstructor?.newInstance() ?: throw NotSerializableException( "Failed to instantiate instance of object $clazz") // *must* read all the parameters in the order they were serialized oldReaders.values.zip(obj).forEach { // if that property still exists on the new object then set it - it.first.property.readProperty(it.second, schemas, input).apply { + it.first.property.readProperty(it.second, schemas, input, context).apply { setters[it.first.property.name]?.set(instance, this) } } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/MapSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/MapSerializer.kt index 8eb5c4332b..d12125d90b 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/MapSerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/MapSerializer.kt @@ -11,6 +11,7 @@ package net.corda.nodeapi.internal.serialization.amqp import net.corda.core.internal.uncheckedCast +import net.corda.core.serialization.SerializationContext import org.apache.qpid.proton.amqp.Symbol import org.apache.qpid.proton.codec.Data import java.io.NotSerializableException @@ -18,9 +19,6 @@ import java.lang.reflect.ParameterizedType import java.lang.reflect.Type import java.util.* import kotlin.collections.LinkedHashMap -import kotlin.collections.Map -import kotlin.collections.iterator -import kotlin.collections.map private typealias MapCreationFunction = (Map<*, *>) -> Map<*, *> @@ -28,8 +26,8 @@ private typealias MapCreationFunction = (Map<*, *>) -> Map<*, *> * Serialization / deserialization of certain supported [Map] types. */ class MapSerializer(private val declaredType: ParameterizedType, factory: SerializerFactory) : AMQPSerializer { - override val type: Type = (declaredType as? DeserializedParameterizedType) ?: - DeserializedParameterizedType.make(SerializerFactory.nameForType(declaredType), factory.classloader) + override val type: Type = (declaredType as? DeserializedParameterizedType) + ?: DeserializedParameterizedType.make(SerializerFactory.nameForType(declaredType), factory.classloader) override val typeDescriptor: Symbol = Symbol.valueOf( "$DESCRIPTOR_DOMAIN:${factory.fingerPrinter.fingerprint(type)}") @@ -67,7 +65,8 @@ class MapSerializer(private val declaredType: ParameterizedType, factory: Serial } private fun deriveParametrizedType(declaredType: Type, collectionClass: Class>): ParameterizedType = - (declaredType as? ParameterizedType) ?: DeserializedParameterizedType(collectionClass, arrayOf(SerializerFactory.AnyType, SerializerFactory.AnyType)) + (declaredType as? ParameterizedType) + ?: DeserializedParameterizedType(collectionClass, arrayOf(SerializerFactory.AnyType, SerializerFactory.AnyType)) private fun findMostSuitableMapType(actualClass: Class<*>): Class> = @@ -90,6 +89,7 @@ class MapSerializer(private val declaredType: ParameterizedType, factory: Serial data: Data, type: Type, output: SerializationOutput, + context: SerializationContext, debugIndent: Int) = ifThrowsAppend({ declaredType.typeName }) { obj.javaClass.checkSupportedMapType() // Write described @@ -98,22 +98,25 @@ class MapSerializer(private val declaredType: ParameterizedType, factory: Serial data.putMap() data.enter() for ((key, value) in obj as Map<*, *>) { - output.writeObjectOrNull(key, data, declaredType.actualTypeArguments[0], debugIndent) - output.writeObjectOrNull(value, data, declaredType.actualTypeArguments[1], debugIndent) + output.writeObjectOrNull(key, data, declaredType.actualTypeArguments[0], context, debugIndent) + output.writeObjectOrNull(value, data, declaredType.actualTypeArguments[1], context, debugIndent) } data.exit() // exit map } } - override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput): Any = ifThrowsAppend({ declaredType.typeName }) { + override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput, + context: SerializationContext + ): Any = ifThrowsAppend({ declaredType.typeName }) { // TODO: General generics question. Do we need to validate that entries in Maps and Collections match the generic type? Is it a security hole? - val entries: Iterable> = (obj as Map<*, *>).map { readEntry(schemas, input, it) } + val entries: Iterable> = (obj as Map<*, *>).map { readEntry(schemas, input, it, context) } concreteBuilder(entries.toMap()) } - private fun readEntry(schemas: SerializationSchemas, input: DeserializationInput, entry: Map.Entry) = - input.readObjectOrNull(entry.key, schemas, declaredType.actualTypeArguments[0]) to - input.readObjectOrNull(entry.value, schemas, declaredType.actualTypeArguments[1]) + private fun readEntry(schemas: SerializationSchemas, input: DeserializationInput, entry: Map.Entry, + context: SerializationContext + ) = input.readObjectOrNull(entry.key, schemas, declaredType.actualTypeArguments[0], context) to + input.readObjectOrNull(entry.value, schemas, declaredType.actualTypeArguments[1], context) // Cannot use * as a bound for EnumMap and EnumSet since * is not an enum. So, we use a sample enum instead. // We don't actually care about the type, we just need to make the compiler happier. diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/ObjectSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/ObjectSerializer.kt index 447ca44181..c903a13019 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/ObjectSerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/ObjectSerializer.kt @@ -10,6 +10,7 @@ package net.corda.nodeapi.internal.serialization.amqp +import net.corda.core.serialization.SerializationContext import net.corda.core.utilities.contextLogger import net.corda.core.utilities.trace import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory.Companion.nameForType @@ -67,6 +68,7 @@ open class ObjectSerializer(val clazz: Type, factory: SerializerFactory) : AMQPS data: Data, type: Type, output: SerializationOutput, + context: SerializationContext, debugIndent: Int) = ifThrowsAppend({ clazz.typeName } ) { if (propertySerializers.size != javaConstructor?.parameterCount && @@ -82,7 +84,7 @@ open class ObjectSerializer(val clazz: Type, factory: SerializerFactory) : AMQPS // Write list withList { propertySerializers.serializationOrder.forEach { property -> - property.getter.writeProperty(obj, this, output, debugIndent + 1) + property.getter.writeProperty(obj, this, output, context, debugIndent + 1) } } } @@ -91,16 +93,17 @@ open class ObjectSerializer(val clazz: Type, factory: SerializerFactory) : AMQPS override fun readObject( obj: Any, schemas: SerializationSchemas, - input: DeserializationInput): Any = ifThrowsAppend({ clazz.typeName }) { + input: DeserializationInput, + context: SerializationContext): Any = ifThrowsAppend({ clazz.typeName }) { if (obj is List<*>) { if (obj.size > propertySerializers.size) { throw NotSerializableException("Too many properties in described type $typeName") } return if (propertySerializers.byConstructor) { - readObjectBuildViaConstructor(obj, schemas, input) + readObjectBuildViaConstructor(obj, schemas, input, context) } else { - readObjectBuildViaSetters(obj, schemas, input) + readObjectBuildViaSetters(obj, schemas, input, context) } } else { throw NotSerializableException("Body of described type is unexpected $obj") @@ -110,12 +113,13 @@ open class ObjectSerializer(val clazz: Type, factory: SerializerFactory) : AMQPS private fun readObjectBuildViaConstructor( obj: List<*>, schemas: SerializationSchemas, - input: DeserializationInput): Any = ifThrowsAppend({ clazz.typeName }) { + input: DeserializationInput, + context: SerializationContext): Any = ifThrowsAppend({ clazz.typeName }) { logger.trace { "Calling construction based construction for ${clazz.typeName}" } return construct(propertySerializers.serializationOrder .zip(obj) - .map { Pair(it.first.initialPosition, it.first.getter.readProperty(it.second, schemas, input)) } + .map { Pair(it.first.initialPosition, it.first.getter.readProperty(it.second, schemas, input, context)) } .sortedWith(compareBy({ it.first })) .map { it.second }) } @@ -123,7 +127,8 @@ open class ObjectSerializer(val clazz: Type, factory: SerializerFactory) : AMQPS private fun readObjectBuildViaSetters( obj: List<*>, schemas: SerializationSchemas, - input: DeserializationInput): Any = ifThrowsAppend({ clazz.typeName }) { + input: DeserializationInput, + context: SerializationContext): Any = ifThrowsAppend({ clazz.typeName }) { logger.trace { "Calling setter based construction for ${clazz.typeName}" } val instance: Any = javaConstructor?.newInstance() ?: throw NotSerializableException( @@ -133,7 +138,7 @@ open class ObjectSerializer(val clazz: Type, factory: SerializerFactory) : AMQPS // do it in doesn't matter val propertiesFromBlob = obj .zip(propertySerializers.serializationOrder) - .map { it.second.getter.readProperty(it.first, schemas, input) } + .map { it.second.getter.readProperty(it.first, schemas, input, context) } // one by one take a property and invoke the setter on the class propertySerializers.serializationOrder.zip(propertiesFromBlob).forEach { diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/PropertySerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/PropertySerializer.kt index 61dbd7ec3d..9786d24998 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/PropertySerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/PropertySerializer.kt @@ -10,6 +10,7 @@ package net.corda.nodeapi.internal.serialization.amqp +import net.corda.core.serialization.SerializationContext import org.apache.qpid.proton.amqp.Binary import org.apache.qpid.proton.codec.Data import java.lang.reflect.Type @@ -19,8 +20,8 @@ import java.lang.reflect.Type */ sealed class PropertySerializer(val name: String, val propertyReader: PropertyReader, val resolvedType: Type) { abstract fun writeClassInfo(output: SerializationOutput) - abstract fun writeProperty(obj: Any?, data: Data, output: SerializationOutput, debugIndent: Int = 0) - abstract fun readProperty(obj: Any?, schemas: SerializationSchemas, input: DeserializationInput): Any? + abstract fun writeProperty(obj: Any?, data: Data, output: SerializationOutput, context: SerializationContext, debugIndent: Int = 0) + abstract fun readProperty(obj: Any?, schemas: SerializationSchemas, input: DeserializationInput, context: SerializationContext): Any? val type: String = generateType() val requires: List = generateRequires() @@ -86,12 +87,15 @@ sealed class PropertySerializer(val name: String, val propertyReader: PropertyRe override fun readProperty( obj: Any?, schemas: SerializationSchemas, - input: DeserializationInput): Any? = ifThrowsAppend({ nameForDebug }) { - input.readObjectOrNull(obj, schemas, resolvedType) + input: DeserializationInput, + context: SerializationContext): Any? = ifThrowsAppend({ nameForDebug }) { + input.readObjectOrNull(obj, schemas, resolvedType, context) } - override fun writeProperty(obj: Any?, data: Data, output: SerializationOutput, debugIndent: Int) = ifThrowsAppend({ nameForDebug }) { - output.writeObjectOrNull(propertyReader.read(obj), data, resolvedType, debugIndent) + override fun writeProperty(obj: Any?, data: Data, output: SerializationOutput, + context: SerializationContext, debugIndent: Int) = ifThrowsAppend({ nameForDebug } + ) { + output.writeObjectOrNull(propertyReader.read(obj), data, resolvedType, context, debugIndent) } private val nameForDebug = "$name(${resolvedType.typeName})" @@ -106,11 +110,15 @@ sealed class PropertySerializer(val name: String, val propertyReader: PropertyRe resolvedType: Type) : PropertySerializer(name, readMethod, resolvedType) { override fun writeClassInfo(output: SerializationOutput) {} - override fun readProperty(obj: Any?, schemas: SerializationSchemas, input: DeserializationInput): Any? { + override fun readProperty(obj: Any?, schemas: SerializationSchemas, + input: DeserializationInput, context: SerializationContext + ): Any? { return if (obj is Binary) obj.array else obj } - override fun writeProperty(obj: Any?, data: Data, output: SerializationOutput, debugIndent: Int) { + override fun writeProperty(obj: Any?, data: Data, output: SerializationOutput, + context: SerializationContext, debugIndent: Int + ) { val value = propertyReader.read(obj) if (value is ByteArray) { data.putObject(Binary(value)) @@ -122,18 +130,22 @@ sealed class PropertySerializer(val name: String, val propertyReader: PropertyRe /** * A property serializer for the AMQP char type, needed as a specialisation as the underlying - * value of the character is stored in numeric UTF-16 form and on deserialisation requires explicit + * value of the character is stored in numeric UTF-16 form and on deserialization requires explicit * casting back to a char otherwise it's treated as an Integer and a TypeMismatch occurs */ class AMQPCharPropertySerializer(name: String, readMethod: PropertyReader) : PropertySerializer(name, readMethod, Character::class.java) { override fun writeClassInfo(output: SerializationOutput) {} - override fun readProperty(obj: Any?, schemas: SerializationSchemas, input: DeserializationInput): Any? { + override fun readProperty(obj: Any?, schemas: SerializationSchemas, + input: DeserializationInput, context: SerializationContext + ): Any? { return if (obj == null) null else (obj as Short).toChar() } - override fun writeProperty(obj: Any?, data: Data, output: SerializationOutput, debugIndent: Int) { + override fun writeProperty(obj: Any?, data: Data, output: SerializationOutput, + context: SerializationContext, debugIndent: Int + ) { val input = propertyReader.read(obj) if (input != null) data.putShort((input as Char).toShort()) else data.putNull() } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/PropertySerializers.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/PropertySerializers.kt index 5e7e17765c..c7b72c646f 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/PropertySerializers.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/PropertySerializers.kt @@ -12,12 +12,12 @@ package net.corda.nodeapi.internal.serialization.amqp import net.corda.core.utilities.loggerFor import java.io.NotSerializableException +import java.lang.reflect.Field import java.lang.reflect.Method import java.lang.reflect.Type import kotlin.reflect.full.memberProperties import kotlin.reflect.jvm.javaGetter import kotlin.reflect.jvm.kotlinProperty -import java.lang.reflect.Field abstract class PropertyReader { abstract fun read(obj: Any?): Any? @@ -151,6 +151,7 @@ class PropertyAccessorGetterSetter( */ setter.isAccessible = true } + /** * Invokes the setter on the underlying object passing in the serialized value. */ @@ -172,7 +173,7 @@ class PropertyAccessorConstructor( * calls to the explicit setter should be an error. */ override fun set(instance: Any, obj: Any?) { - NotSerializableException ("Attempting to access a setter on an object being instantiated " + + NotSerializableException("Attempting to access a setter on an object being instantiated " + "via its constructor.") } } @@ -197,7 +198,7 @@ abstract class PropertySerializers( is PropertyAccessorGetterSetter -> PropertySerializersSetter(serializationOrder) null -> PropertySerializersNoProperties() else -> { - throw NotSerializableException ("Unknown Property Accessor type, cannot create set") + throw NotSerializableException("Unknown Property Accessor type, cannot create set") } } } @@ -206,7 +207,7 @@ abstract class PropertySerializers( abstract val byConstructor: Boolean } -class PropertySerializersNoProperties : PropertySerializers (emptyList()) { +class PropertySerializersNoProperties : PropertySerializers(emptyList()) { override val byConstructor get() = true } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationHelper.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationHelper.kt index 950099ffcc..e231532633 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationHelper.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationHelper.kt @@ -105,7 +105,7 @@ data class PropertyDescriptor(var field: Field?, var setter: Method?, var getter constructor() : this(null, null, null, null) - fun preferredGetter() : Method? = getter ?: iser + fun preferredGetter(): Method? = getter ?: iser } object PropertyDescriptorsRegex { @@ -173,8 +173,7 @@ fun Class.propertyDescriptors(): Map { // fails the getter doesn't refer to a property directly, but may refer to a constructor // parameter that shadows a property val properties = - classProperties[groups[2]!!.value] ?: - classProperties[groups[2]!!.value.decapitalize()] ?: + classProperties[groups[2]!!.value] ?: classProperties[groups[2]!!.value.decapitalize()] ?: // take into account those constructor properties that don't directly map to a named // property which are, by default, already added to the map classProperties.computeIfAbsent(groups[2]!!.value) { PropertyDescriptor() } @@ -255,9 +254,9 @@ internal fun propertiesForSerializationFromConstructor( // We will already have disambiguated getA for property A or a but we still need to cope // with the case we don't know the case of A when the parameter doesn't match a property // but has a getter - val matchingProperty = classProperties[name] ?: classProperties[name.capitalize()] ?: - throw NotSerializableException( - "Constructor parameter - \"$name\" - doesn't refer to a property of \"$clazz\"") + val matchingProperty = classProperties[name] ?: classProperties[name.capitalize()] + ?: throw NotSerializableException( + "Constructor parameter - \"$name\" - doesn't refer to a property of \"$clazz\"") // If the property has a getter we'll use that to retrieve it's value from the instance, if it doesn't // *for *know* we switch to a reflection based method @@ -277,8 +276,8 @@ internal fun propertiesForSerializationFromConstructor( Pair(PublicPropertyReader(getter), returnType) } else { - val field = classProperties[name]!!.field ?: - throw NotSerializableException("No property matching constructor parameter named - \"$name\" - " + + val field = classProperties[name]!!.field + ?: throw NotSerializableException("No property matching constructor parameter named - \"$name\" - " + "of \"$clazz\". If using Java, check that you have the -parameters option specified " + "in the Java compiler. Alternately, provide a proxy serializer " + "(SerializationCustomSerializer) if recompiling isn't an option") @@ -325,7 +324,7 @@ fun propertiesForSerializationFromSetters( } // Make sure the getter returns the same type (within inheritance bounds) the setter accepts. - if (!(TypeToken.of (getter.genericReturnType).isSupertypeOf(setterType))) { + if (!(TypeToken.of(getter.genericReturnType).isSupertypeOf(setterType))) { throw NotSerializableException("Defined setter for parameter ${property.value.field?.name} " + "takes parameter of type $setterType yet the defined getter returns a value of type " + "${getter.returnType} [${getter.genericReturnType}]") diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationOutput.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationOutput.kt index 2f4c862362..9b29316260 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationOutput.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationOutput.kt @@ -10,6 +10,7 @@ package net.corda.nodeapi.internal.serialization.amqp +import net.corda.core.serialization.SerializationContext import net.corda.core.serialization.SerializationEncoding import net.corda.core.serialization.SerializedBytes import net.corda.nodeapi.internal.serialization.CordaSerializationEncoding @@ -33,7 +34,10 @@ data class BytesAndSchemas( * @param serializerFactory This is the factory for [AMQPSerializer] instances and can be shared across multiple * instances and threads. */ -open class SerializationOutput @JvmOverloads constructor(internal val serializerFactory: SerializerFactory, private val encoding: SerializationEncoding? = null) { +open class SerializationOutput @JvmOverloads constructor( + internal val serializerFactory: SerializerFactory, + private val encoding: SerializationEncoding? = null +) { private val objectHistory: MutableMap = IdentityHashMap() private val serializerHistory: MutableSet> = LinkedHashSet() internal val schemaHistory: MutableSet = LinkedHashSet() @@ -44,19 +48,18 @@ open class SerializationOutput @JvmOverloads constructor(internal val serializer * of AMQP serialization constructed the serialized form. */ @Throws(NotSerializableException::class) - fun serialize(obj: T): SerializedBytes { + fun serialize(obj: T, context: SerializationContext): SerializedBytes { try { - return _serialize(obj) + return _serialize(obj, context) } finally { andFinally() } } - @Throws(NotSerializableException::class) - fun serializeAndReturnSchema(obj: T): BytesAndSchemas { + fun serializeAndReturnSchema(obj: T, context: SerializationContext): BytesAndSchemas { try { - val blob = _serialize(obj) + val blob = _serialize(obj, context) val schema = Schema(schemaHistory.toList()) return BytesAndSchemas(blob, schema, TransformsSchema.build(schema, serializerFactory)) } finally { @@ -70,11 +73,11 @@ open class SerializationOutput @JvmOverloads constructor(internal val serializer schemaHistory.clear() } - internal fun _serialize(obj: T): SerializedBytes { + internal fun _serialize(obj: T, context: SerializationContext): SerializedBytes { val data = Data.Factory.create() data.withDescribed(Envelope.DESCRIPTOR_OBJECT) { withList { - writeObject(obj, this) + writeObject(obj, this, context) val schema = Schema(schemaHistory.toList()) writeSchema(schema, this) writeTransformSchema(TransformsSchema.build(schema, serializerFactory), this) @@ -97,8 +100,8 @@ open class SerializationOutput @JvmOverloads constructor(internal val serializer }) } - internal fun writeObject(obj: Any, data: Data) { - writeObject(obj, data, obj.javaClass) + internal fun writeObject(obj: Any, data: Data, context: SerializationContext) { + writeObject(obj, data, obj.javaClass, context) } open fun writeSchema(schema: Schema, data: Data) { @@ -109,15 +112,15 @@ open class SerializationOutput @JvmOverloads constructor(internal val serializer data.putObject(transformsSchema) } - internal fun writeObjectOrNull(obj: Any?, data: Data, type: Type, debugIndent: Int) { + internal fun writeObjectOrNull(obj: Any?, data: Data, type: Type, context: SerializationContext, debugIndent: Int) { if (obj == null) { data.putNull() } else { - writeObject(obj, data, if (type == SerializerFactory.AnyType) obj.javaClass else type, debugIndent) + writeObject(obj, data, if (type == SerializerFactory.AnyType) obj.javaClass else type, context, debugIndent) } } - internal fun writeObject(obj: Any, data: Data, type: Type, debugIndent: Int = 0) { + internal fun writeObject(obj: Any, data: Data, type: Type, context: SerializationContext, debugIndent: Int = 0) { val serializer = serializerFactory.get(obj.javaClass, type) if (serializer !in serializerHistory) { serializerHistory.add(serializer) @@ -126,7 +129,7 @@ open class SerializationOutput @JvmOverloads constructor(internal val serializer val retrievedRefCount = objectHistory[obj] if (retrievedRefCount == null) { - serializer.writeObject(obj, data, type, this, debugIndent) + serializer.writeObject(obj, data, type, this, context, debugIndent) // Important to do it after serialization such that dependent object will have preceding reference numbers // assigned to them first as they will be first read from the stream on receiving end. // Skip for primitive types as they are too small and overhead of referencing them will be much higher than their content diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializerFactory.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializerFactory.kt index 53ebafe871..6d3c8f10f6 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializerFactory.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializerFactory.kt @@ -15,9 +15,11 @@ import com.google.common.reflect.TypeResolver import net.corda.core.internal.getStackTraceAsString import net.corda.core.internal.uncheckedCast import net.corda.core.serialization.ClassWhitelist -import net.corda.core.utilities.contextLogger import net.corda.core.utilities.loggerFor -import net.corda.nodeapi.internal.serialization.carpenter.* +import net.corda.nodeapi.internal.serialization.carpenter.CarpenterMetaSchema +import net.corda.nodeapi.internal.serialization.carpenter.ClassCarpenter +import net.corda.nodeapi.internal.serialization.carpenter.MetaCarpenter +import net.corda.nodeapi.internal.serialization.carpenter.MetaCarpenterException import org.apache.qpid.proton.amqp.* import java.io.NotSerializableException import java.lang.reflect.* @@ -69,8 +71,7 @@ open class SerializerFactory( get() = classCarpenter.classloader private fun getEvolutionSerializer(typeNotation: TypeNotation, newSerializer: AMQPSerializer, - schemas: SerializationSchemas) - = evolutionSerializerGetter.getEvolutionSerializer(this, typeNotation, newSerializer, schemas) + schemas: SerializationSchemas) = evolutionSerializerGetter.getEvolutionSerializer(this, typeNotation, newSerializer, schemas) fun getSerializersByDescriptor() = serializersByDescriptor @@ -109,7 +110,8 @@ open class SerializerFactory( makeMapSerializer(declaredTypeAmended) } } - Enum::class.java.isAssignableFrom(actualClass ?: declaredClass) -> serializersByType.computeIfAbsent(actualClass ?: declaredClass) { + Enum::class.java.isAssignableFrom(actualClass + ?: declaredClass) -> serializersByType.computeIfAbsent(actualClass ?: declaredClass) { whitelist.requireWhitelisted(actualType) EnumSerializer(actualType, actualClass ?: declaredClass, this) } @@ -254,8 +256,8 @@ open class SerializerFactory( } catch (e: MetaCarpenterException) { // preserve the actual message locally loggerFor().apply { - error ("${e.message} [hint: enable trace debugging for the stack trace]") - trace (e.getStackTraceAsString()) + error("${e.message} [hint: enable trace debugging for the stack trace]") + trace(e.getStackTraceAsString()) } // prevent carpenter exceptions escaping into the world, convert things into a nice @@ -293,12 +295,12 @@ open class SerializerFactory( } else { val singleton = clazz.objectInstance() if (singleton != null) { - whitelist.requireWhitelisted(clazz) - SingletonSerializer(clazz, singleton, this) - } else { - whitelist.requireWhitelisted(type) - ObjectSerializer(type, this) - } + whitelist.requireWhitelisted(clazz) + SingletonSerializer(clazz, singleton, this) + } else { + whitelist.requireWhitelisted(type) + ObjectSerializer(type, this) + } } } } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SingletonSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SingletonSerializer.kt index 3dbbc5456f..91f068ef43 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SingletonSerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SingletonSerializer.kt @@ -10,6 +10,7 @@ package net.corda.nodeapi.internal.serialization.amqp +import net.corda.core.serialization.SerializationContext import org.apache.qpid.proton.amqp.Symbol import org.apache.qpid.proton.codec.Data import java.lang.reflect.Type @@ -33,13 +34,16 @@ class SingletonSerializer(override val type: Class<*>, val singleton: Any, facto output.writeTypeNotations(typeNotation) } - override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput, debugIndent: Int) { + override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput, + context: SerializationContext, debugIndent: Int + ) { data.withDescribed(typeNotation.descriptor) { data.putBoolean(false) } } - override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput): Any { + override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput, context: SerializationContext + ): Any { return singleton } } \ No newline at end of file diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/TransformsSchema.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/TransformsSchema.kt index 8b71bce9ba..4b2a33886e 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/TransformsSchema.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/TransformsSchema.kt @@ -224,8 +224,8 @@ data class TransformsSchema(val types: Map ?: - throw NotSerializableException("Transform schema must be encoded as a map") + val map = describedType.described as? Map<*, *> + ?: throw NotSerializableException("Transform schema must be encoded as a map") map.forEach { type -> - val fingerprint = type.key as? String ?: - throw NotSerializableException("Fingerprint must be encoded as a string") + val fingerprint = type.key as? String + ?: throw NotSerializableException("Fingerprint must be encoded as a string") rtn[fingerprint] = EnumMap>(TransformTypes::class.java) @@ -298,8 +298,8 @@ data class TransformsSchema(val types: Map).forEach { - rtn[fingerprint]!![TransformTypes.newInstance(transformType)]?.add(Transform.newInstance(it)) ?: - throw NotSerializableException("De-serialization error with transform for class " + rtn[fingerprint]!![TransformTypes.newInstance(transformType)]?.add(Transform.newInstance(it)) + ?: throw NotSerializableException("De-serialization error with transform for class " + "${type.key} ${transform.name}") } } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/ClassSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/ClassSerializer.kt index d844af4d9c..378b3580c9 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/ClassSerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/ClassSerializer.kt @@ -12,6 +12,7 @@ package net.corda.nodeapi.internal.serialization.amqp.custom import net.corda.nodeapi.internal.serialization.amqp.CustomSerializer import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory +import net.corda.nodeapi.internal.serialization.amqp.custom.ClassSerializer.ClassProxy /** * A serializer for [Class] that uses [ClassProxy] proxy object to write out diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/InputStreamSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/InputStreamSerializer.kt index e9d96784c8..7a12a2fac1 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/InputStreamSerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/InputStreamSerializer.kt @@ -10,6 +10,7 @@ package net.corda.nodeapi.internal.serialization.amqp.custom +import net.corda.core.serialization.SerializationContext import net.corda.nodeapi.internal.serialization.amqp.* import org.apache.qpid.proton.amqp.Binary import org.apache.qpid.proton.codec.Data @@ -25,7 +26,9 @@ object InputStreamSerializer : CustomSerializer.Implements(InputStr override val schemaForDocumentation = Schema(listOf(RestrictedType(type.toString(), "", listOf(type.toString()), SerializerFactory.primitiveTypeName(ByteArray::class.java)!!, descriptor, emptyList()))) - override fun writeDescribedObject(obj: InputStream, data: Data, type: Type, output: SerializationOutput) { + override fun writeDescribedObject(obj: InputStream, data: Data, type: Type, output: SerializationOutput, + context: SerializationContext + ) { val startingSize = maxOf(4096, obj.available() + 1) var buffer = ByteArray(startingSize) var pos = 0 @@ -44,8 +47,10 @@ object InputStreamSerializer : CustomSerializer.Implements(InputStr } } - override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput): InputStream { - val bits = input.readObject(obj, schemas, ByteArray::class.java) as ByteArray + override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput, + context: SerializationContext + ): InputStream { + val bits = input.readObject(obj, schemas, ByteArray::class.java, context) as ByteArray return bits.inputStream() } } \ No newline at end of file diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/MonthDaySerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/MonthDaySerializer.kt index 24094b54e2..d9fa3ce32c 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/MonthDaySerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/MonthDaySerializer.kt @@ -12,12 +12,15 @@ package net.corda.nodeapi.internal.serialization.amqp.custom import net.corda.nodeapi.internal.serialization.amqp.CustomSerializer import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory -import java.time.* +import java.time.MonthDay /** * A serializer for [MonthDay] that uses a proxy object to write out the integer form. */ -class MonthDaySerializer(factory: SerializerFactory) : CustomSerializer.Proxy(MonthDay::class.java, MonthDayProxy::class.java, factory) { +class MonthDaySerializer(factory: SerializerFactory) + : CustomSerializer.Proxy( + MonthDay::class.java, MonthDayProxy::class.java, factory +) { override fun toProxy(obj: MonthDay): MonthDayProxy = MonthDayProxy(obj.monthValue.toByte(), obj.dayOfMonth.toByte()) override fun fromProxy(proxy: MonthDayProxy): MonthDay = MonthDay.of(proxy.month.toInt(), proxy.day.toInt()) diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/OffsetDateTimeSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/OffsetDateTimeSerializer.kt index 6b1217c9f0..a8ba4ec5fb 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/OffsetDateTimeSerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/OffsetDateTimeSerializer.kt @@ -12,7 +12,9 @@ package net.corda.nodeapi.internal.serialization.amqp.custom import net.corda.nodeapi.internal.serialization.amqp.CustomSerializer import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory -import java.time.* +import java.time.LocalDateTime +import java.time.OffsetDateTime +import java.time.ZoneOffset /** * A serializer for [OffsetDateTime] that uses a proxy object to write out the date and zone offset. diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/OffsetTimeSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/OffsetTimeSerializer.kt index 7945b3f9e1..7fad041c1e 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/OffsetTimeSerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/OffsetTimeSerializer.kt @@ -12,7 +12,9 @@ package net.corda.nodeapi.internal.serialization.amqp.custom import net.corda.nodeapi.internal.serialization.amqp.CustomSerializer import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory -import java.time.* +import java.time.LocalTime +import java.time.OffsetTime +import java.time.ZoneOffset /** * A serializer for [OffsetTime] that uses a proxy object to write out the time and zone offset. diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/PeriodSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/PeriodSerializer.kt index 50478ea8e2..d67db18099 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/PeriodSerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/PeriodSerializer.kt @@ -12,7 +12,7 @@ package net.corda.nodeapi.internal.serialization.amqp.custom import net.corda.nodeapi.internal.serialization.amqp.CustomSerializer import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory -import java.time.* +import java.time.Period /** * A serializer for [Period] that uses a proxy object to write out the integer form. diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/PrivateKeySerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/PrivateKeySerializer.kt index 50469d9cd0..9cba34b0b8 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/PrivateKeySerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/PrivateKeySerializer.kt @@ -11,7 +11,9 @@ package net.corda.nodeapi.internal.serialization.amqp.custom import net.corda.core.crypto.Crypto -import net.corda.core.serialization.SerializationContext.UseCase.* +import net.corda.core.serialization.SerializationContext +import net.corda.core.serialization.SerializationContext.UseCase.Checkpoint +import net.corda.core.serialization.SerializationContext.UseCase.Storage import net.corda.nodeapi.internal.serialization.amqp.* import net.corda.nodeapi.internal.serialization.checkUseCase import org.apache.qpid.proton.codec.Data @@ -25,13 +27,17 @@ object PrivateKeySerializer : CustomSerializer.Implements(PrivateKey override val schemaForDocumentation = Schema(listOf(RestrictedType(type.toString(), "", listOf(type.toString()), SerializerFactory.primitiveTypeName(ByteArray::class.java)!!, descriptor, emptyList()))) - override fun writeDescribedObject(obj: PrivateKey, data: Data, type: Type, output: SerializationOutput) { + override fun writeDescribedObject(obj: PrivateKey, data: Data, type: Type, output: SerializationOutput, + context: SerializationContext + ) { checkUseCase(allowedUseCases) - output.writeObject(obj.encoded, data, clazz) + output.writeObject(obj.encoded, data, clazz, context) } - override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput): PrivateKey { - val bits = input.readObject(obj, schemas, ByteArray::class.java) as ByteArray + override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput, + context: SerializationContext + ): PrivateKey { + val bits = input.readObject(obj, schemas, ByteArray::class.java, context) as ByteArray return Crypto.decodePrivateKey(bits) } } \ No newline at end of file diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/PublicKeySerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/PublicKeySerializer.kt index 99b91eaf71..3339f2de17 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/PublicKeySerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/PublicKeySerializer.kt @@ -11,6 +11,7 @@ package net.corda.nodeapi.internal.serialization.amqp.custom import net.corda.core.crypto.Crypto +import net.corda.core.serialization.SerializationContext import net.corda.nodeapi.internal.serialization.amqp.* import org.apache.qpid.proton.codec.Data import java.lang.reflect.Type @@ -22,13 +23,17 @@ import java.security.PublicKey object PublicKeySerializer : CustomSerializer.Implements(PublicKey::class.java) { override val schemaForDocumentation = Schema(listOf(RestrictedType(type.toString(), "", listOf(type.toString()), SerializerFactory.primitiveTypeName(ByteArray::class.java)!!, descriptor, emptyList()))) - override fun writeDescribedObject(obj: PublicKey, data: Data, type: Type, output: SerializationOutput) { + override fun writeDescribedObject(obj: PublicKey, data: Data, type: Type, output: SerializationOutput, + context: SerializationContext + ) { // TODO: Instead of encoding to the default X509 format, we could have a custom per key type (space-efficient) serialiser. - output.writeObject(obj.encoded, data, clazz) + output.writeObject(obj.encoded, data, clazz, context) } - override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput): PublicKey { - val bits = input.readObject(obj, schemas, ByteArray::class.java) as ByteArray + override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput, + context: SerializationContext + ): PublicKey { + val bits = input.readObject(obj, schemas, ByteArray::class.java, context) as ByteArray return Crypto.decodePublicKey(bits) } } \ No newline at end of file diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/X509CRLSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/X509CRLSerializer.kt index 2b7f026d14..1c95dad698 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/X509CRLSerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/X509CRLSerializer.kt @@ -10,6 +10,7 @@ package net.corda.nodeapi.internal.serialization.amqp.custom +import net.corda.core.serialization.SerializationContext import net.corda.nodeapi.internal.crypto.X509CertificateFactory import net.corda.nodeapi.internal.serialization.amqp.* import org.apache.qpid.proton.codec.Data @@ -26,12 +27,16 @@ object X509CRLSerializer : CustomSerializer.Implements(X509CRL::class.j emptyList() ))) - override fun writeDescribedObject(obj: X509CRL, data: Data, type: Type, output: SerializationOutput) { - output.writeObject(obj.encoded, data, clazz) + override fun writeDescribedObject(obj: X509CRL, data: Data, type: Type, output: SerializationOutput, + context: SerializationContext + ) { + output.writeObject(obj.encoded, data, clazz, context) } - override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput): X509CRL { - val bytes = input.readObject(obj, schemas, ByteArray::class.java) as ByteArray + override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput, + context: SerializationContext + ): X509CRL { + val bytes = input.readObject(obj, schemas, ByteArray::class.java, context) as ByteArray return X509CertificateFactory().delegate.generateCRL(bytes.inputStream()) as X509CRL } } \ No newline at end of file diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/X509CertificateSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/X509CertificateSerializer.kt index 1ef4549915..782aa7c19f 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/X509CertificateSerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/X509CertificateSerializer.kt @@ -10,6 +10,7 @@ package net.corda.nodeapi.internal.serialization.amqp.custom +import net.corda.core.serialization.SerializationContext import net.corda.nodeapi.internal.crypto.X509CertificateFactory import net.corda.nodeapi.internal.serialization.amqp.* import org.apache.qpid.proton.codec.Data @@ -26,12 +27,16 @@ object X509CertificateSerializer : CustomSerializer.Implements( emptyList() ))) - override fun writeDescribedObject(obj: X509Certificate, data: Data, type: Type, output: SerializationOutput) { - output.writeObject(obj.encoded, data, clazz) + override fun writeDescribedObject(obj: X509Certificate, data: Data, type: Type, output: SerializationOutput, + context: SerializationContext + ) { + output.writeObject(obj.encoded, data, clazz, context) } - override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput): X509Certificate { - val bits = input.readObject(obj, schemas, ByteArray::class.java) as ByteArray + override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput, + context: SerializationContext + ): X509Certificate { + val bits = input.readObject(obj, schemas, ByteArray::class.java, context) as ByteArray return X509CertificateFactory().generateCertificate(bits.inputStream()) } } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/YearMonthSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/YearMonthSerializer.kt index a849819897..c4b9758a5e 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/YearMonthSerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/YearMonthSerializer.kt @@ -12,7 +12,7 @@ package net.corda.nodeapi.internal.serialization.amqp.custom import net.corda.nodeapi.internal.serialization.amqp.CustomSerializer import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory -import java.time.* +import java.time.YearMonth /** * A serializer for [YearMonth] that uses a proxy object to write out the integer form. diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/YearSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/YearSerializer.kt index f5bff3f10d..631432e65a 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/YearSerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/YearSerializer.kt @@ -12,7 +12,7 @@ package net.corda.nodeapi.internal.serialization.amqp.custom import net.corda.nodeapi.internal.serialization.amqp.CustomSerializer import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory -import java.time.* +import java.time.Year /** * A serializer for [Year] that uses a proxy object to write out the integer form. diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/ZonedDateTimeSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/ZonedDateTimeSerializer.kt index fa09cfa381..aaed150908 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/ZonedDateTimeSerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/ZonedDateTimeSerializer.kt @@ -12,7 +12,10 @@ package net.corda.nodeapi.internal.serialization.amqp.custom import net.corda.nodeapi.internal.serialization.amqp.CustomSerializer import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory -import java.time.* +import java.time.LocalDateTime +import java.time.ZoneId +import java.time.ZoneOffset +import java.time.ZonedDateTime /** * A serializer for [ZonedDateTime] that uses a proxy object to write out the date, time, offset and zone. diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/AMQPSchemaExtensions.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/AMQPSchemaExtensions.kt index f93ce58ff4..042784b0af 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/AMQPSchemaExtensions.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/AMQPSchemaExtensions.kt @@ -12,11 +12,11 @@ package net.corda.nodeapi.internal.serialization.carpenter +import net.corda.core.serialization.SerializationContext import net.corda.nodeapi.internal.serialization.amqp.CompositeType import net.corda.nodeapi.internal.serialization.amqp.RestrictedType import net.corda.nodeapi.internal.serialization.amqp.Field as AMQPField import net.corda.nodeapi.internal.serialization.amqp.Schema as AMQPSchema -import net.corda.core.serialization.SerializationContext fun AMQPSchema.carpenterSchema(classloader: ClassLoader): CarpenterMetaSchema { val rtn = CarpenterMetaSchema.newInstance() @@ -130,7 +130,7 @@ val typeStrToType: Map, Class> = mapOf( Pair("byte", false) to Byte::class.javaObjectType ) -fun String.stripGenerics() : String = if(this.endsWith('>')) { +fun String.stripGenerics(): String = if (this.endsWith('>')) { this.substring(0, this.indexOf('<')) } else this diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/ClassCarpenter.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/ClassCarpenter.kt index 45efb11d5c..298cd5e1e4 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/ClassCarpenter.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/ClassCarpenter.kt @@ -36,11 +36,11 @@ class CarpenterClassLoader(parentClassLoader: ClassLoader = Thread.currentThread fun load(name: String, bytes: ByteArray) = defineClass(name, bytes, 0, bytes.size) } -class InterfaceMismatchNonGetterException (val clazz: Class<*>, val method: Method) : InterfaceMismatchException( - "Requested interfaces must consist only of methods that start with 'get': ${clazz.name}.${method.name}") +class InterfaceMismatchNonGetterException(val clazz: Class<*>, val method: Method) : InterfaceMismatchException( + "Requested interfaces must consist only of methods that start with 'get': ${clazz.name}.${method.name}") -class InterfaceMismatchMissingAMQPFieldException (val clazz: Class<*>, val field: String) : InterfaceMismatchException( - "Interface ${clazz.name} requires a field named $field but that isn't found in the schema or any superclass schemas") +class InterfaceMismatchMissingAMQPFieldException(val clazz: Class<*>, val field: String) : InterfaceMismatchException( + "Interface ${clazz.name} requires a field named $field but that isn't found in the schema or any superclass schemas") /** * Which version of the java runtime are we constructing objects against diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/Exceptions.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/Exceptions.kt index 460f73197f..d64dc3017f 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/Exceptions.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/Exceptions.kt @@ -19,18 +19,18 @@ import org.objectweb.asm.Type abstract class ClassCarpenterException(msg: String) : CordaRuntimeException(msg) /** - * Thrown by the [ClassCarpenter] when trying to build + * Thrown by the [ClassCarpenter] when trying to build */ abstract class InterfaceMismatchException(msg: String) : ClassCarpenterException(msg) -class DuplicateNameException(val name : String) : ClassCarpenterException ( +class DuplicateNameException(val name: String) : ClassCarpenterException( "An attempt was made to register two classes with the name '$name' within the same ClassCarpenter namespace.") class NullablePrimitiveException(val name: String, val field: Class) : ClassCarpenterException( "Field $name is primitive type ${Type.getDescriptor(field)} and thus cannot be nullable") class UncarpentableException(name: String, field: String, type: String) : - ClassCarpenterException("Class $name is loadable yet contains field $field of unknown type $type") + ClassCarpenterException("Class $name is loadable yet contains field $field of unknown type $type") /** * A meta exception used by the [MetaCarpenter] to wrap any exceptions generated during the build diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/Kryo.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/Kryo.kt index 37905fcd15..3695c3bdca 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/Kryo.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/Kryo.kt @@ -268,7 +268,7 @@ object NotaryChangeWireTransactionSerializer : Serializer): NotaryChangeWireTransaction { - val components : List = uncheckedCast(kryo.readClassAndObject(input)) + val components: List = uncheckedCast(kryo.readClassAndObject(input)) return NotaryChangeWireTransaction(components) } } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/KryoSerializationScheme.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/KryoSerializationScheme.kt index fa9606b8ee..24c612bb1d 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/KryoSerializationScheme.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/KryoSerializationScheme.kt @@ -10,7 +10,6 @@ package net.corda.nodeapi.internal.serialization.kryo -import java.util.concurrent.ConcurrentHashMap import co.paralleluniverse.fibers.Fiber import co.paralleluniverse.io.serialization.kryo.KryoSerializer import com.esotericsoftware.kryo.Kryo @@ -22,14 +21,14 @@ import com.esotericsoftware.kryo.io.Output import com.esotericsoftware.kryo.pool.KryoPool import com.esotericsoftware.kryo.serializers.ClosureSerializer import net.corda.core.internal.uncheckedCast +import net.corda.core.serialization.ClassWhitelist +import net.corda.core.serialization.SerializationContext +import net.corda.core.serialization.SerializedBytes import net.corda.core.utilities.ByteSequence -import net.corda.core.serialization.* -import net.corda.nodeapi.internal.serialization.CordaSerializationMagic -import net.corda.nodeapi.internal.serialization.CordaClassResolver -import net.corda.nodeapi.internal.serialization.SectionId -import net.corda.nodeapi.internal.serialization.SerializationScheme import net.corda.nodeapi.internal.serialization.* +import net.corda.nodeapi.internal.serialization.SectionId import java.security.PublicKey +import java.util.concurrent.ConcurrentHashMap val kryoMagic = CordaSerializationMagic("corda".toByteArray() + byteArrayOf(0, 0)) @@ -96,7 +95,8 @@ abstract class AbstractKryoSerializationScheme : SerializationScheme { } override fun deserialize(byteSequence: ByteSequence, clazz: Class, context: SerializationContext): T { - val dataBytes = kryoMagic.consume(byteSequence) ?: throw KryoException("Serialized bytes header does not match expected format.") + val dataBytes = kryoMagic.consume(byteSequence) + ?: throw KryoException("Serialized bytes header does not match expected format.") return context.kryo { kryoInput(ByteBufferInputStream(dataBytes)) { val result: T diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/KryoStreams.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/KryoStreams.kt index c638fff0f3..27b96c6b13 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/KryoStreams.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/KryoStreams.kt @@ -13,7 +13,10 @@ package net.corda.nodeapi.internal.serialization.kryo import com.esotericsoftware.kryo.io.Input import com.esotericsoftware.kryo.io.Output import net.corda.core.internal.LazyPool -import java.io.* +import java.io.ByteArrayOutputStream +import java.io.InputStream +import java.io.OutputStream +import java.io.SequenceInputStream import java.nio.ByteBuffer class ByteBufferOutputStream(size: Int) : ByteArrayOutputStream(size) { diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/SerializeAsTokenSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/SerializeAsTokenSerializer.kt index ea4b9f7e5c..23c0c79971 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/SerializeAsTokenSerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/SerializeAsTokenSerializer.kt @@ -24,12 +24,16 @@ import net.corda.core.serialization.SerializeAsToken */ class SerializeAsTokenSerializer : Serializer() { override fun write(kryo: Kryo, output: Output, obj: T) { - kryo.writeClassAndObject(output, obj.toToken(kryo.serializationContext() ?: throw KryoException("Attempt to write a ${SerializeAsToken::class.simpleName} instance of ${obj.javaClass.name} without initialising a context"))) + kryo.writeClassAndObject(output, obj.toToken(kryo.serializationContext() + ?: throw KryoException("Attempt to write a ${SerializeAsToken::class.simpleName} instance of ${obj.javaClass.name} without initialising a context"))) } override fun read(kryo: Kryo, input: Input, type: Class): T { - val token = (kryo.readClassAndObject(input) as? SerializationToken) ?: throw KryoException("Non-token read for tokenized type: ${type.name}") - val fromToken = token.fromToken(kryo.serializationContext() ?: throw KryoException("Attempt to read a token for a ${SerializeAsToken::class.simpleName} instance of ${type.name} without initialising a context")) - return type.castIfPossible(fromToken) ?: throw KryoException("Token read ($token) did not return expected tokenized type: ${type.name}") + val token = (kryo.readClassAndObject(input) as? SerializationToken) + ?: throw KryoException("Non-token read for tokenized type: ${type.name}") + val fromToken = token.fromToken(kryo.serializationContext() + ?: throw KryoException("Attempt to read a token for a ${SerializeAsToken::class.simpleName} instance of ${type.name} without initialising a context")) + return type.castIfPossible(fromToken) + ?: throw KryoException("Token read ($token) did not return expected tokenized type: ${type.name}") } } \ No newline at end of file diff --git a/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/ErrorMessageTests.java b/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/ErrorMessageTests.java index 035b5f8b24..c7932721ee 100644 --- a/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/ErrorMessageTests.java +++ b/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/ErrorMessageTests.java @@ -11,6 +11,8 @@ package net.corda.nodeapi.internal.serialization.amqp; import net.corda.nodeapi.internal.serialization.AllWhitelist; +import net.corda.nodeapi.internal.serialization.amqp.testutils.TestSerializationContext; +import net.corda.nodeapi.internal.serialization.amqp.testutils.TestSerializationContextKt; import org.assertj.core.api.Assertions; import org.junit.Ignore; import org.junit.Test; @@ -51,7 +53,7 @@ public class ErrorMessageTests { SerializationOutput ser = new SerializationOutput(factory1); - Assertions.assertThatThrownBy(() -> ser.serialize(new C(1))) + Assertions.assertThatThrownBy(() -> ser.serialize(new C(1), TestSerializationContext.testSerializationContext)) .isInstanceOf(NotSerializableException.class) .hasMessage(errMsg("a", getClass().getName())); } diff --git a/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/JavaGenericsTest.java b/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/JavaGenericsTest.java index f957ae3147..1bdaabdd78 100644 --- a/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/JavaGenericsTest.java +++ b/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/JavaGenericsTest.java @@ -12,6 +12,7 @@ package net.corda.nodeapi.internal.serialization.amqp; import net.corda.core.serialization.SerializedBytes; import net.corda.nodeapi.internal.serialization.AllWhitelist; +import net.corda.nodeapi.internal.serialization.amqp.testutils.TestSerializationContext; import org.junit.Test; import java.io.NotSerializableException; @@ -43,10 +44,10 @@ public class JavaGenericsTest { new SerializerFingerPrinter()); SerializationOutput ser = new SerializationOutput(factory); - SerializedBytes bytes = ser.serialize(a1); + SerializedBytes bytes = ser.serialize(a1, TestSerializationContext.testSerializationContext); DeserializationInput des = new DeserializationInput(factory); - A a2 = des.deserialize(bytes, A.class); + A a2 = des.deserialize(bytes, A.class, TestSerializationContext.testSerializationContext); assertEquals(1, a2.getT()); } @@ -58,13 +59,13 @@ public class JavaGenericsTest { new EvolutionSerializerGetter(), new SerializerFingerPrinter()); - return (new SerializationOutput(factory)).serialize(a); + return (new SerializationOutput(factory)).serialize(a, TestSerializationContext.testSerializationContext); } private SerializedBytes forceWildcardSerializeFactory( A a, SerializerFactory factory) throws NotSerializableException { - return (new SerializationOutput(factory)).serialize(a); + return (new SerializationOutput(factory)).serialize(a, TestSerializationContext.testSerializationContext); } private A forceWildcardDeserialize(SerializedBytes bytes) throws NotSerializableException { @@ -75,13 +76,14 @@ public class JavaGenericsTest { new SerializerFingerPrinter()); DeserializationInput des = new DeserializationInput(factory); - return des.deserialize(bytes, A.class); + return des.deserialize(bytes, A.class, TestSerializationContext.testSerializationContext); } private A forceWildcardDeserializeFactory( SerializedBytes bytes, SerializerFactory factory) throws NotSerializableException { - return (new DeserializationInput(factory)).deserialize(bytes, A.class); + return (new DeserializationInput(factory)).deserialize(bytes, A.class, + TestSerializationContext.testSerializationContext); } @Test diff --git a/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/JavaNestedClassesTests.java b/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/JavaNestedClassesTests.java index 66cd995220..80621a0a99 100644 --- a/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/JavaNestedClassesTests.java +++ b/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/JavaNestedClassesTests.java @@ -6,6 +6,7 @@ import net.corda.core.contracts.ContractState; import net.corda.core.identity.AbstractParty; import net.corda.core.serialization.SerializedBytes; import net.corda.nodeapi.internal.serialization.AllWhitelist; +import net.corda.nodeapi.internal.serialization.amqp.testutils.TestSerializationContext; import org.assertj.core.api.Assertions; import org.jetbrains.annotations.NotNull; import org.junit.Test; @@ -39,17 +40,17 @@ class OuterClass1 { } public void run() throws NotSerializableException { - SerializedBytes b = ser.serialize(new DummyState()); - desExisting.deserialize(b, DummyState.class); - desRegen.deserialize(b, DummyState.class); + SerializedBytes b = ser.serialize(new DummyState(), TestSerializationContext.testSerializationContext); + desExisting.deserialize(b, DummyState.class, TestSerializationContext.testSerializationContext); + desRegen.deserialize(b, DummyState.class, TestSerializationContext.testSerializationContext); } } class Inherator1 extends OuterClass1 { public void iRun() throws NotSerializableException { - SerializedBytes b = ser.serialize(new DummyState()); - desExisting.deserialize(b, DummyState.class); - desRegen.deserialize(b, DummyState.class); + SerializedBytes b = ser.serialize(new DummyState(), TestSerializationContext.testSerializationContext); + desExisting.deserialize(b, DummyState.class, TestSerializationContext.testSerializationContext); + desRegen.deserialize(b, DummyState.class, TestSerializationContext.testSerializationContext); } } @@ -85,17 +86,17 @@ class OuterClass2 { } public void run() throws NotSerializableException { - SerializedBytes b = ser.serialize(new DummyState(12)); - desExisting.deserialize(b, DummyState.class); - desRegen.deserialize(b, DummyState.class); + SerializedBytes b = ser.serialize(new DummyState(12), TestSerializationContext.testSerializationContext); + desExisting.deserialize(b, DummyState.class, TestSerializationContext.testSerializationContext); + desRegen.deserialize(b, DummyState.class, TestSerializationContext.testSerializationContext); } } class Inherator2 extends OuterClass2 { public void iRun() throws NotSerializableException { - SerializedBytes b = ser.serialize(new DummyState(12)); - desExisting.deserialize(b, DummyState.class); - desRegen.deserialize(b, DummyState.class); + SerializedBytes b = ser.serialize(new DummyState(12), TestSerializationContext.testSerializationContext); + desExisting.deserialize(b, DummyState.class, TestSerializationContext.testSerializationContext); + desRegen.deserialize(b, DummyState.class, TestSerializationContext.testSerializationContext); } } @@ -120,7 +121,7 @@ abstract class AbstractClass2 { class Inherator4 extends AbstractClass2 { public void run() throws NotSerializableException { - ser.serialize(new DummyState()); + ser.serialize(new DummyState(), TestSerializationContext.testSerializationContext); } } @@ -139,7 +140,7 @@ class Inherator5 extends AbstractClass3 { new SerializerFingerPrinter()); SerializationOutput ser = new SerializationOutput(factory); - ser.serialize(new DummyState()); + ser.serialize(new DummyState(), TestSerializationContext.testSerializationContext); } } @@ -159,7 +160,7 @@ class Inherator6 extends AbstractClass3 { new SerializerFingerPrinter()); SerializationOutput ser = new SerializationOutput(factory); - ser.serialize(new Wrapper(new DummyState())); + ser.serialize(new Wrapper(new DummyState()), TestSerializationContext.testSerializationContext); } } diff --git a/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/JavaNestedInheritenceTests.java b/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/JavaNestedInheritenceTests.java index 548f3625a3..8097fe9530 100644 --- a/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/JavaNestedInheritenceTests.java +++ b/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/JavaNestedInheritenceTests.java @@ -4,6 +4,7 @@ import com.google.common.collect.ImmutableList; import net.corda.core.contracts.ContractState; import net.corda.core.identity.AbstractParty; import net.corda.nodeapi.internal.serialization.AllWhitelist; +import net.corda.nodeapi.internal.serialization.amqp.testutils.TestSerializationContext; import org.assertj.core.api.Assertions; import org.jetbrains.annotations.NotNull; import org.junit.Test; @@ -38,7 +39,7 @@ public class JavaNestedInheritenceTests extends JavaNestedInheritenceTestsBase { SerializationOutput ser = new SerializationOutput(factory); - Assertions.assertThatThrownBy(() -> ser.serialize(new DummyState())).isInstanceOf( + Assertions.assertThatThrownBy(() -> ser.serialize(new DummyState(), TestSerializationContext.testSerializationContext)).isInstanceOf( NotSerializableException.class).hasMessageContaining( "has synthetic fields and is likely a nested inner class"); } @@ -50,7 +51,7 @@ public class JavaNestedInheritenceTests extends JavaNestedInheritenceTestsBase { new SerializerFingerPrinter()); SerializationOutput ser = new SerializationOutput(factory); - Assertions.assertThatThrownBy(() -> ser.serialize(new Wrapper (new DummyState()))).isInstanceOf( + Assertions.assertThatThrownBy(() -> ser.serialize(new Wrapper (new DummyState()), TestSerializationContext.testSerializationContext)).isInstanceOf( NotSerializableException.class).hasMessageContaining( "has synthetic fields and is likely a nested inner class"); } @@ -63,7 +64,7 @@ public class JavaNestedInheritenceTests extends JavaNestedInheritenceTestsBase { SerializationOutput ser = new SerializationOutput(factory1); - Assertions.assertThatThrownBy(() -> ser.serialize(new TemplateWrapper (new DummyState()))).isInstanceOf( + Assertions.assertThatThrownBy(() -> ser.serialize(new TemplateWrapper (new DummyState()), TestSerializationContext.testSerializationContext)).isInstanceOf( NotSerializableException.class).hasMessageContaining( "has synthetic fields and is likely a nested inner class"); } diff --git a/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/JavaPrivatePropertyTests.java b/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/JavaPrivatePropertyTests.java index e11227b9ed..fbc86a1f89 100644 --- a/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/JavaPrivatePropertyTests.java +++ b/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/JavaPrivatePropertyTests.java @@ -11,6 +11,7 @@ package net.corda.nodeapi.internal.serialization.amqp; import net.corda.nodeapi.internal.serialization.AllWhitelist; +import net.corda.nodeapi.internal.serialization.amqp.testutils.TestSerializationContext; import org.junit.Test; import static org.junit.Assert.*; @@ -93,7 +94,7 @@ public class JavaPrivatePropertyTests { DeserializationInput des = new DeserializationInput(factory); B b = new B(true); - B b2 = des.deserialize(ser.serialize(b), B.class); + B b2 = des.deserialize(ser.serialize(b, TestSerializationContext.testSerializationContext), B.class, TestSerializationContext.testSerializationContext); assertEquals (b.b, b2.b); } @@ -108,7 +109,7 @@ public class JavaPrivatePropertyTests { B2 b = new B2(); b.setB(false); - B2 b2 = des.deserialize(ser.serialize(b), B2.class); + B2 b2 = des.deserialize(ser.serialize(b, TestSerializationContext.testSerializationContext), B2.class, TestSerializationContext.testSerializationContext); assertEquals (b.b, b2.b); } @@ -122,7 +123,7 @@ public class JavaPrivatePropertyTests { B3 b = new B3(); b.setB(false); - B3 b2 = des.deserialize(ser.serialize(b), B3.class); + B3 b2 = des.deserialize(ser.serialize(b, TestSerializationContext.testSerializationContext), B3.class, TestSerializationContext.testSerializationContext); // since we can't find a getter for b (isb != isB) then we won't serialize that parameter assertEquals (null, b2.b); @@ -138,7 +139,7 @@ public class JavaPrivatePropertyTests { C3 c = new C3(); c.setA(12345); - C3 c2 = des.deserialize(ser.serialize(c), C3.class); + C3 c2 = des.deserialize(ser.serialize(c, TestSerializationContext.testSerializationContext), C3.class, TestSerializationContext.testSerializationContext); assertEquals (c.a, c2.a); } @@ -153,7 +154,7 @@ public class JavaPrivatePropertyTests { DeserializationInput des = new DeserializationInput(factory); C c = new C("dripping taps"); - C c2 = des.deserialize(ser.serialize(c), C.class); + C c2 = des.deserialize(ser.serialize(c, TestSerializationContext.testSerializationContext), C.class, TestSerializationContext.testSerializationContext); assertEquals (c.a, c2.a); @@ -185,7 +186,7 @@ public class JavaPrivatePropertyTests { DeserializationInput des = new DeserializationInput(factory); C2 c = new C2("dripping taps"); - C2 c2 = des.deserialize(ser.serialize(c), C2.class); + C2 c2 = des.deserialize(ser.serialize(c, TestSerializationContext.testSerializationContext), C2.class, TestSerializationContext.testSerializationContext); assertEquals (c.a, c2.a); diff --git a/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/JavaSerialiseEnumTests.java b/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/JavaSerialiseEnumTests.java index bdc59a8eb0..a263406d81 100644 --- a/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/JavaSerialiseEnumTests.java +++ b/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/JavaSerialiseEnumTests.java @@ -10,6 +10,7 @@ package net.corda.nodeapi.internal.serialization.amqp; +import net.corda.nodeapi.internal.serialization.amqp.testutils.TestSerializationContext; import org.junit.Test; import net.corda.nodeapi.internal.serialization.AllWhitelist; @@ -43,6 +44,6 @@ public class JavaSerialiseEnumTests { new EvolutionSerializerGetter(), new SerializerFingerPrinter()); SerializationOutput ser = new SerializationOutput(factory1); - SerializedBytes bytes = ser.serialize(bra); + SerializedBytes bytes = ser.serialize(bra, TestSerializationContext.testSerializationContext); } } diff --git a/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/JavaSerializationOutputTests.java b/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/JavaSerializationOutputTests.java index 99176eefdb..153d4fb5c8 100644 --- a/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/JavaSerializationOutputTests.java +++ b/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/JavaSerializationOutputTests.java @@ -16,6 +16,7 @@ import net.corda.core.identity.AbstractParty; import net.corda.core.serialization.ConstructorForDeserialization; import net.corda.nodeapi.internal.serialization.AllWhitelist; import net.corda.core.serialization.SerializedBytes; +import net.corda.nodeapi.internal.serialization.amqp.testutils.TestSerializationContext; import org.apache.qpid.proton.codec.DecoderImpl; import org.apache.qpid.proton.codec.EncoderImpl; import org.jetbrains.annotations.NotNull; @@ -199,7 +200,7 @@ public class JavaSerializationOutputTests { evolutionSerialiserGetter, fingerPrinter); SerializationOutput ser = new SerializationOutput(factory1); - SerializedBytes bytes = ser.serialize(obj); + SerializedBytes bytes = ser.serialize(obj, TestSerializationContext.testSerializationContext); DecoderImpl decoder = new DecoderImpl(); @@ -219,13 +220,15 @@ public class JavaSerializationOutputTests { assertTrue(result != null); DeserializationInput des = new DeserializationInput(factory2); - Object desObj = des.deserialize(bytes, Object.class); + Object desObj = des.deserialize(bytes, Object.class, TestSerializationContext.testSerializationContext); assertTrue(Objects.deepEquals(obj, desObj)); // Now repeat with a re-used factory SerializationOutput ser2 = new SerializationOutput(factory1); DeserializationInput des2 = new DeserializationInput(factory1); - Object desObj2 = des2.deserialize(ser2.serialize(obj), Object.class); + Object desObj2 = des2.deserialize(ser2.serialize(obj, TestSerializationContext.testSerializationContext), + Object.class, TestSerializationContext.testSerializationContext); + assertTrue(Objects.deepEquals(obj, desObj2)); // TODO: check schema is as expected return desObj2; diff --git a/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/ListsSerializationJavaTest.java b/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/ListsSerializationJavaTest.java index 25ba853d26..d865cff4c4 100644 --- a/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/ListsSerializationJavaTest.java +++ b/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/ListsSerializationJavaTest.java @@ -13,6 +13,7 @@ package net.corda.nodeapi.internal.serialization.amqp; import net.corda.core.serialization.CordaSerializable; import net.corda.core.serialization.SerializedBytes; import net.corda.nodeapi.internal.serialization.AllWhitelist; +import net.corda.nodeapi.internal.serialization.amqp.testutils.TestSerializationContext; import org.junit.Assert; import org.junit.Test; @@ -142,9 +143,9 @@ public class ListsSerializationJavaTest { evolutionSerializerGetter, fingerPrinter); SerializationOutput ser = new SerializationOutput(factory1); - SerializedBytes bytes = ser.serialize(container); + SerializedBytes bytes = ser.serialize(container, TestSerializationContext.testSerializationContext); DeserializationInput des = new DeserializationInput(factory1); - T deserialized = des.deserialize(bytes, clazz); + T deserialized = des.deserialize(bytes, clazz, TestSerializationContext.testSerializationContext); Assert.assertEquals(container, deserialized); } } \ No newline at end of file diff --git a/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/SetterConstructorTests.java b/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/SetterConstructorTests.java index 2c4f9dd9aa..52182b9851 100644 --- a/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/SetterConstructorTests.java +++ b/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/SetterConstructorTests.java @@ -12,6 +12,7 @@ package net.corda.nodeapi.internal.serialization.amqp; import net.corda.core.serialization.SerializedBytes; import net.corda.nodeapi.internal.serialization.AllWhitelist; +import net.corda.nodeapi.internal.serialization.amqp.testutils.TestSerializationContext; import org.assertj.core.api.Assertions; import org.junit.Test; import static org.junit.Assert.*; @@ -142,7 +143,7 @@ public class SetterConstructorTests { c1.setA(1); c1.setB(2); c1.setC(3); - Schema schemas = ser.serializeAndReturnSchema(c1).component2(); + Schema schemas = ser.serializeAndReturnSchema(c1, TestSerializationContext.testSerializationContext).component2(); assertEquals(1, schemas.component1().size()); assertEquals(this.getClass().getName() + "$C", schemas.component1().get(0).getName()); @@ -157,7 +158,7 @@ public class SetterConstructorTests { C2 c2 = new C2(); c2.setA(1); c2.setB(2); - schemas = ser.serializeAndReturnSchema(c2).component2(); + schemas = ser.serializeAndReturnSchema(c2, TestSerializationContext.testSerializationContext).component2(); assertEquals(1, schemas.component1().size()); assertEquals(this.getClass().getName() + "$C2", schemas.component1().get(0).getName()); @@ -174,7 +175,7 @@ public class SetterConstructorTests { c3.setA(1); c3.setB(2); c3.setC(3); - schemas = ser.serializeAndReturnSchema(c3).component2(); + schemas = ser.serializeAndReturnSchema(c3, TestSerializationContext.testSerializationContext).component2(); assertEquals(1, schemas.component1().size()); assertEquals(this.getClass().getName() + "$C3", schemas.component1().get(0).getName()); @@ -190,7 +191,7 @@ public class SetterConstructorTests { c4.setA(1); c4.setB(2); c4.setC(3); - schemas = ser.serializeAndReturnSchema(c4).component2(); + schemas = ser.serializeAndReturnSchema(c4, TestSerializationContext.testSerializationContext).component2(); assertEquals(1, schemas.component1().size()); assertEquals(this.getClass().getName() + "$C4", schemas.component1().get(0).getName()); @@ -222,9 +223,9 @@ public class SetterConstructorTests { cPre1.setB(b); cPre1.setC(c); - SerializedBytes bytes = new SerializationOutput(factory1).serialize(cPre1); + SerializedBytes bytes = new SerializationOutput(factory1).serialize(cPre1, TestSerializationContext.testSerializationContext); - C cPost1 = new DeserializationInput(factory1).deserialize(bytes, C.class); + C cPost1 = new DeserializationInput(factory1).deserialize(bytes, C.class, TestSerializationContext.testSerializationContext); assertEquals(a, cPost1.a); assertEquals(b, cPost1.b); @@ -234,8 +235,8 @@ public class SetterConstructorTests { cPre2.setA(1); cPre2.setB(2); - C2 cPost2 = new DeserializationInput(factory1).deserialize(new SerializationOutput(factory1).serialize(cPre2), - C2.class); + C2 cPost2 = new DeserializationInput(factory1).deserialize(new SerializationOutput(factory1).serialize(cPre2, TestSerializationContext.testSerializationContext), + C2.class, TestSerializationContext.testSerializationContext); assertEquals(a, cPost2.a); assertEquals(b, cPost2.b); @@ -249,8 +250,9 @@ public class SetterConstructorTests { cPre3.setB(2); cPre3.setC(3); - C3 cPost3 = new DeserializationInput(factory1).deserialize(new SerializationOutput(factory1).serialize(cPre3), - C3.class); + C3 cPost3 = new DeserializationInput(factory1).deserialize( + new SerializationOutput(factory1).serialize(cPre3, TestSerializationContext.testSerializationContext), + C3.class, TestSerializationContext.testSerializationContext); assertEquals(a, cPost3.a); @@ -263,8 +265,11 @@ public class SetterConstructorTests { cPre4.setB(2); cPre4.setC(3); - C4 cPost4 = new DeserializationInput(factory1).deserialize(new SerializationOutput(factory1).serialize(cPre4), - C4.class); + C4 cPost4 = new DeserializationInput(factory1).deserialize( + new SerializationOutput(factory1).serialize(cPre4, + TestSerializationContext.testSerializationContext), + C4.class, + TestSerializationContext.testSerializationContext); assertEquals(0, cPost4.a); assertEquals(0, cPost4.b); @@ -290,8 +295,10 @@ public class SetterConstructorTests { o.setB("World"); o.setC(i2); - Outer post = new DeserializationInput(factory1).deserialize(new SerializationOutput(factory1).serialize(o), - Outer.class); + Outer post = new DeserializationInput(factory1).deserialize( + new SerializationOutput(factory1).serialize( + o, TestSerializationContext.testSerializationContext), + Outer.class, TestSerializationContext.testSerializationContext); assertEquals("Hello", post.a.a); assertEquals("World", post.b); @@ -300,7 +307,7 @@ public class SetterConstructorTests { } @Test - public void typeMistmatch() throws NotSerializableException { + public void typeMistmatch() { EvolutionSerializerGetterBase evolutionSerialiserGetter = new EvolutionSerializerGetter(); FingerPrinter fingerPrinter = new SerializerFingerPrinter(); SerializerFactory factory1 = new SerializerFactory( @@ -313,12 +320,13 @@ public class SetterConstructorTests { tm.setA(10); assertEquals("10", tm.getA()); - Assertions.assertThatThrownBy(() -> new SerializationOutput(factory1).serialize(tm)).isInstanceOf ( + Assertions.assertThatThrownBy(() -> new SerializationOutput(factory1).serialize(tm, + TestSerializationContext.testSerializationContext)).isInstanceOf ( NotSerializableException.class); } @Test - public void typeMistmatch2() throws NotSerializableException { + public void typeMistmatch2() { EvolutionSerializerGetterBase evolutionSerialiserGetter = new EvolutionSerializerGetter(); FingerPrinter fingerPrinter = new SerializerFingerPrinter(); SerializerFactory factory1 = new SerializerFactory( @@ -331,7 +339,8 @@ public class SetterConstructorTests { tm.setA("10"); assertEquals((Integer)10, tm.getA()); - Assertions.assertThatThrownBy(() -> new SerializationOutput(factory1).serialize(tm)).isInstanceOf( + Assertions.assertThatThrownBy(() -> new SerializationOutput(factory1).serialize(tm, + TestSerializationContext.testSerializationContext)).isInstanceOf( NotSerializableException.class); } @@ -357,6 +366,10 @@ public class SetterConstructorTests { // if we've got super / sub types on the setter vs the underlying type the wrong way around this will // explode. See CORDA-1229 (https://r3-cev.atlassian.net/browse/CORDA-1229) - new DeserializationInput(factory1).deserialize(new SerializationOutput(factory1).serialize(cil), CIntList.class); + new DeserializationInput(factory1).deserialize( + new SerializationOutput(factory1).serialize( + cil, TestSerializationContext.testSerializationContext), + CIntList.class, + TestSerializationContext.testSerializationContext); } } diff --git a/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/testutils/TestSerializationContext.java b/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/testutils/TestSerializationContext.java new file mode 100644 index 0000000000..126ff34f71 --- /dev/null +++ b/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/testutils/TestSerializationContext.java @@ -0,0 +1,24 @@ +package net.corda.nodeapi.internal.serialization.amqp.testutils; + +import net.corda.core.serialization.SerializationContext; +import net.corda.core.utilities.ByteSequence; +import net.corda.nodeapi.internal.serialization.AllWhitelist; +import net.corda.nodeapi.internal.serialization.SerializationContextImpl; + + +import java.util.HashMap; +import java.util.Map; + +public class TestSerializationContext { + + static private Map serializationProperties = new HashMap(); + + public static SerializationContext testSerializationContext = new SerializationContextImpl( + ByteSequence.of(new byte[] { 'c', 'o', 'r', 'd', 'a', (byte)0, (byte)0, (byte)1}), + ClassLoader.getSystemClassLoader(), + AllWhitelist.INSTANCE, + serializationProperties, + false, + SerializationContext.UseCase.Testing, + null); +} diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/CorDappSerializerTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/CorDappSerializerTests.kt index 20898f309e..9ffe50eaa4 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/CorDappSerializerTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/CorDappSerializerTests.kt @@ -13,9 +13,15 @@ package net.corda.nodeapi.internal.serialization.amqp import org.junit.Test import net.corda.core.serialization.ClassWhitelist import net.corda.core.serialization.SerializationCustomSerializer +import net.corda.nodeapi.internal.serialization.amqp.testutils.testDefaultFactory import org.assertj.core.api.Assertions import java.io.NotSerializableException import kotlin.test.assertEquals +import net.corda.nodeapi.internal.serialization.amqp.testutils.serializeAndReturnSchema +import net.corda.nodeapi.internal.serialization.amqp.testutils.serialize +import net.corda.nodeapi.internal.serialization.amqp.testutils.deserializeAndReturnEnvelope +import net.corda.nodeapi.internal.serialization.amqp.testutils.deserialize + class CorDappSerializerTests { data class NeedsProxy (val a: String) diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializeAndReturnEnvelopeTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializeAndReturnEnvelopeTests.kt index 57e7c26dea..36b266b5de 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializeAndReturnEnvelopeTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializeAndReturnEnvelopeTests.kt @@ -11,10 +11,17 @@ package net.corda.nodeapi.internal.serialization.amqp import net.corda.core.serialization.CordaSerializable +import net.corda.nodeapi.internal.serialization.amqp.testutils.testDefaultFactoryNoEvolution +import net.corda.nodeapi.internal.serialization.amqp.testutils.testDefaultFactoryWithWhitelist +import net.corda.nodeapi.internal.serialization.amqp.testutils.testName import org.junit.Test import kotlin.test.assertEquals import kotlin.test.assertNotEquals import kotlin.test.assertTrue +import net.corda.nodeapi.internal.serialization.amqp.testutils.serializeAndReturnSchema +import net.corda.nodeapi.internal.serialization.amqp.testutils.serialize +import net.corda.nodeapi.internal.serialization.amqp.testutils.deserializeAndReturnEnvelope +import net.corda.nodeapi.internal.serialization.amqp.testutils.deserialize class DeserializeAndReturnEnvelopeTests { // the 'this' reference means we can't just move this to the common test utils diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializeMapTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializeMapTests.kt index c17ffe468c..2e9f1a1c1a 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializeMapTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializeMapTests.kt @@ -10,9 +10,15 @@ package net.corda.nodeapi.internal.serialization.amqp +import net.corda.nodeapi.internal.serialization.amqp.testutils.TestSerializationOutput +import net.corda.nodeapi.internal.serialization.amqp.testutils.testDefaultFactoryNoEvolution import org.assertj.core.api.Assertions import org.junit.Test import java.util.* +import net.corda.nodeapi.internal.serialization.amqp.testutils.serializeAndReturnSchema +import net.corda.nodeapi.internal.serialization.amqp.testutils.serialize +import net.corda.nodeapi.internal.serialization.amqp.testutils.deserializeAndReturnEnvelope +import net.corda.nodeapi.internal.serialization.amqp.testutils.deserialize class DeserializeMapTests { companion object { diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializeNeedingCarpentryOfEnumsTest.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializeNeedingCarpentryOfEnumsTest.kt index a78bc414b1..42272c1655 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializeNeedingCarpentryOfEnumsTest.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializeNeedingCarpentryOfEnumsTest.kt @@ -11,9 +11,16 @@ package net.corda.nodeapi.internal.serialization.amqp import net.corda.nodeapi.internal.serialization.AllWhitelist +import net.corda.nodeapi.internal.serialization.amqp.testutils.TestSerializationOutput +import net.corda.nodeapi.internal.serialization.amqp.testutils.testDefaultFactoryNoEvolution +import net.corda.nodeapi.internal.serialization.amqp.testutils.testDefaultFactoryWithWhitelist import org.junit.Test import kotlin.test.* import net.corda.nodeapi.internal.serialization.carpenter.* +import net.corda.nodeapi.internal.serialization.amqp.testutils.serializeAndReturnSchema +import net.corda.nodeapi.internal.serialization.amqp.testutils.serialize +import net.corda.nodeapi.internal.serialization.amqp.testutils.deserializeAndReturnEnvelope +import net.corda.nodeapi.internal.serialization.amqp.testutils.deserialize class DeserializeNeedingCarpentryOfEnumsTest : AmqpCarpenterBase(AllWhitelist) { companion object { diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializeNeedingCarpentrySimpleTypesTest.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializeNeedingCarpentrySimpleTypesTest.kt index af0c6edba3..bb88b16faf 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializeNeedingCarpentrySimpleTypesTest.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializeNeedingCarpentrySimpleTypesTest.kt @@ -11,9 +11,15 @@ package net.corda.nodeapi.internal.serialization.amqp import net.corda.nodeapi.internal.serialization.AllWhitelist +import net.corda.nodeapi.internal.serialization.amqp.testutils.TestSerializationOutput +import net.corda.nodeapi.internal.serialization.amqp.testutils.testDefaultFactoryNoEvolution import org.junit.Test import kotlin.test.* import net.corda.nodeapi.internal.serialization.carpenter.* +import net.corda.nodeapi.internal.serialization.amqp.testutils.serializeAndReturnSchema +import net.corda.nodeapi.internal.serialization.amqp.testutils.serialize +import net.corda.nodeapi.internal.serialization.amqp.testutils.deserializeAndReturnEnvelope +import net.corda.nodeapi.internal.serialization.amqp.testutils.deserialize // These tests work by having the class carpenter build the classes we serialise and then deserialise. Because // those classes don't exist within the system's Class Loader the deserialiser will be forced to carpent diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializeNeedingCarpentryTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializeNeedingCarpentryTests.kt index 1e9d5b5d53..b2a88a7ecf 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializeNeedingCarpentryTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializeNeedingCarpentryTests.kt @@ -15,6 +15,11 @@ import org.junit.Test import kotlin.test.* import net.corda.nodeapi.internal.serialization.carpenter.* import net.corda.nodeapi.internal.serialization.AllWhitelist +import net.corda.nodeapi.internal.serialization.amqp.testutils.TestSerializationOutput +import net.corda.nodeapi.internal.serialization.amqp.testutils.testDefaultFactoryNoEvolution +import net.corda.nodeapi.internal.serialization.amqp.testutils.testDefaultFactoryWithWhitelist +import net.corda.nodeapi.internal.serialization.amqp.testutils.serialize +import net.corda.nodeapi.internal.serialization.amqp.testutils.deserialize @CordaSerializable interface I { diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializeSimpleTypesTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializeSimpleTypesTests.kt index 21f93fc9f9..86a1ccd93d 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializeSimpleTypesTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializeSimpleTypesTests.kt @@ -10,8 +10,12 @@ package net.corda.nodeapi.internal.serialization.amqp +import net.corda.nodeapi.internal.serialization.amqp.testutils.TestSerializationOutput +import net.corda.nodeapi.internal.serialization.amqp.testutils.testDefaultFactoryNoEvolution import org.junit.Test import kotlin.test.assertEquals +import net.corda.nodeapi.internal.serialization.amqp.testutils.serialize +import net.corda.nodeapi.internal.serialization.amqp.testutils.deserialize // Prior to certain fixes being made within the [PropertySerializaer] classes these simple // deserialization operations would've blown up with type mismatch errors where the deserlized diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/EnumEvolvabilityTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/EnumEvolvabilityTests.kt index f547b546ac..c9713d952a 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/EnumEvolvabilityTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/EnumEvolvabilityTests.kt @@ -11,6 +11,8 @@ package net.corda.nodeapi.internal.serialization.amqp import net.corda.core.serialization.* +import net.corda.nodeapi.internal.serialization.amqp.testutils.TestSerializationOutput +import net.corda.nodeapi.internal.serialization.amqp.testutils.testDefaultFactory import net.corda.testing.common.internal.ProjectStructure.projectRootDir import org.assertj.core.api.Assertions import org.junit.Test @@ -20,6 +22,9 @@ import java.util.* import java.util.concurrent.ConcurrentHashMap import kotlin.test.assertEquals import kotlin.test.assertTrue +import net.corda.nodeapi.internal.serialization.amqp.testutils.serializeAndReturnSchema +import net.corda.nodeapi.internal.serialization.amqp.testutils.serialize +import net.corda.nodeapi.internal.serialization.amqp.testutils.deserializeAndReturnEnvelope class EnumEvolvabilityTests { @Suppress("UNUSED") diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.kt index efbec4bbf8..cb28a4e19a 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.kt @@ -14,6 +14,8 @@ import net.corda.core.internal.toPath import net.corda.core.serialization.CordaSerializationTransformEnumDefault import net.corda.core.serialization.CordaSerializationTransformEnumDefaults import net.corda.core.serialization.SerializedBytes +import net.corda.nodeapi.internal.serialization.amqp.testutils.testDefaultFactory +import net.corda.nodeapi.internal.serialization.amqp.testutils.testName import net.corda.testing.common.internal.ProjectStructure.projectRootDir import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatThrownBy @@ -22,6 +24,8 @@ import java.io.NotSerializableException import java.net.URI import kotlin.test.assertEquals import kotlin.test.assertNotNull +import net.corda.nodeapi.internal.serialization.amqp.testutils.serialize +import net.corda.nodeapi.internal.serialization.amqp.testutils.deserialize // NOTE: To recreate the test files used by these tests uncomment the original test classes and comment // the new ones out, then change each test to write out the serialized bytes rather than read diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/EnumTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/EnumTests.kt index 3e80e31d9b..978507bfa0 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/EnumTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/EnumTests.kt @@ -13,12 +13,19 @@ package net.corda.nodeapi.internal.serialization.amqp import net.corda.core.serialization.ClassWhitelist import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.SerializedBytes +import net.corda.nodeapi.internal.serialization.amqp.testutils.TestSerializationOutput +import net.corda.nodeapi.internal.serialization.amqp.testutils.testDefaultFactoryNoEvolution +import net.corda.nodeapi.internal.serialization.amqp.testutils.testName import org.assertj.core.api.Assertions import org.junit.Test import java.io.NotSerializableException import java.time.DayOfWeek import kotlin.test.assertEquals import kotlin.test.assertNotNull +import net.corda.nodeapi.internal.serialization.amqp.testutils.serializeAndReturnSchema +import net.corda.nodeapi.internal.serialization.amqp.testutils.serialize +import net.corda.nodeapi.internal.serialization.amqp.testutils.deserializeAndReturnEnvelope +import net.corda.nodeapi.internal.serialization.amqp.testutils.deserialize class EnumTests { enum class Bras { diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/ErrorMessagesTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/ErrorMessagesTests.kt index 02f694782f..4e84fae705 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/ErrorMessagesTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/ErrorMessagesTests.kt @@ -10,10 +10,17 @@ package net.corda.nodeapi.internal.serialization.amqp +import net.corda.nodeapi.internal.serialization.amqp.testutils.TestSerializationOutput +import net.corda.nodeapi.internal.serialization.amqp.testutils.testDefaultFactory +import net.corda.nodeapi.internal.serialization.amqp.testutils.testName import org.assertj.core.api.Assertions import org.junit.Ignore import org.junit.Test import java.io.NotSerializableException +import net.corda.nodeapi.internal.serialization.amqp.testutils.serializeAndReturnSchema +import net.corda.nodeapi.internal.serialization.amqp.testutils.serialize +import net.corda.nodeapi.internal.serialization.amqp.testutils.deserializeAndReturnEnvelope +import net.corda.nodeapi.internal.serialization.amqp.testutils.deserialize class ErrorMessagesTests { companion object { diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/EvolvabilityTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/EvolvabilityTests.kt index bd5408b2f2..fd9fad89b6 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/EvolvabilityTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/EvolvabilityTests.kt @@ -18,6 +18,8 @@ import net.corda.core.node.NotaryInfo import net.corda.core.serialization.ConstructorForDeserialization import net.corda.core.serialization.DeprecatedConstructorForDeserialization import net.corda.core.serialization.SerializedBytes +import net.corda.nodeapi.internal.serialization.amqp.testutils.TestSerializationOutput +import net.corda.nodeapi.internal.serialization.amqp.testutils.testDefaultFactory import net.corda.testing.common.internal.ProjectStructure.projectRootDir import net.corda.testing.core.DUMMY_NOTARY_NAME import net.corda.testing.core.TestIdentity @@ -28,6 +30,8 @@ import java.io.NotSerializableException import java.net.URI import java.time.Instant import kotlin.test.assertEquals +import net.corda.nodeapi.internal.serialization.amqp.testutils.serialize +import net.corda.nodeapi.internal.serialization.amqp.testutils.deserialize // To regenerate any of the binary test files do the following // diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/FingerPrinterTesting.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/FingerPrinterTesting.kt index 9168f7792c..b5d3f21f71 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/FingerPrinterTesting.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/FingerPrinterTesting.kt @@ -14,6 +14,8 @@ import org.junit.Test import java.lang.reflect.Type import kotlin.test.assertEquals import net.corda.nodeapi.internal.serialization.AllWhitelist +import net.corda.nodeapi.internal.serialization.amqp.testutils.TestSerializationOutput +import net.corda.nodeapi.internal.serialization.amqp.testutils.serializeAndReturnSchema class FingerPrinterTesting : FingerPrinter { private var index = 0 diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/GenericsTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/GenericsTests.kt index 84db597caa..9767de16dd 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/GenericsTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/GenericsTests.kt @@ -17,12 +17,8 @@ import net.corda.testing.common.internal.ProjectStructure.projectRootDir import org.junit.Test import net.corda.core.identity.AbstractParty import net.corda.core.identity.CordaX500Name -import net.corda.core.identity.Party -import net.corda.core.transactions.WireTransaction +import net.corda.nodeapi.internal.serialization.amqp.testutils.* import net.corda.testing.core.TestIdentity -import org.hibernate.Transaction -import java.io.File -import java.net.URI import java.util.* import java.util.concurrent.ConcurrentHashMap import kotlin.test.assertEquals @@ -311,7 +307,6 @@ class GenericsTests { val factory4 = SerializerFactory(AllWhitelist, cl()) factory4.register(net.corda.nodeapi.internal.serialization.amqp.custom.PublicKeySerializer) val des2 = DeserializationInput(factory4).deserializeAndReturnEnvelope(ser2.obj) - } @Test diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/OverridePKSerializerTest.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/OverridePKSerializerTest.kt index d16e881688..56c6ab7624 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/OverridePKSerializerTest.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/OverridePKSerializerTest.kt @@ -23,11 +23,15 @@ class OverridePKSerializerTest { class SerializerTestException(message: String) : Exception(message) class TestPublicKeySerializer : CustomSerializer.Implements(PublicKey::class.java) { - override fun writeDescribedObject(obj: PublicKey, data: Data, type: Type, output: SerializationOutput) { + override fun writeDescribedObject(obj: PublicKey, data: Data, type: Type, output: SerializationOutput, + context: SerializationContext + ) { throw SerializerTestException("Custom write call") } - override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput): PublicKey { + override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput, + context: SerializationContext + ) : PublicKey { throw SerializerTestException("Custom read call") } diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/PrivatePropertyTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/PrivatePropertyTests.kt index dad1a4696d..4efb054eae 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/PrivatePropertyTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/PrivatePropertyTests.kt @@ -13,11 +13,15 @@ package net.corda.nodeapi.internal.serialization.amqp import junit.framework.TestCase.assertTrue import junit.framework.TestCase.assertEquals import net.corda.core.serialization.ConstructorForDeserialization +import net.corda.nodeapi.internal.serialization.amqp.testutils.testDefaultFactoryNoEvolution import org.junit.Test import org.apache.qpid.proton.amqp.Symbol import org.assertj.core.api.Assertions import java.io.NotSerializableException import java.util.concurrent.ConcurrentHashMap +import net.corda.nodeapi.internal.serialization.amqp.testutils.serializeAndReturnSchema +import net.corda.nodeapi.internal.serialization.amqp.testutils.serialize +import net.corda.nodeapi.internal.serialization.amqp.testutils.deserialize class PrivatePropertyTests { private val factory = testDefaultFactoryNoEvolution() diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/RoundTripTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/RoundTripTests.kt index f42823a862..aeff6be5bd 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/RoundTripTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/RoundTripTests.kt @@ -1,8 +1,11 @@ package net.corda.nodeapi.internal.serialization.amqp import net.corda.core.serialization.ConstructorForDeserialization +import net.corda.nodeapi.internal.serialization.amqp.testutils.testDefaultFactoryNoEvolution import org.assertj.core.api.Assertions import org.junit.Test +import net.corda.nodeapi.internal.serialization.amqp.testutils.serialize +import net.corda.nodeapi.internal.serialization.amqp.testutils.deserialize class RoundTripTests { @Test diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationOutputTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationOutputTests.kt index 1ac7a84d37..002ee156cc 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationOutputTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationOutputTests.kt @@ -33,6 +33,7 @@ import net.corda.nodeapi.internal.DEV_INTERMEDIATE_CA import net.corda.nodeapi.internal.crypto.ContentSignerBuilder import net.corda.nodeapi.internal.serialization.* import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory.Companion.isPrimitive +import net.corda.nodeapi.internal.serialization.amqp.testutils.* import net.corda.testing.contracts.DummyContract import net.corda.testing.core.BOB_NAME import net.corda.testing.core.SerializationEnvironmentRule @@ -490,12 +491,12 @@ class SerializationOutputTests(private val compression: CordaSerializationEncodi // Double check copy[valueIndex] = 0x03 - assertThat(des.deserialize(OpaqueBytes(copy), NonZeroByte::class.java).value).isEqualTo(3) + assertThat(des.deserialize(OpaqueBytes(copy), NonZeroByte::class.java, testSerializationContext).value).isEqualTo(3) // Now use the forbidden value copy[valueIndex] = 0x00 assertThatExceptionOfType(NotSerializableException::class.java).isThrownBy { - des.deserialize(OpaqueBytes(copy), NonZeroByte::class.java) + des.deserialize(OpaqueBytes(copy), NonZeroByte::class.java, testSerializationContext) }.withMessageContaining("Zero not allowed") } @@ -660,14 +661,16 @@ class SerializationOutputTests(private val compression: CordaSerializationEncodi val scheme = AMQPServerSerializationScheme(emptyList()) val func = scheme::class.superclasses.single { it.simpleName == "AbstractAMQPSerializationScheme" } - .java.getDeclaredMethod("registerCustomSerializers", SerializerFactory::class.java) + .java.getDeclaredMethod("registerCustomSerializers", + SerializationContext::class.java, + SerializerFactory::class.java) func.isAccessible = true val factory = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader()) - func.invoke(scheme, factory) + func.invoke(scheme, testSerializationContext, factory) val factory2 = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader()) - func.invoke(scheme, factory2) + func.invoke(scheme, testSerializationContext, factory2) val desState = serdes(state, factory, factory2, expectedEqual = false, expectDeserializedEqual = false) assertTrue((desState as TransactionState<*>).data is FooState) diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationPropertyOrdering.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationPropertyOrdering.kt index ccc4dde1a9..381a376b82 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationPropertyOrdering.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationPropertyOrdering.kt @@ -11,6 +11,8 @@ package net.corda.nodeapi.internal.serialization.amqp import net.corda.core.serialization.ConstructorForDeserialization +import net.corda.nodeapi.internal.serialization.amqp.testutils.TestSerializationOutput +import net.corda.nodeapi.internal.serialization.amqp.testutils.testDefaultFactoryNoEvolution import org.junit.Test import java.util.concurrent.ConcurrentHashMap import kotlin.test.assertEquals @@ -18,6 +20,10 @@ import org.apache.qpid.proton.amqp.Symbol import java.lang.reflect.Method import kotlin.test.assertNotNull import kotlin.test.assertTrue +import net.corda.nodeapi.internal.serialization.amqp.testutils.serializeAndReturnSchema +import net.corda.nodeapi.internal.serialization.amqp.testutils.serialize +import net.corda.nodeapi.internal.serialization.amqp.testutils.deserializeAndReturnEnvelope +import net.corda.nodeapi.internal.serialization.amqp.testutils.deserialize class SerializationPropertyOrdering { companion object { diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializeAndReturnSchemaTest.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializeAndReturnSchemaTest.kt index 4b6235431b..001f2f6ef5 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializeAndReturnSchemaTest.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializeAndReturnSchemaTest.kt @@ -10,10 +10,13 @@ package net.corda.nodeapi.internal.serialization.amqp +import net.corda.nodeapi.internal.serialization.amqp.testutils.testDefaultFactoryNoEvolution +import net.corda.nodeapi.internal.serialization.amqp.testutils.testName import org.junit.Test import kotlin.test.assertEquals import kotlin.test.assertNotNull import kotlin.test.assertTrue +import net.corda.nodeapi.internal.serialization.amqp.testutils.serializeAndReturnSchema class SerializeAndReturnSchemaTest { // the 'this' reference means we can't just move this to the common test utils diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/StaticInitialisationOfSerializedObjectTest.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/StaticInitialisationOfSerializedObjectTest.kt index f5ef970f1b..d1c67de023 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/StaticInitialisationOfSerializedObjectTest.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/StaticInitialisationOfSerializedObjectTest.kt @@ -20,6 +20,10 @@ import java.io.NotSerializableException import java.lang.reflect.Type import java.util.concurrent.ConcurrentHashMap import kotlin.test.assertEquals +import net.corda.nodeapi.internal.serialization.amqp.testutils.serializeAndReturnSchema +import net.corda.nodeapi.internal.serialization.amqp.testutils.serialize +import net.corda.nodeapi.internal.serialization.amqp.testutils.deserializeAndReturnEnvelope +import net.corda.nodeapi.internal.serialization.amqp.testutils.deserialize class InStatic : Exception("Help!, help!, I'm being repressed") diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/testutils/AMQPTestUtils.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/testutils/AMQPTestUtils.kt new file mode 100644 index 0000000000..e8a02a80b4 --- /dev/null +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/testutils/AMQPTestUtils.kt @@ -0,0 +1,67 @@ +package net.corda.nodeapi.internal.serialization.amqp.testutils + +import net.corda.core.serialization.SerializationContext +import net.corda.core.serialization.SerializedBytes +import org.apache.qpid.proton.codec.Data +import net.corda.nodeapi.internal.serialization.AllWhitelist +import net.corda.nodeapi.internal.serialization.EmptyWhitelist +import net.corda.nodeapi.internal.serialization.amqp.* +import java.io.NotSerializableException + +fun testDefaultFactory() = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader()) +fun testDefaultFactoryNoEvolution() = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader(), + EvolutionSerializerGetterTesting()) +fun testDefaultFactoryWithWhitelist() = SerializerFactory(EmptyWhitelist, ClassLoader.getSystemClassLoader()) + +class TestSerializationOutput( + private val verbose: Boolean, + serializerFactory: SerializerFactory = testDefaultFactory()) + : SerializationOutput(serializerFactory) { + + override fun writeSchema(schema: Schema, data: Data) { + if (verbose) println(schema) + super.writeSchema(schema, data) + } + + override fun writeTransformSchema(transformsSchema: TransformsSchema, data: Data) { + if(verbose) { + println ("Writing Transform Schema") + println (transformsSchema) + } + super.writeTransformSchema(transformsSchema, data) + } +} + +fun testName(): String = Thread.currentThread().stackTrace[2].methodName + + +@Throws(NotSerializableException::class) +inline fun DeserializationInput.deserializeAndReturnEnvelope( + bytes: SerializedBytes, + context: SerializationContext? = null +) : ObjectAndEnvelope { + return deserializeAndReturnEnvelope(bytes, T::class.java, + context ?: testSerializationContext) +} + +@Throws(NotSerializableException::class) +inline fun DeserializationInput.deserialize( + bytes: SerializedBytes, + context: SerializationContext? = null +) : T = deserialize(bytes, T::class.java, context ?: testSerializationContext) + + +@Throws(NotSerializableException::class) +fun SerializationOutput.serializeAndReturnSchema( + obj: T, context: SerializationContext? = null +): BytesAndSchemas = serializeAndReturnSchema(obj, context ?: testSerializationContext) + + +@Throws(NotSerializableException::class) +fun SerializationOutput.serialize(obj: T): SerializedBytes { + try { + return _serialize(obj, testSerializationContext) + } finally { + andFinally() + } +} diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/testutils/TestSerializationContext.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/testutils/TestSerializationContext.kt new file mode 100644 index 0000000000..237640a59a --- /dev/null +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/testutils/TestSerializationContext.kt @@ -0,0 +1,17 @@ +package net.corda.nodeapi.internal.serialization.amqp.testutils + +import net.corda.core.serialization.SerializationContext +import net.corda.nodeapi.internal.serialization.AllWhitelist +import net.corda.nodeapi.internal.serialization.SerializationContextImpl +import net.corda.nodeapi.internal.serialization.amqp.amqpMagic + +val serializationProperties: MutableMap = mutableMapOf() + +val testSerializationContext = SerializationContextImpl( + preferredSerializationVersion = amqpMagic, + deserializationClassLoader = ClassLoader.getSystemClassLoader(), + whitelist = AllWhitelist, + properties = serializationProperties, + objectReferencesEnabled = false, + useCase = SerializationContext.UseCase.Testing, + encoding = null) \ No newline at end of file diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/carpenter/CarpenterExceptionTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/carpenter/CarpenterExceptionTests.kt index c3ce2469a5..388f20517d 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/carpenter/CarpenterExceptionTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/carpenter/CarpenterExceptionTests.kt @@ -4,6 +4,8 @@ import com.google.common.reflect.TypeToken import junit.framework.Assert.assertTrue import net.corda.nodeapi.internal.serialization.AllWhitelist import net.corda.nodeapi.internal.serialization.amqp.* +import net.corda.nodeapi.internal.serialization.amqp.testutils.TestSerializationOutput +import net.corda.nodeapi.internal.serialization.amqp.testutils.testDefaultFactory import org.assertj.core.api.Assertions import org.junit.Test import java.io.NotSerializableException @@ -11,6 +13,8 @@ import java.lang.reflect.Type import java.net.URL import kotlin.reflect.jvm.jvmName import kotlin.test.assertEquals +import net.corda.nodeapi.internal.serialization.amqp.testutils.serialize +import net.corda.nodeapi.internal.serialization.amqp.testutils.deserialize // Simple way to ensure we end up trying to carpent a class, "remove" it from the class loader (if only // actually doing that was simple) diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/carpenter/ClassCarpenterTestUtils.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/carpenter/ClassCarpenterTestUtils.kt index 8a331c54d2..c3892a6a07 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/carpenter/ClassCarpenterTestUtils.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/carpenter/ClassCarpenterTestUtils.kt @@ -14,7 +14,7 @@ import net.corda.core.serialization.ClassWhitelist import net.corda.nodeapi.internal.serialization.amqp.* import net.corda.nodeapi.internal.serialization.amqp.Field import net.corda.nodeapi.internal.serialization.amqp.Schema -import net.corda.nodeapi.internal.serialization.AllWhitelist +import net.corda.nodeapi.internal.serialization.amqp.testutils.serialize fun mangleName(name: String) = "${name}__carpenter" diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/carpenter/CompositeMemberCompositeSchemaToClassCarpenterTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/carpenter/CompositeMemberCompositeSchemaToClassCarpenterTests.kt index c7ee5001f6..2f5dab3ede 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/carpenter/CompositeMemberCompositeSchemaToClassCarpenterTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/carpenter/CompositeMemberCompositeSchemaToClassCarpenterTests.kt @@ -18,6 +18,7 @@ import org.junit.Test import kotlin.test.assertEquals import kotlin.test.assertFalse import kotlin.test.assertTrue +import net.corda.nodeapi.internal.serialization.amqp.testutils.deserializeAndReturnEnvelope @CordaSerializable interface I_ { diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/carpenter/InheritanceSchemaToClassCarpenterTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/carpenter/InheritanceSchemaToClassCarpenterTests.kt index 7213ac7b0f..e3a97502eb 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/carpenter/InheritanceSchemaToClassCarpenterTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/carpenter/InheritanceSchemaToClassCarpenterTests.kt @@ -15,6 +15,7 @@ import net.corda.nodeapi.internal.serialization.AllWhitelist import net.corda.nodeapi.internal.serialization.amqp.DeserializationInput import org.junit.Test import kotlin.test.* +import net.corda.nodeapi.internal.serialization.amqp.testutils.deserializeAndReturnEnvelope @CordaSerializable interface J { diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/carpenter/MultiMemberCompositeSchemaToClassCarpenterTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/carpenter/MultiMemberCompositeSchemaToClassCarpenterTests.kt index 30deee8ea7..acc6e8426e 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/carpenter/MultiMemberCompositeSchemaToClassCarpenterTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/carpenter/MultiMemberCompositeSchemaToClassCarpenterTests.kt @@ -17,6 +17,7 @@ import net.corda.nodeapi.internal.serialization.amqp.DeserializationInput import org.junit.Test import kotlin.test.assertEquals import kotlin.test.assertNotEquals +import net.corda.nodeapi.internal.serialization.amqp.testutils.deserializeAndReturnEnvelope class MultiMemberCompositeSchemaToClassCarpenterTests : AmqpCarpenterBase(AllWhitelist) { diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/carpenter/SingleMemberCompositeSchemaToClassCarpenterTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/carpenter/SingleMemberCompositeSchemaToClassCarpenterTests.kt index adc7b7d791..88bb90e5fa 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/carpenter/SingleMemberCompositeSchemaToClassCarpenterTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/carpenter/SingleMemberCompositeSchemaToClassCarpenterTests.kt @@ -16,6 +16,7 @@ import net.corda.nodeapi.internal.serialization.amqp.CompositeType import net.corda.nodeapi.internal.serialization.amqp.DeserializationInput import org.junit.Test import kotlin.test.assertEquals +import net.corda.nodeapi.internal.serialization.amqp.testutils.deserializeAndReturnEnvelope class SingleMemberCompositeSchemaToClassCarpenterTests : AmqpCarpenterBase(AllWhitelist) { @Test diff --git a/node/src/main/kotlin/net/corda/node/Corda.kt b/node/src/main/kotlin/net/corda/node/Corda.kt index 58ff6eeaac..f8166cf62b 100644 --- a/node/src/main/kotlin/net/corda/node/Corda.kt +++ b/node/src/main/kotlin/net/corda/node/Corda.kt @@ -13,15 +13,10 @@ package net.corda.node -import net.corda.core.crypto.CordaSecurityProvider -import net.corda.core.crypto.Crypto import kotlin.system.exitProcess import net.corda.node.internal.EnterpriseNode fun main(args: Array) { - // Register all cryptography [Provider]s first thing on boot. - // Required to install our [SecureRandom] before e.g., UUID asks for one. - Crypto.registerProviders() // Pass the arguments to the Node factory. In the Enterprise edition, this line is modified to point to a subclass. // It will exit the process in case of startup failure and is not intended to be used by embedders. If you want // to embed Node in your own container, instantiate it directly and set up the configuration objects yourself. 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 baa8f3bfc0..0991826bef 100644 --- a/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt +++ b/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt @@ -11,6 +11,7 @@ package net.corda.node.internal import com.jcabi.manifests.Manifests +import net.corda.core.crypto.Crypto import net.corda.core.internal.Emoji import net.corda.core.internal.concurrent.thenMatch import net.corda.core.internal.createDirectories @@ -66,6 +67,11 @@ open class NodeStartup(val args: Array) { initLogging(cmdlineOptions) + // Register all cryptography [Provider]s. + // Required to install our [SecureRandom] before e.g., UUID asks for one. + // This needs to go after initLogging(netty clashes with our logging). + Crypto.registerProviders() + val versionInfo = getVersionInfo() if (cmdlineOptions.isVersion) { diff --git a/settings.gradle b/settings.gradle index c3220102e7..27044b5e0b 100644 --- a/settings.gradle +++ b/settings.gradle @@ -34,6 +34,7 @@ include 'experimental:quasar-hook' include 'experimental:kryo-hook' include 'experimental:intellij-plugin' include 'experimental:flow-hook' +include 'experimental:blobinspector' include 'test-common' include 'test-utils' include 'smoke-test-utils' diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNetwork.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNetwork.kt index 500121ff4e..9861c8b59a 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNetwork.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNetwork.kt @@ -43,17 +43,23 @@ import java.nio.file.Path * @property entropyRoot the initial entropy value to use when generating keys. Defaults to an (insecure) random value, * but can be overridden to cause nodes to have stable or colliding identity/service keys. * @property configOverrides Add/override behaviour of the [NodeConfiguration] mock object. + * @property extraCordappPackages Extra CorDapp packages to include for this node. */ @Suppress("unused") -data class MockNodeParameters( +data class MockNodeParameters @JvmOverloads constructor( val forcedID: Int? = null, val legalName: CordaX500Name? = null, val entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()), - val configOverrides: (NodeConfiguration) -> Any? = {}) { + val configOverrides: (NodeConfiguration) -> Any? = {}, + val extraCordappPackages: List = emptyList()) { fun withForcedID(forcedID: Int?): MockNodeParameters = copy(forcedID = forcedID) fun withLegalName(legalName: CordaX500Name?): MockNodeParameters = copy(legalName = legalName) fun withEntropyRoot(entropyRoot: BigInteger): MockNodeParameters = copy(entropyRoot = entropyRoot) fun withConfigOverrides(configOverrides: (NodeConfiguration) -> Any?): MockNodeParameters = copy(configOverrides = configOverrides) + fun withExtraCordappPackages(extraCordappPackages: List): MockNodeParameters = copy(extraCordappPackages = extraCordappPackages) + fun copy(forcedID: Int?, legalName: CordaX500Name?, entropyRoot: BigInteger, configOverrides: (NodeConfiguration) -> Any?): MockNodeParameters { + return MockNodeParameters(forcedID, legalName, entropyRoot, configOverrides) + } } /** @@ -258,14 +264,15 @@ open class MockNetwork( * @param entropyRoot The initial entropy value to use when generating keys. Defaults to an (insecure) random value, * but can be overridden to cause nodes to have stable or colliding identity/service keys. * @param configOverrides Add/override behaviour of the [NodeConfiguration] mock object. - * @param version The mock node's platform, release, revision and vendor versions. + * @param extraCordappPackages Extra CorDapp packages to add for this node. */ @JvmOverloads fun createNode(legalName: CordaX500Name? = null, forcedID: Int? = null, entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()), - configOverrides: (NodeConfiguration) -> Any? = {}): StartedMockNode { - val parameters = MockNodeParameters(forcedID, legalName, entropyRoot, configOverrides) + configOverrides: (NodeConfiguration) -> Any? = {}, + extraCordappPackages: List = emptyList()): StartedMockNode { + val parameters = MockNodeParameters(forcedID, legalName, entropyRoot, configOverrides, extraCordappPackages) return StartedMockNode.create(internalMockNetwork.createNode(InternalMockNodeParameters(parameters))) } @@ -280,14 +287,15 @@ open class MockNetwork( * @param entropyRoot The initial entropy value to use when generating keys. Defaults to an (insecure) random value, * but can be overridden to cause nodes to have stable or colliding identity/service keys. * @param configOverrides Add/override behaviour of the [NodeConfiguration] mock object. - * @param version The mock node's platform, release, revision and vendor versions. + * @param extraCordappPackages Extra CorDapp packages to add for this node. */ @JvmOverloads fun createUnstartedNode(legalName: CordaX500Name? = null, forcedID: Int? = null, entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()), - configOverrides: (NodeConfiguration) -> Any? = {}): UnstartedMockNode { - val parameters = MockNodeParameters(forcedID, legalName, entropyRoot, configOverrides) + configOverrides: (NodeConfiguration) -> Any? = {}, + extraCordappPackages: List = emptyList()): UnstartedMockNode { + val parameters = MockNodeParameters(forcedID, legalName, entropyRoot, configOverrides, extraCordappPackages) return UnstartedMockNode.create(internalMockNetwork.createUnstartedNode(InternalMockNodeParameters(parameters))) } diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/InternalMockNetwork.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/InternalMockNetwork.kt index 437bf2927e..51dd476c6e 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/InternalMockNetwork.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/InternalMockNetwork.kt @@ -83,7 +83,8 @@ data class MockNodeArgs( val network: InternalMockNetwork, val id: Int, val entropyRoot: BigInteger, - val version: VersionInfo = MOCK_VERSION_INFO + val version: VersionInfo = MOCK_VERSION_INFO, + val extraCordappPackages: List = emptyList() ) data class InternalMockNodeParameters( @@ -91,12 +92,16 @@ data class InternalMockNodeParameters( val legalName: CordaX500Name? = null, val entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()), val configOverrides: (NodeConfiguration) -> Any? = {}, - val version: VersionInfo = MOCK_VERSION_INFO) { + val version: VersionInfo = MOCK_VERSION_INFO, + val extraCordappPackages: List = emptyList()) { constructor(mockNodeParameters: MockNodeParameters) : this( mockNodeParameters.forcedID, mockNodeParameters.legalName, mockNodeParameters.entropyRoot, - mockNodeParameters.configOverrides) + mockNodeParameters.configOverrides, + MOCK_VERSION_INFO, + mockNodeParameters.extraCordappPackages + ) } open class InternalMockNetwork(private val cordappPackages: List, @@ -227,7 +232,8 @@ open class InternalMockNetwork(private val cordappPackages: List, args.config, TestClock(Clock.systemUTC()), args.version, - CordappLoader.createDefaultWithTestPackages(args.config, args.network.cordappPackages), + // Add the specified additional CorDapps. + CordappLoader.createDefaultWithTestPackages(args.config, args.network.cordappPackages + args.extraCordappPackages), args.network.busyLatch ) { companion object { @@ -379,7 +385,7 @@ open class InternalMockNetwork(private val cordappPackages: List, doReturn(emptyList()).whenever(it).extraNetworkMapKeys parameters.configOverrides(it) } - val node = nodeFactory(MockNodeArgs(config, this, id, parameters.entropyRoot, parameters.version)) + val node = nodeFactory(MockNodeArgs(config, this, id, parameters.entropyRoot, parameters.version, parameters.extraCordappPackages)) _nodes += node if (start) { node.start()