This commit is contained in:
cburlinchon 2018-05-04 15:47:36 +01:00 committed by GitHub
parent 081b7251a7
commit 5c0775f7b4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
121 changed files with 1884 additions and 374 deletions

4
.idea/compiler.xml generated
View File

@ -14,6 +14,8 @@
<module name="behave_main" target="1.8" />
<module name="behave_scenario" target="1.8" />
<module name="behave_test" target="1.8" />
<module name="blobinspector_main" target="1.8" />
<module name="blobinspector_test" target="1.8" />
<module name="bootstrapper_main" target="1.8" />
<module name="bootstrapper_test" target="1.8" />
<module name="bridge_integrationTest" target="1.8" />
@ -189,4 +191,4 @@
<component name="JavacSettings">
<option name="ADDITIONAL_OPTIONS_STRING" value="-parameters" />
</component>
</project>
</project>

View File

@ -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'

View File

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

View File

@ -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<String> = 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
^^^^^^^^^^^^^^^^^^^

View File

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

View File

@ -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="<cmd> <line> <args>" :experimental:blobinspector:run
*
* For example, to parse a file from the command line and print out the deserialized properties
*
* ./gradlew -PrunArgs="-f <path/to/file> -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'
)
}
}

View File

@ -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<net.corda.blobinspector.Class2>
* Class1 <Class2>
*
* net.corda.blobinspector.Class1<net.corda.blobinspector.Class2, net.corda.blobinspector.Class3>
* Class1 <Class2, Class3>
*
* net.corda.blobinspector.Class1<net.corda.blobinspector.Class2<net.corda.blobinspector.Class3>>
* Class1 <Class2 <Class3>>
*
* net.corda.blobinspector.Class1<net.corda.blobinspector.Class2<net.corda.blobinspector.Class3>>
* Class1 :: C <Class2 <Class3>>
*/
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 : <<<BINARY BLOB>>>"
override fun stringify(sb: IndentingStringBuilder) {
sb.appendln("$name : $type : <<<BINARY BLOB>>>")
}
}
/**
* 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<Any> = 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<Property> = mutableListOf()) : Stringify {
override fun stringify(sb: IndentingStringBuilder) {
sb.apply {
appendln("${name.simplifyClass()} : {")
fields.forEach {
it.stringify(this)
}
appendln("}")
}
}
}
/**
*
*/
fun inspectComposite(
config: Config,
typeMap: Map<Symbol?, TypeNotation>,
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<Any>)
}
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<Symbol?, TypeNotation>,
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<Symbol?, TypeNotation>,
obj: DescribedType
) : List<Any> {
if (obj.described !is List<*>) throw MalformedBlob("")
return mutableListOf<Any>().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<Symbol?, TypeNotation>,
obj: DescribedType
) : Map<Any, Any> {
if (obj.described !is Map<*,*>) throw MalformedBlob("")
return mutableMapOf<Any, Any>().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<Symbol?, TypeNotation>,
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)
}
}
}
}

View File

@ -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
}

View File

@ -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")
}
}

View File

@ -0,0 +1,3 @@
package net.corda.blobinspector
class MalformedBlob(msg: String) : Exception(msg)

View File

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

View File

@ -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<String>) : 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<String>) {
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<String>) {
println ("<<< WARNING: this tool is experimental and under active development >>>")
getMode(args).let { mode ->
loadModeSpecificOptions(mode, args)
BlobHandler.make(mode)
}.apply {
inspectBlob(config, getBytes())
}
}

View File

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

View File

@ -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<String>)
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<Elem>)
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<List<Elem>>)
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<String, String>)
inspect (SerializationOutput(factory).serialize(
C(mapOf(
"a" to "a a a",
"b" to "b b b",
"c" to "c c c")),
AMQP_P2P_CONTEXT
))
}
}

View File

@ -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<String>(2) {
when (it) {
0 -> "-m"
1 -> "file"
else -> "error"
}
}
assertEquals(Mode.file, getMode(opts1).mode)
}
@Test
fun nothingIsSetToFile() {
val opts1 = Array<String>(0) { "" }
assertEquals(Mode.file, getMode(opts1).mode)
}
@Test
fun filePathIsSet() {
val opts1 = Array<String>(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)
}
}
}
}

View File

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

View File

@ -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<String, Any?>)
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)

View File

@ -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 -> {

View File

@ -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<String>, private val delegate: ClassWhitelist) : MutableClassWhitelist {
sealed class AbstractMutableClassWhitelist(private val whitelist: MutableSet<String>, private val delegate: ClassWhitelist) : MutableClassWhitelist {
override fun hasListed(type: Class<*>): Boolean {
/**

View File

@ -76,5 +76,5 @@ object DefaultWhitelist : SerializationWhitelist {
// Implementation of X509Certificate.
X509CertImpl::class.java,
CRLReason::class.java
)
)
}

View File

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

View File

@ -49,13 +49,15 @@ data class SerializationContextImpl @JvmOverloads constructor(override val prefe
*/
override fun withAttachmentsClassLoader(attachmentHashes: List<SecureHash>): 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<SecureHash>()
val attachments = ArrayList<Attachment>()
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<StackTraceElement> = Exception().stackTrace.asList()
@ -153,8 +155,6 @@ open class SerializationFactoryImpl : SerializationFactory() {
}
interface SerializationScheme {
fun canDeserializeVersion(magic: CordaSerializationMagic, target: SerializationContext.UseCase): Boolean
@Throws(NotSerializableException::class)

View File

@ -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")
}

View File

@ -15,7 +15,8 @@ import net.corda.core.serialization.SerializationFactory
import java.util.*
internal fun checkUseCase(allowedUseCases: EnumSet<SerializationContext.UseCase>) {
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'")
}

View File

@ -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.

View File

@ -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<Any> {
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<Any> {
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
}

View File

@ -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<Cordapp>,
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<PublicKey>
= net.corda.nodeapi.internal.serialization.amqp.custom.PublicKeySerializer
protected open val publicKeySerializer: CustomSerializer.Implements<PublicKey> = 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 <T : Any> deserialize(byteSequence: ByteSequence, clazz: Class<T>, context: SerializationContext): T {
val serializerFactory = getSerializerFactory(context)
return DeserializationInput(serializerFactory).deserialize(byteSequence, clazz)
return DeserializationInput(serializerFactory).deserialize(byteSequence, clazz, context)
}
override fun <T : Any> serialize(obj: T, context: SerializationContext): SerializedBytes<T> {
val serializerFactory = getSerializerFactory(context)
return SerializationOutput(serializerFactory).serialize(obj)
return SerializationOutput(serializerFactory).serialize(obj, context)
}
protected fun canDeserializeVersion(magic: CordaSerializationMagic) = magic == amqpMagic

View File

@ -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<out T> {
/**
* 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
}

View File

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

View File

@ -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<Any> {
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<out Collection<*>>): ParameterizedType =
(declaredType as? ParameterizedType) ?: DeserializedParameterizedType(collectionClass, arrayOf(SerializerFactory.AnyType))
(declaredType as? ParameterizedType)
?: DeserializedParameterizedType(collectionClass, arrayOf(SerializerFactory.AnyType))
private fun findMostSuitableCollectionType(actualClass: Class<*>): Class<out Collection<*>> =
@ -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)
})
}
}

View File

@ -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<*, *>,
SerializationCustomSerializer<Any?, Any?>>(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<*, *>, SerializationCustomSerializer<Any?, Any?>>(
serializer).fromProxy(uncheckedCast(proxySerializer.readObject(obj, schemas, input)))!!
override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput,
context: SerializationContext
) = uncheckedCast<SerializationCustomSerializer<*, *>, SerializationCustomSerializer<Any?, Any?>>(
serializer).fromProxy(uncheckedCast(proxySerializer.readObject(obj, schemas, input, context)))!!
override fun isSerializerFor(clazz: Class<*>) = clazz == type
}

View File

@ -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<T : Any> : AMQPSerializer<T>, 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<T : Any> : AMQPSerializer<T>, 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<T : Any> : AMQPSerializer<T>, SerializerFor {
private val proxySerializer: ObjectSerializer by lazy { ObjectSerializer(proxyClass, factory) }
override val schemaForDocumentation: Schema by lazy {
val typeNotations = mutableSetOf<TypeNotation>(CompositeType(nameForType(type), null, emptyList(), descriptor, (proxySerializer.typeNotation as CompositeType).fields))
val typeNotations = mutableSetOf<TypeNotation>(
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<T : Any> : AMQPSerializer<T>, 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<T : Any> : AMQPSerializer<T>, 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)
}

View File

@ -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<Any> = 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 <T> withDataBytes(byteSequence: ByteSequence, encodingWhitelist: EncodingWhitelist, task: (ByteBuffer) -> T): T {
fun <T> 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 <reified T : Any> deserialize(bytes: SerializedBytes<T>): T = deserialize(bytes, T::class.java)
@Throws(NotSerializableException::class)
inline internal fun <reified T : Any> deserializeAndReturnEnvelope(bytes: SerializedBytes<T>): ObjectAndEnvelope<T> =
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 <reified T : Any> deserialize(bytes: SerializedBytes<T>, context: SerializationContext): T =
deserialize(bytes, T::class.java, context)
@Throws(NotSerializableException::class)
private fun <R> 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 <T : Any> deserialize(bytes: ByteSequence, clazz: Class<T>): T = des {
val envelope = getEnvelope(bytes)
clazz.cast(readObjectOrNull(envelope.obj, SerializationSchemas(envelope.schema, envelope.transformsSchema), clazz))
}
fun <T : Any> deserialize(bytes: ByteSequence, clazz: Class<T>, 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 <T : Any> deserializeAndReturnEnvelope(bytes: SerializedBytes<T>, clazz: Class<T>): ObjectAndEnvelope<T> = des {
val envelope = getEnvelope(bytes)
fun <T : Any> deserializeAndReturnEnvelope(
bytes: SerializedBytes<T>,
clazz: Class<T>,
context: SerializationContext
): ObjectAndEnvelope<T> = 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)
}

View File

@ -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<Any>,
factory: SerializerFactory,
schemas: SerializationSchemas): AMQPSerializer<Any> {
val wireTransforms = schemas.transforms.types[old.name] ?: EnumMap<TransformTypes, MutableList<Transform>>(TransformTypes::class.java)
val wireTransforms = schemas.transforms.types[old.name]
?: EnumMap<TransformTypes, MutableList<Transform>>(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")
}
}

View File

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

View File

@ -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<*>

View File

@ -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<Any?>) =
property.readProperty(obj, schemas, input).apply {
if(resultsIndex >= 0) {
new[resultsIndex] = this
}
}
fun readProperty(obj: Any?, schemas: SerializationSchemas, input: DeserializationInput,
new: Array<Any?>, 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<String, EvolutionSerializer.OldParam>,
kotlinConstructor: KFunction<Any>?,
private val constructorArgs: Array<Any?>) : EvolutionSerializer (clazz, factory, oldReaders, kotlinConstructor) {
private val constructorArgs: Array<Any?>) : 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<String, EvolutionSerializer.OldParam>,
kotlinConstructor: KFunction<Any>?,
private val setters: Map<String, PropertyAccessor>) : EvolutionSerializer (clazz, factory, oldReaders, kotlinConstructor) {
private val setters: Map<String, PropertyAccessor>) : 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)
}
}

View File

@ -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<Any> {
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<out Map<*, *>>): 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<out Map<*, *>> =
@ -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<Pair<Any?, Any?>> = (obj as Map<*, *>).map { readEntry(schemas, input, it) }
val entries: Iterable<Pair<Any?, Any?>> = (obj as Map<*, *>).map { readEntry(schemas, input, it, context) }
concreteBuilder(entries.toMap())
}
private fun readEntry(schemas: SerializationSchemas, input: DeserializationInput, entry: Map.Entry<Any?, Any?>) =
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<Any?, Any?>,
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.

View File

@ -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 {

View File

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

View File

@ -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
}

View File

@ -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<out Any?>.propertyDescriptors(): Map<String, PropertyDescriptor> {
// 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 <T : Any> 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 <T : Any> 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}]")

View File

@ -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<T : Any>(
* @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<Any, Int> = IdentityHashMap()
private val serializerHistory: MutableSet<AMQPSerializer<*>> = LinkedHashSet()
internal val schemaHistory: MutableSet<TypeNotation> = LinkedHashSet()
@ -44,19 +48,18 @@ open class SerializationOutput @JvmOverloads constructor(internal val serializer
* of AMQP serialization constructed the serialized form.
*/
@Throws(NotSerializableException::class)
fun <T : Any> serialize(obj: T): SerializedBytes<T> {
fun <T : Any> serialize(obj: T, context: SerializationContext): SerializedBytes<T> {
try {
return _serialize(obj)
return _serialize(obj, context)
} finally {
andFinally()
}
}
@Throws(NotSerializableException::class)
fun <T : Any> serializeAndReturnSchema(obj: T): BytesAndSchemas<T> {
fun <T : Any> serializeAndReturnSchema(obj: T, context: SerializationContext): BytesAndSchemas<T> {
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 <T : Any> _serialize(obj: T): SerializedBytes<T> {
internal fun <T : Any> _serialize(obj: T, context: SerializationContext): SerializedBytes<T> {
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

View File

@ -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<Any>,
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<SerializerFactory>().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)
}
}
}
}

View File

@ -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
}
}

View File

@ -224,8 +224,8 @@ data class TransformsSchema(val types: Map<String, EnumMap<TransformTypes, Mutab
// ignore them it feels like a good thing to alert the user to since this is
// more than likely a typo in their code so best make it an actual error
if (transforms.computeIfAbsent(transform.enum) { mutableListOf() }
.filter { t == it }
.isNotEmpty()) {
.filter { t == it }
.isNotEmpty()) {
throw NotSerializableException(
"Repeated unique transformation annotation of type ${t.name}")
}
@ -284,12 +284,12 @@ data class TransformsSchema(val types: Map<String, EnumMap<TransformTypes, Mutab
throw NotSerializableException("Unexpected descriptor ${describedType.descriptor}.")
}
val map = describedType.described as? 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, MutableList<Transform>>(TransformTypes::class.java)
@ -298,8 +298,8 @@ data class TransformsSchema(val types: Map<String, EnumMap<TransformTypes, Mutab
rtn[fingerprint]!![transform] = mutableListOf()
(transforms as List<*>).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}")
}
}

View File

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

View File

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

View File

@ -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, MonthDaySerializer.MonthDayProxy>(MonthDay::class.java, MonthDayProxy::class.java, factory) {
class MonthDaySerializer(factory: SerializerFactory)
: CustomSerializer.Proxy<MonthDay, MonthDaySerializer.MonthDayProxy>(
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())

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

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

View File

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

View File

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

View File

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

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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<Pair<String, Boolean>, Class<out Any?>> = 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

View File

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

View File

@ -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<out Any>) : 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

View File

@ -268,7 +268,7 @@ object NotaryChangeWireTransactionSerializer : Serializer<NotaryChangeWireTransa
}
override fun read(kryo: Kryo, input: Input, type: Class<NotaryChangeWireTransaction>): NotaryChangeWireTransaction {
val components : List<OpaqueBytes> = uncheckedCast(kryo.readClassAndObject(input))
val components: List<OpaqueBytes> = uncheckedCast(kryo.readClassAndObject(input))
return NotaryChangeWireTransaction(components)
}
}

View File

@ -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 <T : Any> deserialize(byteSequence: ByteSequence, clazz: Class<T>, 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

View File

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

View File

@ -24,12 +24,16 @@ import net.corda.core.serialization.SerializeAsToken
*/
class SerializeAsTokenSerializer<T : SerializeAsToken> : Serializer<T>() {
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>): 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}")
}
}

View File

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

View File

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

View File

@ -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);
}
}

View File

@ -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<ContractState> (new DummyState()))).isInstanceOf(
Assertions.assertThatThrownBy(() -> ser.serialize(new TemplateWrapper<ContractState> (new DummyState()), TestSerializationContext.testSerializationContext)).isInstanceOf(
NotSerializableException.class).hasMessageContaining(
"has synthetic fields and is likely a nested inner class");
}

View File

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

View File

@ -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<Object> bytes = ser.serialize(bra);
SerializedBytes<Object> bytes = ser.serialize(bra, TestSerializationContext.testSerializationContext);
}
}

View File

@ -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<Object> bytes = ser.serialize(obj);
SerializedBytes<Object> 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;

View File

@ -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<Object> bytes = ser.serialize(container);
SerializedBytes<Object> 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);
}
}

View File

@ -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);
}
}

View File

@ -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<Object, Object> serializationProperties = new HashMap<Object, Object>();
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);
}

View File

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

View File

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

View File

@ -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 {

View File

@ -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 {

View File

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

View File

@ -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 {

View File

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

View File

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

View File

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

View File

@ -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 {

View File

@ -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 {

View File

@ -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
//

View File

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

Some files were not shown because too many files have changed in this diff Show More