mirror of
https://github.com/corda/corda.git
synced 2024-12-18 12:46:29 +00:00
ENT-12070 AMQP Serialisation performance improvements (#7778)
* Serialization performance test of creating wire transaction * Initial serialization refactoring to enable future caching of schema * Add caching of schema * Move encoder pool to the companion object so it actually gets re-used! * Slightly better cache concurrency for LocalSerializerFactory * Upgrade grgit to 4.1.1 as 4.0.0 seems to have vanished
This commit is contained in:
parent
d721bb7f3e
commit
852127c648
@ -919,4 +919,26 @@ class CashTests {
|
|||||||
|
|
||||||
assertEquals(2, wtx.commands.size)
|
assertEquals(2, wtx.commands.size)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(timeout = 300_000)
|
||||||
|
fun performanceTest() {
|
||||||
|
val tx = TransactionBuilder(dummyNotary.party)
|
||||||
|
database.transaction {
|
||||||
|
val payments = listOf(
|
||||||
|
PartyAndAmount(miniCorpAnonymised, 400.DOLLARS),
|
||||||
|
PartyAndAmount(charlie.party.anonymise(), 150.DOLLARS)
|
||||||
|
)
|
||||||
|
CashUtils.generateSpend(ourServices, tx, payments, ourServices.myInfo.singleIdentityAndCert())
|
||||||
|
}
|
||||||
|
val counts = 1000
|
||||||
|
val loops = 50
|
||||||
|
for (loop in 0 until loops) {
|
||||||
|
val start = System.nanoTime()
|
||||||
|
for (count in 0 until counts) {
|
||||||
|
tx.toWireTransaction(ourServices)
|
||||||
|
}
|
||||||
|
val end = System.nanoTime()
|
||||||
|
println("Time per transaction serialize on loop $loop = ${(end - start) / counts} nanoseconds")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -775,6 +775,34 @@ class SerializationOutputTests(private val compression: CordaSerializationEncodi
|
|||||||
assertEquals(desState.encumbrance, state.encumbrance)
|
assertEquals(desState.encumbrance, state.encumbrance)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(timeout = 300_000)
|
||||||
|
fun performanceTest() {
|
||||||
|
val state = TransactionState(FooState(), FOO_PROGRAM_ID, MEGA_CORP)
|
||||||
|
val scheme = AMQPServerSerializationScheme(emptyList())
|
||||||
|
val func = scheme::class.superclasses.single { it.simpleName == "AbstractAMQPSerializationScheme" }
|
||||||
|
.java.getDeclaredMethod("registerCustomSerializers",
|
||||||
|
SerializationContext::class.java,
|
||||||
|
SerializerFactory::class.java)
|
||||||
|
func.isAccessible = true
|
||||||
|
|
||||||
|
val factory = SerializerFactoryBuilder.build(AllWhitelist,
|
||||||
|
ClassCarpenterImpl(AllWhitelist, ClassLoader.getSystemClassLoader())
|
||||||
|
)
|
||||||
|
func.invoke(scheme, testSerializationContext, factory)
|
||||||
|
val ser = SerializationOutput(factory)
|
||||||
|
|
||||||
|
val counts = 1000
|
||||||
|
val loops = 50
|
||||||
|
for (loop in 0 until loops) {
|
||||||
|
val start = System.nanoTime()
|
||||||
|
for (count in 0 until counts) {
|
||||||
|
ser.serialize(state, compression)
|
||||||
|
}
|
||||||
|
val end = System.nanoTime()
|
||||||
|
println("Time per transaction state serialize on loop $loop = ${(end - start) / counts} nanoseconds")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test(timeout=300_000)
|
@Test(timeout=300_000)
|
||||||
fun `test currencies serialize`() {
|
fun `test currencies serialize`() {
|
||||||
val factory = SerializerFactoryBuilder.build(AllWhitelist,
|
val factory = SerializerFactoryBuilder.build(AllWhitelist,
|
||||||
|
@ -5,12 +5,15 @@ import net.corda.core.serialization.ClassWhitelist
|
|||||||
import net.corda.core.utilities.contextLogger
|
import net.corda.core.utilities.contextLogger
|
||||||
import net.corda.core.utilities.debug
|
import net.corda.core.utilities.debug
|
||||||
import net.corda.core.utilities.trace
|
import net.corda.core.utilities.trace
|
||||||
import net.corda.serialization.internal.model.*
|
import net.corda.serialization.internal.model.FingerPrinter
|
||||||
import net.corda.serialization.internal.model.TypeIdentifier.*
|
import net.corda.serialization.internal.model.LocalTypeInformation
|
||||||
|
import net.corda.serialization.internal.model.LocalTypeModel
|
||||||
|
import net.corda.serialization.internal.model.TypeIdentifier
|
||||||
|
import net.corda.serialization.internal.model.TypeIdentifier.Parameterised
|
||||||
import org.apache.qpid.proton.amqp.Symbol
|
import org.apache.qpid.proton.amqp.Symbol
|
||||||
import java.lang.reflect.ParameterizedType
|
import java.lang.reflect.ParameterizedType
|
||||||
import java.lang.reflect.Type
|
import java.lang.reflect.Type
|
||||||
import java.util.*
|
import java.util.Optional
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
import java.util.function.Function
|
import java.util.function.Function
|
||||||
import java.util.function.Predicate
|
import java.util.function.Predicate
|
||||||
@ -82,6 +85,8 @@ interface LocalSerializerFactory {
|
|||||||
* when serialising and deserialising.
|
* when serialising and deserialising.
|
||||||
*/
|
*/
|
||||||
fun isSuitableForObjectReference(type: Type): Boolean
|
fun isSuitableForObjectReference(type: Type): Boolean
|
||||||
|
|
||||||
|
fun getCachedSchema(types: Set<TypeNotation>): Pair<Schema, TransformsSchema>
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -277,4 +282,24 @@ class DefaultLocalSerializerFactory(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val schemaCache = ConcurrentHashMap<Set<TypeNotation>, Pair<Schema, TransformsSchema>>()
|
||||||
|
|
||||||
|
override fun getCachedSchema(types: Set<TypeNotation>): Pair<Schema, TransformsSchema> {
|
||||||
|
val cacheKey = CachingSet(types)
|
||||||
|
return schemaCache.getOrPut(cacheKey) {
|
||||||
|
val schema = Schema(cacheKey.toList())
|
||||||
|
schema to TransformsSchema.build(schema, this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class CachingSet<T>(exisitingSet: Set<T>) : LinkedHashSet<T>(exisitingSet) {
|
||||||
|
override val size: Int = super.size
|
||||||
|
private val hashCode = super.hashCode()
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
return hashCode
|
||||||
|
}
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
return super.equals(other)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,83 @@
|
|||||||
|
package net.corda.serialization.internal.amqp
|
||||||
|
|
||||||
|
import org.apache.qpid.proton.codec.ReadableBuffer
|
||||||
|
import org.apache.qpid.proton.codec.WritableBuffer
|
||||||
|
import java.io.OutputStream
|
||||||
|
import java.nio.ByteBuffer
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is just a wrapper around an [OutputStream] for Proton-J Encoder. Only the methods
|
||||||
|
* we are actively using are implemented and tested.
|
||||||
|
*/
|
||||||
|
@Suppress("MagicNumber")
|
||||||
|
class OutputStreamWritableBuffer(private val stream: OutputStream) : WritableBuffer {
|
||||||
|
private val writeBuffer = ByteArray(8)
|
||||||
|
|
||||||
|
override fun put(b: Byte) {
|
||||||
|
stream.write(b.toInt())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun put(src: ByteArray, offset: Int, length: Int) {
|
||||||
|
stream.write(src, offset, length)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun put(payload: ByteBuffer) {
|
||||||
|
throw UnsupportedOperationException()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun put(payload: ReadableBuffer?) {
|
||||||
|
throw UnsupportedOperationException()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun putFloat(f: Float) {
|
||||||
|
throw UnsupportedOperationException()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun putDouble(d: Double) {
|
||||||
|
throw UnsupportedOperationException()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun putShort(s: Short) {
|
||||||
|
throw UnsupportedOperationException()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun putInt(i: Int) {
|
||||||
|
writeBuffer[0] = (i ushr 24).toByte()
|
||||||
|
writeBuffer[1] = (i ushr 16).toByte()
|
||||||
|
writeBuffer[2] = (i ushr 8).toByte()
|
||||||
|
writeBuffer[3] = (i ushr 0).toByte()
|
||||||
|
put(writeBuffer, 0, 4)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun putLong(v: Long) {
|
||||||
|
writeBuffer[0] = (v ushr 56).toByte()
|
||||||
|
writeBuffer[1] = (v ushr 48).toByte()
|
||||||
|
writeBuffer[2] = (v ushr 40).toByte()
|
||||||
|
writeBuffer[3] = (v ushr 32).toByte()
|
||||||
|
writeBuffer[4] = (v ushr 24).toByte()
|
||||||
|
writeBuffer[5] = (v ushr 16).toByte()
|
||||||
|
writeBuffer[6] = (v ushr 8).toByte()
|
||||||
|
writeBuffer[7] = (v ushr 0).toByte()
|
||||||
|
put(writeBuffer, 0, 8)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hasRemaining(): Boolean {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun remaining(): Int {
|
||||||
|
throw UnsupportedOperationException()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun position(): Int {
|
||||||
|
throw UnsupportedOperationException()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun position(position: Int) {
|
||||||
|
throw UnsupportedOperationException()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun limit(): Int {
|
||||||
|
throw UnsupportedOperationException()
|
||||||
|
}
|
||||||
|
}
|
@ -11,7 +11,11 @@ import org.apache.qpid.proton.amqp.DescribedType
|
|||||||
import org.apache.qpid.proton.amqp.Symbol
|
import org.apache.qpid.proton.amqp.Symbol
|
||||||
import org.apache.qpid.proton.amqp.UnsignedInteger
|
import org.apache.qpid.proton.amqp.UnsignedInteger
|
||||||
import org.apache.qpid.proton.amqp.UnsignedLong
|
import org.apache.qpid.proton.amqp.UnsignedLong
|
||||||
|
import org.apache.qpid.proton.codec.AMQPType
|
||||||
|
import org.apache.qpid.proton.codec.Data
|
||||||
import org.apache.qpid.proton.codec.DescribedTypeConstructor
|
import org.apache.qpid.proton.codec.DescribedTypeConstructor
|
||||||
|
import org.apache.qpid.proton.codec.EncoderImpl
|
||||||
|
import org.apache.qpid.proton.codec.TypeEncoding
|
||||||
import java.io.NotSerializableException
|
import java.io.NotSerializableException
|
||||||
import java.lang.reflect.Type
|
import java.lang.reflect.Type
|
||||||
|
|
||||||
@ -50,7 +54,7 @@ private class RedescribedType(
|
|||||||
* This and the classes below are OO representations of the AMQP XML schema described in the specification. Their
|
* This and the classes below are OO representations of the AMQP XML schema described in the specification. Their
|
||||||
* [toString] representations generate the associated XML form.
|
* [toString] representations generate the associated XML form.
|
||||||
*/
|
*/
|
||||||
data class Schema(val types: List<TypeNotation>) : DescribedType {
|
data class Schema(val types: List<TypeNotation>) : CachingDescribedType, DescribedType {
|
||||||
companion object : DescribedTypeConstructor<Schema> {
|
companion object : DescribedTypeConstructor<Schema> {
|
||||||
val DESCRIPTOR = AMQPDescriptorRegistry.SCHEMA.amqpDescriptor
|
val DESCRIPTOR = AMQPDescriptorRegistry.SCHEMA.amqpDescriptor
|
||||||
|
|
||||||
@ -74,8 +78,78 @@ data class Schema(val types: List<TypeNotation>) : DescribedType {
|
|||||||
override fun getDescriptor(): Any = DESCRIPTOR
|
override fun getDescriptor(): Any = DESCRIPTOR
|
||||||
|
|
||||||
override fun getDescribed(): Any = listOf(types)
|
override fun getDescribed(): Any = listOf(types)
|
||||||
|
|
||||||
override fun toString(): String = types.joinToString("\n")
|
override fun toString(): String = types.joinToString("\n")
|
||||||
|
override val bytes: ByteArray by lazy {
|
||||||
|
val data = Data.Factory.create()
|
||||||
|
data.putObject(this)
|
||||||
|
data.encode().array
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CachingDescribedType {
|
||||||
|
val bytes: ByteArray
|
||||||
|
}
|
||||||
|
|
||||||
|
class CachingWrapper(dataWriter: (Data) -> Unit) : CachingDescribedType {
|
||||||
|
override val bytes: ByteArray = let {
|
||||||
|
val data = Data.Factory.create()
|
||||||
|
dataWriter(data)
|
||||||
|
data.encode().array
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CachingDescribedAMQPType<T : CachingDescribedType>(private val type: Class<T>, private val encoder: EncoderImpl) : AMQPType<T> {
|
||||||
|
override fun getTypeClass(): Class<T> {
|
||||||
|
return type
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getCanonicalEncoding(): TypeEncoding<T> {
|
||||||
|
throw UnsupportedOperationException()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getAllEncodings(): MutableCollection<out TypeEncoding<T>> {
|
||||||
|
throw UnsupportedOperationException()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun write(obj: T) {
|
||||||
|
val bytes = obj.bytes
|
||||||
|
encoder.buffer.put(bytes, 0, bytes.size)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getEncoding(obj: T): TypeEncoding<T> {
|
||||||
|
return object : TypeEncoding<T> {
|
||||||
|
override fun getType(): AMQPType<T> {
|
||||||
|
return this@CachingDescribedAMQPType
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun writeConstructor() {
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getConstructorSize(): Int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isFixedSizeVal(): Boolean {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun encodesJavaPrimitive(): Boolean {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun encodesSuperset(encoder: TypeEncoding<T>?): Boolean {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getValueSize(obj: T): Int {
|
||||||
|
return obj.bytes.size
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun writeValue(obj: T) {
|
||||||
|
write(obj)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data class Descriptor(val name: Symbol?, val code: UnsignedLong? = null) : DescribedType {
|
data class Descriptor(val name: Symbol?, val code: UnsignedLong? = null) : DescribedType {
|
||||||
@ -215,6 +289,16 @@ data class CompositeType(
|
|||||||
|
|
||||||
override fun getDescribed(): Any = listOf(name, label, provides, descriptor, fields)
|
override fun getDescribed(): Any = listOf(name, label, provides, descriptor, fields)
|
||||||
|
|
||||||
|
private val hashCode = descriptor.hashCode()
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
return hashCode
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if(other !is TypeNotation) return false
|
||||||
|
return descriptor.equals(other.descriptor)
|
||||||
|
}
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
val sb = StringBuilder("<type class=\"composite\" name=\"$name\"")
|
val sb = StringBuilder("<type class=\"composite\" name=\"$name\"")
|
||||||
if (!label.isNullOrBlank()) {
|
if (!label.isNullOrBlank()) {
|
||||||
@ -264,6 +348,16 @@ data class RestrictedType(override val name: String,
|
|||||||
|
|
||||||
override fun getDescribed(): Any = listOf(name, label, provides, source, descriptor, choices)
|
override fun getDescribed(): Any = listOf(name, label, provides, source, descriptor, choices)
|
||||||
|
|
||||||
|
private val hashCode = descriptor.hashCode()
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
return hashCode
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if(other !is TypeNotation) return false
|
||||||
|
return descriptor.equals(other.descriptor)
|
||||||
|
}
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
val sb = StringBuilder("<type class=\"restricted\" name=\"$name\"")
|
val sb = StringBuilder("<type class=\"restricted\" name=\"$name\"")
|
||||||
if (!label.isNullOrBlank()) {
|
if (!label.isNullOrBlank()) {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package net.corda.serialization.internal.amqp
|
package net.corda.serialization.internal.amqp
|
||||||
|
|
||||||
|
import net.corda.core.internal.LazyPool
|
||||||
import net.corda.core.serialization.SerializationContext
|
import net.corda.core.serialization.SerializationContext
|
||||||
import net.corda.core.serialization.SerializedBytes
|
import net.corda.core.serialization.SerializedBytes
|
||||||
import net.corda.core.utilities.contextLogger
|
import net.corda.core.utilities.contextLogger
|
||||||
@ -8,12 +9,13 @@ import net.corda.serialization.internal.SectionId
|
|||||||
import net.corda.serialization.internal.byteArrayOutput
|
import net.corda.serialization.internal.byteArrayOutput
|
||||||
import net.corda.serialization.internal.model.TypeIdentifier
|
import net.corda.serialization.internal.model.TypeIdentifier
|
||||||
import org.apache.qpid.proton.codec.Data
|
import org.apache.qpid.proton.codec.Data
|
||||||
|
import org.apache.qpid.proton.codec.DecoderImpl
|
||||||
|
import org.apache.qpid.proton.codec.EncoderImpl
|
||||||
import java.io.NotSerializableException
|
import java.io.NotSerializableException
|
||||||
import java.io.OutputStream
|
import java.io.OutputStream
|
||||||
import java.lang.reflect.Type
|
import java.lang.reflect.Type
|
||||||
import java.lang.reflect.WildcardType
|
import java.lang.reflect.WildcardType
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.collections.LinkedHashSet
|
|
||||||
|
|
||||||
data class BytesAndSchemas<T : Any>(
|
data class BytesAndSchemas<T : Any>(
|
||||||
val obj: SerializedBytes<T>,
|
val obj: SerializedBytes<T>,
|
||||||
@ -31,6 +33,15 @@ open class SerializationOutput constructor(
|
|||||||
) {
|
) {
|
||||||
companion object {
|
companion object {
|
||||||
private val logger = contextLogger()
|
private val logger = contextLogger()
|
||||||
|
|
||||||
|
private val encoderPool = LazyPool<EncoderImpl> {
|
||||||
|
EncoderImpl(DecoderImpl()).apply {
|
||||||
|
registerDescribedType(Envelope::class.java, Envelope.DESCRIPTOR)
|
||||||
|
register(CachingDescribedAMQPType(CachingWrapper::class.java, this))
|
||||||
|
register(CachingDescribedAMQPType(Schema::class.java, this))
|
||||||
|
register(CachingDescribedAMQPType(TransformsSchema::class.java, this))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val objectHistory: MutableMap<Any, Int> = IdentityHashMap()
|
private val objectHistory: MutableMap<Any, Int> = IdentityHashMap()
|
||||||
@ -74,15 +85,6 @@ open class SerializationOutput constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal fun <T : Any> _serialize(obj: T, context: SerializationContext): 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, context)
|
|
||||||
val schema = Schema(schemaHistory.toList())
|
|
||||||
writeSchema(schema, this)
|
|
||||||
writeTransformSchema(TransformsSchema.build(schema, serializerFactory), this)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return SerializedBytes(byteArrayOutput {
|
return SerializedBytes(byteArrayOutput {
|
||||||
var stream: OutputStream = it
|
var stream: OutputStream = it
|
||||||
try {
|
try {
|
||||||
@ -94,7 +96,16 @@ open class SerializationOutput constructor(
|
|||||||
stream = encoding.wrap(stream)
|
stream = encoding.wrap(stream)
|
||||||
}
|
}
|
||||||
SectionId.DATA_AND_STOP.writeTo(stream)
|
SectionId.DATA_AND_STOP.writeTo(stream)
|
||||||
stream.alsoAsByteBuffer(data.encodedSize().toInt(), data::encode)
|
encoderPool.reentrantRun { encoderImpl ->
|
||||||
|
val previousBuffer = encoderImpl.buffer
|
||||||
|
encoderImpl.setByteBuffer(OutputStreamWritableBuffer(stream))
|
||||||
|
encoderImpl.writeObject(Envelope(CachingWrapper { data ->
|
||||||
|
writeObject(obj, data, context)
|
||||||
|
}) {
|
||||||
|
serializerFactory.getCachedSchema(schemaHistory)
|
||||||
|
})
|
||||||
|
encoderImpl.setByteBuffer(previousBuffer)
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
stream.close()
|
stream.close()
|
||||||
}
|
}
|
||||||
@ -105,14 +116,6 @@ open class SerializationOutput constructor(
|
|||||||
writeObject(obj, data, obj.javaClass, context)
|
writeObject(obj, data, obj.javaClass, context)
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun writeSchema(schema: Schema, data: Data) {
|
|
||||||
data.putObject(schema)
|
|
||||||
}
|
|
||||||
|
|
||||||
open fun writeTransformSchema(transformsSchema: TransformsSchema, data: Data) {
|
|
||||||
data.putObject(transformsSchema)
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun writeObjectOrNull(obj: Any?, data: Data, type: Type, context: SerializationContext, debugIndent: Int) {
|
internal fun writeObjectOrNull(obj: Any?, data: Data, type: Type, context: SerializationContext, debugIndent: Int) {
|
||||||
if (obj == null) {
|
if (obj == null) {
|
||||||
data.putNull()
|
data.putNull()
|
||||||
|
@ -4,9 +4,10 @@ import net.corda.core.serialization.CordaSerializationTransformEnumDefault
|
|||||||
import net.corda.core.serialization.CordaSerializationTransformRename
|
import net.corda.core.serialization.CordaSerializationTransformRename
|
||||||
import net.corda.serialization.internal.model.LocalTypeInformation
|
import net.corda.serialization.internal.model.LocalTypeInformation
|
||||||
import org.apache.qpid.proton.amqp.DescribedType
|
import org.apache.qpid.proton.amqp.DescribedType
|
||||||
|
import org.apache.qpid.proton.codec.Data
|
||||||
import org.apache.qpid.proton.codec.DescribedTypeConstructor
|
import org.apache.qpid.proton.codec.DescribedTypeConstructor
|
||||||
import java.io.NotSerializableException
|
import java.io.NotSerializableException
|
||||||
import java.util.*
|
import java.util.EnumMap
|
||||||
|
|
||||||
// NOTE: We are effectively going to replicate the annotations, we need to do this because
|
// NOTE: We are effectively going to replicate the annotations, we need to do this because
|
||||||
// we can't instantiate instances of those annotation classes and this code needs to
|
// we can't instantiate instances of those annotation classes and this code needs to
|
||||||
@ -243,7 +244,7 @@ object TransformsAnnotationProcessor {
|
|||||||
* @property types maps class names to a map of transformation types. In turn those transformation types
|
* @property types maps class names to a map of transformation types. In turn those transformation types
|
||||||
* are each a list of instances o that transform.
|
* are each a list of instances o that transform.
|
||||||
*/
|
*/
|
||||||
data class TransformsSchema(val types: Map<String, EnumMap<TransformTypes, MutableList<Transform>>>) : DescribedType {
|
data class TransformsSchema(val types: Map<String, EnumMap<TransformTypes, MutableList<Transform>>>) : CachingDescribedType, DescribedType {
|
||||||
companion object : DescribedTypeConstructor<TransformsSchema> {
|
companion object : DescribedTypeConstructor<TransformsSchema> {
|
||||||
val DESCRIPTOR = AMQPDescriptorRegistry.TRANSFORM_SCHEMA.amqpDescriptor
|
val DESCRIPTOR = AMQPDescriptorRegistry.TRANSFORM_SCHEMA.amqpDescriptor
|
||||||
|
|
||||||
@ -341,6 +342,12 @@ data class TransformsSchema(val types: Map<String, EnumMap<TransformTypes, Mutab
|
|||||||
|
|
||||||
return sb.toString()
|
return sb.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override val bytes: ByteArray by lazy {
|
||||||
|
val data = Data.Factory.create()
|
||||||
|
data.putObject(this)
|
||||||
|
data.encode().array
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun String.esc() = "\"$this\""
|
private fun String.esc() = "\"$this\""
|
||||||
|
@ -0,0 +1,54 @@
|
|||||||
|
package net.corda.serialization.internal.amqp
|
||||||
|
|
||||||
|
import org.apache.qpid.proton.codec.ReadableBuffer.ByteBufferReader
|
||||||
|
import org.junit.Test
|
||||||
|
import java.io.ByteArrayOutputStream
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
|
class OutputStreamWritableBufferTests {
|
||||||
|
|
||||||
|
@Test(timeout = 300_000)
|
||||||
|
fun testByte() {
|
||||||
|
val stream = ByteArrayOutputStream()
|
||||||
|
val buffer = OutputStreamWritableBuffer(stream)
|
||||||
|
var b = Byte.MIN_VALUE
|
||||||
|
while (b <= Byte.MAX_VALUE) {
|
||||||
|
buffer.put(b)
|
||||||
|
if (b == Byte.MAX_VALUE) break
|
||||||
|
b++
|
||||||
|
}
|
||||||
|
stream.close()
|
||||||
|
|
||||||
|
b = Byte.MIN_VALUE
|
||||||
|
val bytes = stream.toByteArray()
|
||||||
|
for (byte in bytes) {
|
||||||
|
assertEquals(b++, byte)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(timeout = 300_000)
|
||||||
|
fun testInt() {
|
||||||
|
val stream = ByteArrayOutputStream()
|
||||||
|
val buffer = OutputStreamWritableBuffer(stream)
|
||||||
|
buffer.putInt(Int.MIN_VALUE)
|
||||||
|
buffer.putInt(Int.MAX_VALUE)
|
||||||
|
stream.close()
|
||||||
|
|
||||||
|
val reader = ByteBufferReader.wrap(stream.toByteArray())
|
||||||
|
assertEquals(Int.MIN_VALUE, reader.int)
|
||||||
|
assertEquals(Int.MAX_VALUE, reader.int)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(timeout = 300_000)
|
||||||
|
fun testLong() {
|
||||||
|
val stream = ByteArrayOutputStream()
|
||||||
|
val buffer = OutputStreamWritableBuffer(stream)
|
||||||
|
buffer.putLong(Long.MIN_VALUE)
|
||||||
|
buffer.putLong(Long.MAX_VALUE)
|
||||||
|
stream.close()
|
||||||
|
|
||||||
|
val reader = ByteBufferReader.wrap(stream.toByteArray())
|
||||||
|
assertEquals(Long.MIN_VALUE, reader.long)
|
||||||
|
assertEquals(Long.MAX_VALUE, reader.long)
|
||||||
|
}
|
||||||
|
}
|
@ -9,7 +9,6 @@ import net.corda.serialization.internal.AllWhitelist
|
|||||||
import net.corda.serialization.internal.EmptyWhitelist
|
import net.corda.serialization.internal.EmptyWhitelist
|
||||||
import net.corda.serialization.internal.amqp.*
|
import net.corda.serialization.internal.amqp.*
|
||||||
import net.corda.serialization.internal.carpenter.ClassCarpenterImpl
|
import net.corda.serialization.internal.carpenter.ClassCarpenterImpl
|
||||||
import org.apache.qpid.proton.codec.Data
|
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.io.File.separatorChar
|
import java.io.File.separatorChar
|
||||||
import java.io.NotSerializableException
|
import java.io.NotSerializableException
|
||||||
@ -63,19 +62,6 @@ class TestSerializationOutput(
|
|||||||
serializerFactory: SerializerFactory = testDefaultFactory())
|
serializerFactory: SerializerFactory = testDefaultFactory())
|
||||||
: SerializationOutput(serializerFactory) {
|
: 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)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Throws(NotSerializableException::class)
|
@Throws(NotSerializableException::class)
|
||||||
fun <T : Any> serialize(obj: T): SerializedBytes<T> {
|
fun <T : Any> serialize(obj: T): SerializedBytes<T> {
|
||||||
try {
|
try {
|
||||||
|
Loading…
Reference in New Issue
Block a user