CORDA-847 - Pass serialization context down serialization call stack (#3062)

* CORDA-847 - Pass serialization context down serialization call stack

Needed for later work on AMQP RPC where we need to set per-thread
elements on a context. Could use some magic thread local but I'd rather
it was explicit on the stack and thus easier to reason about.

Additionally, now we're passing this around we can make better use of it
in the future

* Test Fix

* Test fixes

* REVIEW COMMENTS / CODE FORMAT

* Fix build issues
This commit is contained in:
Katelyn Baker 2018-05-03 16:09:03 +01:00 committed by GitHub
parent db22c5259d
commit a61aa5d645
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
91 changed files with 763 additions and 374 deletions

View File

@ -2,6 +2,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
@ -19,13 +20,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)
@ -42,7 +47,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)
@ -52,7 +59,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)
@ -62,7 +71,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)
@ -73,7 +84,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)
@ -84,7 +97,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)
@ -99,7 +114,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

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

@ -4,6 +4,7 @@ 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
@ -21,13 +22,13 @@ class InMemoryTests {
@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")))
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))))))
inspect (SerializationOutput(factory).serialize(C(1, C(2, C(3, C(4, null)))), AMQP_P2P_CONTEXT))
}
@Test
@ -37,7 +38,7 @@ class InMemoryTests {
val a = IntArray(10) { i -> i }
val c = C(a, arrayOf("aaa", "bbb", "ccc"))
inspect (SerializationOutput(factory).serialize(c))
inspect (SerializationOutput(factory).serialize(c, AMQP_P2P_CONTEXT))
}
@Test
@ -51,7 +52,7 @@ class InMemoryTests {
Elem(1L, "First element"),
Elem(2L, "Second element"),
Elem(3L, "Third element")
))))
)), AMQP_P2P_CONTEXT))
}
@Test
@ -72,7 +73,7 @@ class InMemoryTests {
Elem(5L, "Fifth element"),
Elem(6L, "Sixth element")
)
))))
)), AMQP_P2P_CONTEXT))
}
@Test
@ -83,7 +84,8 @@ class InMemoryTests {
C(mapOf(
"a" to "a a a",
"b" to "b b b",
"c" to "c c c"))
"c" to "c c c")),
AMQP_P2P_CONTEXT
))
}
}

View File

@ -351,7 +351,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

View File

@ -80,10 +80,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)!!)
@ -162,7 +162,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

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

View File

@ -7,8 +7,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

@ -39,13 +39,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)
@ -143,8 +145,6 @@ open class SerializationFactoryImpl : SerializationFactory() {
}
interface SerializationScheme {
fun canDeserializeVersion(magic: CordaSerializationMagic, target: SerializationContext.UseCase): Boolean
@Throws(NotSerializableException::class)

View File

@ -52,5 +52,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

@ -5,7 +5,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

@ -10,7 +10,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

@ -1,5 +1,6 @@
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
@ -18,7 +19,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 {
@ -29,5 +37,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

@ -6,8 +6,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
@ -31,12 +31,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
@ -52,7 +52,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()
@ -64,7 +64,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)
@ -121,8 +121,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)) {
@ -135,19 +134,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

@ -1,5 +1,6 @@
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
@ -30,10 +31,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

@ -1,5 +1,6 @@
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
@ -32,7 +33,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) }
@ -46,20 +48,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")
}
@ -108,20 +114,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)
}
}
}
@ -135,47 +145,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

@ -1,6 +1,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
@ -8,15 +9,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)}")
}
@ -50,7 +50,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<*>> =
@ -73,12 +74,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)
}
}
}
@ -87,8 +89,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

@ -1,6 +1,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
@ -64,22 +65,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

@ -1,6 +1,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
@ -40,13 +41,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
@ -77,12 +81,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)
}
}
@ -124,7 +132,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)
}
@ -138,17 +151,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)
}
}
@ -176,11 +193,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

@ -4,6 +4,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
@ -62,7 +63,8 @@ class DeserializationInput @JvmOverloads constructor(private val serializerFacto
@Throws(NotSerializableException::class)
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) {
@ -96,11 +98,9 @@ class DeserializationInput @JvmOverloads constructor(private val serializerFacto
fun getEnvelope(byteSequence: ByteSequence) = Companion.getEnvelope(byteSequence, encodingWhitelist)
@Throws(NotSerializableException::class)
inline fun <reified T : Any> deserialize(bytes: SerializedBytes<T>): T = deserialize(bytes, T::class.java)
inline fun <reified T : Any> deserialize(bytes: SerializedBytes<T>, context: SerializationContext): T =
deserialize(bytes, T::class.java, context)
@Throws(NotSerializableException::class)
inline internal fun <reified T : Any> deserializeAndReturnEnvelope(bytes: SerializedBytes<T>): ObjectAndEnvelope<T> =
deserializeAndReturnEnvelope(bytes, T::class.java)
@Throws(NotSerializableException::class)
private fun <R> des(generator: () -> R): R {
@ -121,23 +121,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, encodingWhitelist)
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 {
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()
@ -158,19 +172,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

@ -1,6 +1,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
@ -65,7 +66,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
@ -117,7 +119,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) {
@ -131,7 +135,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

@ -1,5 +1,6 @@
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
@ -28,7 +29,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<*>?
@ -40,7 +43,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

@ -1,10 +1,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
@ -39,12 +40,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 {
@ -96,7 +98,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(
@ -108,7 +110,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)
}
/**
@ -143,14 +145,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")
}
}
@ -160,7 +163,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
@ -170,15 +173,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.")
}
}
@ -191,18 +195,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

@ -1,6 +1,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
@ -8,9 +9,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<*, *>
@ -18,8 +16,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)}")
@ -57,7 +55,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<*, *>> =
@ -80,6 +79,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
@ -88,22 +88,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

@ -1,5 +1,6 @@
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
@ -57,6 +58,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 &&
@ -72,7 +74,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)
}
}
}
@ -81,16 +83,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")
@ -100,12 +103,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 })
}
@ -113,7 +117,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(
@ -123,7 +128,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

@ -1,5 +1,6 @@
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
@ -9,8 +10,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()
@ -76,12 +77,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})"
@ -96,11 +100,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))
@ -112,18 +120,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

@ -2,12 +2,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?
@ -141,6 +141,7 @@ class PropertyAccessorGetterSetter(
*/
setter.isAccessible = true
}
/**
* Invokes the setter on the underlying object passing in the serialized value.
*/
@ -162,7 +163,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.")
}
}
@ -187,7 +188,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")
}
}
}
@ -196,7 +197,7 @@ abstract class PropertySerializers(
abstract val byConstructor: Boolean
}
class PropertySerializersNoProperties : PropertySerializers (emptyList()) {
class PropertySerializersNoProperties : PropertySerializers(emptyList()) {
override val byConstructor get() = true
}

View File

@ -95,7 +95,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 {
@ -163,8 +163,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() }
@ -245,9 +244,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
@ -267,8 +266,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")
@ -315,7 +314,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

@ -1,5 +1,6 @@
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
@ -23,7 +24,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()
@ -34,19 +38,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 {
@ -60,11 +63,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)
@ -87,8 +90,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) {
@ -99,15 +102,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)
@ -116,7 +119,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

@ -5,9 +5,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.*
@ -59,8 +61,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
@ -99,7 +100,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)
}
@ -244,8 +246,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
@ -283,12 +285,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

@ -1,5 +1,6 @@
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
@ -23,13 +24,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

@ -214,8 +214,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}")
}
@ -274,12 +274,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)
@ -288,8 +288,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

@ -2,6 +2,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

@ -1,5 +1,6 @@
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
@ -15,7 +16,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
@ -34,8 +37,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

@ -2,12 +2,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

@ -2,7 +2,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

@ -2,7 +2,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

@ -2,7 +2,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

@ -1,7 +1,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
@ -15,13 +17,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

@ -1,6 +1,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
@ -12,13 +13,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

@ -1,5 +1,6 @@
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
@ -16,12 +17,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

@ -1,5 +1,6 @@
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
@ -16,12 +17,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

@ -2,7 +2,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

@ -2,7 +2,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

@ -2,7 +2,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

@ -2,11 +2,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()
@ -120,7 +120,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

@ -26,11 +26,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

@ -9,18 +9,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

@ -254,7 +254,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

@ -1,6 +1,5 @@
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
@ -12,14 +11,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))
@ -86,7 +85,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

@ -3,7 +3,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

@ -14,12 +14,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

@ -1,6 +1,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;
@ -41,7 +43,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

@ -2,6 +2,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;
@ -33,10 +34,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());
}
@ -48,13 +49,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 {
@ -65,13 +66,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

@ -1,6 +1,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.*;
@ -83,7 +84,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);
}
@ -98,7 +99,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);
}
@ -112,7 +113,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);
@ -128,7 +129,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);
}
@ -143,7 +144,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);
@ -175,7 +176,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

@ -1,5 +1,6 @@
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;
@ -33,6 +34,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

@ -6,6 +6,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;
@ -189,7 +190,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();
@ -209,13 +210,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

@ -3,6 +3,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;
@ -132,9 +133,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

@ -2,6 +2,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.*;
@ -132,7 +133,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());
@ -147,7 +148,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());
@ -164,7 +165,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());
@ -180,7 +181,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());
@ -212,9 +213,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);
@ -224,8 +225,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);
@ -239,8 +240,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);
@ -253,8 +255,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);
@ -280,8 +285,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);
@ -290,7 +297,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(
@ -303,12 +310,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(
@ -321,7 +329,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);
}
@ -347,6 +356,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

@ -1,32 +0,0 @@
package net.corda.nodeapi.internal.serialization.amqp
import org.apache.qpid.proton.codec.Data
import net.corda.nodeapi.internal.serialization.AllWhitelist
import net.corda.nodeapi.internal.serialization.EmptyWhitelist
fun testDefaultFactory() = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader())
fun testDefaultFactoryNoEvolution() = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader(),
EvolutionSerializerGetterTesting())
fun testDefaultFactoryWithWhitelist() = SerializerFactory(EmptyWhitelist, ClassLoader.getSystemClassLoader())
class TestSerializationOutput(
private val verbose: Boolean,
serializerFactory: SerializerFactory = testDefaultFactory())
: SerializationOutput(serializerFactory) {
override fun writeSchema(schema: Schema, data: Data) {
if (verbose) println(schema)
super.writeSchema(schema, data)
}
override fun writeTransformSchema(transformsSchema: TransformsSchema, data: Data) {
if(verbose) {
println ("Writing Transform Schema")
println (transformsSchema)
}
super.writeTransformSchema(transformsSchema, data)
}
}
fun testName(): String = Thread.currentThread().stackTrace[2].methodName

View File

@ -3,9 +3,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

@ -1,10 +1,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

@ -1,8 +1,14 @@
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

@ -1,9 +1,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

@ -1,9 +1,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

@ -5,6 +5,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

@ -1,7 +1,11 @@
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

@ -1,6 +1,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
@ -10,6 +12,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

@ -4,6 +4,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
@ -12,6 +14,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

@ -3,12 +3,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

@ -1,9 +1,16 @@
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

@ -8,6 +8,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
@ -18,6 +20,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

@ -4,6 +4,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

View File

@ -7,12 +7,8 @@ import net.corda.testing.common.internal.ProjectStructure.projectRootDir
import org.junit.Test
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party
import net.corda.core.transactions.WireTransaction
import net.corda.nodeapi.internal.serialization.amqp.testutils.*
import net.corda.testing.core.TestIdentity
import org.hibernate.Transaction
import java.io.File
import java.net.URI
import java.util.*
import java.util.concurrent.ConcurrentHashMap
import kotlin.test.assertEquals
@ -301,7 +297,6 @@ class GenericsTests {
val factory4 = SerializerFactory(AllWhitelist, cl())
factory4.register(net.corda.nodeapi.internal.serialization.amqp.custom.PublicKeySerializer)
val des2 = DeserializationInput(factory4).deserializeAndReturnEnvelope(ser2.obj)
}
@Test

View File

@ -13,11 +13,15 @@ class OverridePKSerializerTest {
class SerializerTestException(message: String) : Exception(message)
class TestPublicKeySerializer : CustomSerializer.Implements<PublicKey>(PublicKey::class.java) {
override fun writeDescribedObject(obj: PublicKey, data: Data, type: Type, output: SerializationOutput) {
override fun writeDescribedObject(obj: PublicKey, data: Data, type: Type, output: SerializationOutput,
context: SerializationContext
) {
throw SerializerTestException("Custom write call")
}
override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput): PublicKey {
override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput,
context: SerializationContext
) : PublicKey {
throw SerializerTestException("Custom read call")
}

View File

@ -3,11 +3,15 @@ package net.corda.nodeapi.internal.serialization.amqp
import junit.framework.TestCase.assertTrue
import junit.framework.TestCase.assertEquals
import net.corda.core.serialization.ConstructorForDeserialization
import net.corda.nodeapi.internal.serialization.amqp.testutils.testDefaultFactoryNoEvolution
import org.junit.Test
import org.apache.qpid.proton.amqp.Symbol
import org.assertj.core.api.Assertions
import java.io.NotSerializableException
import java.util.concurrent.ConcurrentHashMap
import net.corda.nodeapi.internal.serialization.amqp.testutils.serializeAndReturnSchema
import net.corda.nodeapi.internal.serialization.amqp.testutils.serialize
import net.corda.nodeapi.internal.serialization.amqp.testutils.deserialize
class PrivatePropertyTests {
private val factory = testDefaultFactoryNoEvolution()

View File

@ -1,8 +1,11 @@
package net.corda.nodeapi.internal.serialization.amqp
import net.corda.core.serialization.ConstructorForDeserialization
import net.corda.nodeapi.internal.serialization.amqp.testutils.testDefaultFactoryNoEvolution
import org.assertj.core.api.Assertions
import org.junit.Test
import net.corda.nodeapi.internal.serialization.amqp.testutils.serialize
import net.corda.nodeapi.internal.serialization.amqp.testutils.deserialize
class RoundTripTests {
@Test

View File

@ -23,6 +23,7 @@ import net.corda.nodeapi.internal.DEV_INTERMEDIATE_CA
import net.corda.nodeapi.internal.crypto.ContentSignerBuilder
import net.corda.nodeapi.internal.serialization.*
import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory.Companion.isPrimitive
import net.corda.nodeapi.internal.serialization.amqp.testutils.*
import net.corda.testing.contracts.DummyContract
import net.corda.testing.core.BOB_NAME
import net.corda.testing.core.SerializationEnvironmentRule
@ -480,12 +481,12 @@ class SerializationOutputTests(private val compression: CordaSerializationEncodi
// Double check
copy[valueIndex] = 0x03
assertThat(des.deserialize(OpaqueBytes(copy), NonZeroByte::class.java).value).isEqualTo(3)
assertThat(des.deserialize(OpaqueBytes(copy), NonZeroByte::class.java, testSerializationContext).value).isEqualTo(3)
// Now use the forbidden value
copy[valueIndex] = 0x00
assertThatExceptionOfType(NotSerializableException::class.java).isThrownBy {
des.deserialize(OpaqueBytes(copy), NonZeroByte::class.java)
des.deserialize(OpaqueBytes(copy), NonZeroByte::class.java, testSerializationContext)
}.withMessageContaining("Zero not allowed")
}
@ -650,14 +651,16 @@ class SerializationOutputTests(private val compression: CordaSerializationEncodi
val scheme = AMQPServerSerializationScheme(emptyList())
val func = scheme::class.superclasses.single { it.simpleName == "AbstractAMQPSerializationScheme" }
.java.getDeclaredMethod("registerCustomSerializers", SerializerFactory::class.java)
.java.getDeclaredMethod("registerCustomSerializers",
SerializationContext::class.java,
SerializerFactory::class.java)
func.isAccessible = true
val factory = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader())
func.invoke(scheme, factory)
func.invoke(scheme, testSerializationContext, factory)
val factory2 = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader())
func.invoke(scheme, factory2)
func.invoke(scheme, testSerializationContext, factory2)
val desState = serdes(state, factory, factory2, expectedEqual = false, expectDeserializedEqual = false)
assertTrue((desState as TransactionState<*>).data is FooState)

View File

@ -1,6 +1,8 @@
package net.corda.nodeapi.internal.serialization.amqp
import net.corda.core.serialization.ConstructorForDeserialization
import net.corda.nodeapi.internal.serialization.amqp.testutils.TestSerializationOutput
import net.corda.nodeapi.internal.serialization.amqp.testutils.testDefaultFactoryNoEvolution
import org.junit.Test
import java.util.concurrent.ConcurrentHashMap
import kotlin.test.assertEquals
@ -8,6 +10,10 @@ import org.apache.qpid.proton.amqp.Symbol
import java.lang.reflect.Method
import kotlin.test.assertNotNull
import kotlin.test.assertTrue
import net.corda.nodeapi.internal.serialization.amqp.testutils.serializeAndReturnSchema
import net.corda.nodeapi.internal.serialization.amqp.testutils.serialize
import net.corda.nodeapi.internal.serialization.amqp.testutils.deserializeAndReturnEnvelope
import net.corda.nodeapi.internal.serialization.amqp.testutils.deserialize
class SerializationPropertyOrdering {
companion object {

View File

@ -1,9 +1,12 @@
package net.corda.nodeapi.internal.serialization.amqp
import net.corda.nodeapi.internal.serialization.amqp.testutils.testDefaultFactoryNoEvolution
import net.corda.nodeapi.internal.serialization.amqp.testutils.testName
import org.junit.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertTrue
import net.corda.nodeapi.internal.serialization.amqp.testutils.serializeAndReturnSchema
class SerializeAndReturnSchemaTest {
// the 'this' reference means we can't just move this to the common test utils

View File

@ -10,6 +10,10 @@ import java.io.NotSerializableException
import java.lang.reflect.Type
import java.util.concurrent.ConcurrentHashMap
import kotlin.test.assertEquals
import net.corda.nodeapi.internal.serialization.amqp.testutils.serializeAndReturnSchema
import net.corda.nodeapi.internal.serialization.amqp.testutils.serialize
import net.corda.nodeapi.internal.serialization.amqp.testutils.deserializeAndReturnEnvelope
import net.corda.nodeapi.internal.serialization.amqp.testutils.deserialize
class InStatic : Exception("Help!, help!, I'm being repressed")

View File

@ -0,0 +1,67 @@
package net.corda.nodeapi.internal.serialization.amqp.testutils
import net.corda.core.serialization.SerializationContext
import net.corda.core.serialization.SerializedBytes
import org.apache.qpid.proton.codec.Data
import net.corda.nodeapi.internal.serialization.AllWhitelist
import net.corda.nodeapi.internal.serialization.EmptyWhitelist
import net.corda.nodeapi.internal.serialization.amqp.*
import java.io.NotSerializableException
fun testDefaultFactory() = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader())
fun testDefaultFactoryNoEvolution() = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader(),
EvolutionSerializerGetterTesting())
fun testDefaultFactoryWithWhitelist() = SerializerFactory(EmptyWhitelist, ClassLoader.getSystemClassLoader())
class TestSerializationOutput(
private val verbose: Boolean,
serializerFactory: SerializerFactory = testDefaultFactory())
: SerializationOutput(serializerFactory) {
override fun writeSchema(schema: Schema, data: Data) {
if (verbose) println(schema)
super.writeSchema(schema, data)
}
override fun writeTransformSchema(transformsSchema: TransformsSchema, data: Data) {
if(verbose) {
println ("Writing Transform Schema")
println (transformsSchema)
}
super.writeTransformSchema(transformsSchema, data)
}
}
fun testName(): String = Thread.currentThread().stackTrace[2].methodName
@Throws(NotSerializableException::class)
inline fun <reified T : Any> DeserializationInput.deserializeAndReturnEnvelope(
bytes: SerializedBytes<T>,
context: SerializationContext? = null
) : ObjectAndEnvelope<T> {
return deserializeAndReturnEnvelope(bytes, T::class.java,
context ?: testSerializationContext)
}
@Throws(NotSerializableException::class)
inline fun <reified T : Any> DeserializationInput.deserialize(
bytes: SerializedBytes<T>,
context: SerializationContext? = null
) : T = deserialize(bytes, T::class.java, context ?: testSerializationContext)
@Throws(NotSerializableException::class)
fun <T : Any> SerializationOutput.serializeAndReturnSchema(
obj: T, context: SerializationContext? = null
): BytesAndSchemas<T> = serializeAndReturnSchema(obj, context ?: testSerializationContext)
@Throws(NotSerializableException::class)
fun <T : Any> SerializationOutput.serialize(obj: T): SerializedBytes<T> {
try {
return _serialize(obj, testSerializationContext)
} finally {
andFinally()
}
}

View File

@ -0,0 +1,17 @@
package net.corda.nodeapi.internal.serialization.amqp.testutils
import net.corda.core.serialization.SerializationContext
import net.corda.nodeapi.internal.serialization.AllWhitelist
import net.corda.nodeapi.internal.serialization.SerializationContextImpl
import net.corda.nodeapi.internal.serialization.amqp.amqpMagic
val serializationProperties: MutableMap<Any, Any> = mutableMapOf()
val testSerializationContext = SerializationContextImpl(
preferredSerializationVersion = amqpMagic,
deserializationClassLoader = ClassLoader.getSystemClassLoader(),
whitelist = AllWhitelist,
properties = serializationProperties,
objectReferencesEnabled = false,
useCase = SerializationContext.UseCase.Testing,
encoding = null)

View File

@ -4,6 +4,8 @@ import com.google.common.reflect.TypeToken
import junit.framework.Assert.assertTrue
import net.corda.nodeapi.internal.serialization.AllWhitelist
import net.corda.nodeapi.internal.serialization.amqp.*
import net.corda.nodeapi.internal.serialization.amqp.testutils.TestSerializationOutput
import net.corda.nodeapi.internal.serialization.amqp.testutils.testDefaultFactory
import org.assertj.core.api.Assertions
import org.junit.Test
import java.io.NotSerializableException
@ -11,6 +13,8 @@ import java.lang.reflect.Type
import java.net.URL
import kotlin.reflect.jvm.jvmName
import kotlin.test.assertEquals
import net.corda.nodeapi.internal.serialization.amqp.testutils.serialize
import net.corda.nodeapi.internal.serialization.amqp.testutils.deserialize
// Simple way to ensure we end up trying to carpent a class, "remove" it from the class loader (if only
// actually doing that was simple)

View File

@ -4,7 +4,7 @@ import net.corda.core.serialization.ClassWhitelist
import net.corda.nodeapi.internal.serialization.amqp.*
import net.corda.nodeapi.internal.serialization.amqp.Field
import net.corda.nodeapi.internal.serialization.amqp.Schema
import net.corda.nodeapi.internal.serialization.AllWhitelist
import net.corda.nodeapi.internal.serialization.amqp.testutils.serialize
fun mangleName(name: String) = "${name}__carpenter"

View File

@ -8,6 +8,7 @@ import org.junit.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertTrue
import net.corda.nodeapi.internal.serialization.amqp.testutils.deserializeAndReturnEnvelope
@CordaSerializable
interface I_ {

View File

@ -5,6 +5,7 @@ import net.corda.nodeapi.internal.serialization.AllWhitelist
import net.corda.nodeapi.internal.serialization.amqp.DeserializationInput
import org.junit.Test
import kotlin.test.*
import net.corda.nodeapi.internal.serialization.amqp.testutils.deserializeAndReturnEnvelope
@CordaSerializable
interface J {

View File

@ -7,6 +7,7 @@ import net.corda.nodeapi.internal.serialization.amqp.DeserializationInput
import org.junit.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotEquals
import net.corda.nodeapi.internal.serialization.amqp.testutils.deserializeAndReturnEnvelope
class MultiMemberCompositeSchemaToClassCarpenterTests : AmqpCarpenterBase(AllWhitelist) {

View File

@ -6,6 +6,7 @@ import net.corda.nodeapi.internal.serialization.amqp.CompositeType
import net.corda.nodeapi.internal.serialization.amqp.DeserializationInput
import org.junit.Test
import kotlin.test.assertEquals
import net.corda.nodeapi.internal.serialization.amqp.testutils.deserializeAndReturnEnvelope
class SingleMemberCompositeSchemaToClassCarpenterTests : AmqpCarpenterBase(AllWhitelist) {
@Test