mirror of
https://github.com/corda/corda.git
synced 2024-12-22 06:17:55 +00:00
Some additional serializers to fill in the blanks vs. our default whitelist (#1490)
This commit is contained in:
parent
b102900c90
commit
988e3e5007
@ -64,7 +64,7 @@ abstract class SerializationFactory {
|
|||||||
val priorContext = _currentFactory.get()
|
val priorContext = _currentFactory.get()
|
||||||
_currentFactory.set(this)
|
_currentFactory.set(this)
|
||||||
try {
|
try {
|
||||||
return block()
|
return this.block()
|
||||||
} finally {
|
} finally {
|
||||||
_currentFactory.set(priorContext)
|
_currentFactory.set(priorContext)
|
||||||
}
|
}
|
||||||
|
@ -54,6 +54,11 @@ abstract class AbstractAMQPSerializationScheme : SerializationScheme {
|
|||||||
register(net.corda.nodeapi.internal.serialization.amqp.custom.ClassSerializer(this))
|
register(net.corda.nodeapi.internal.serialization.amqp.custom.ClassSerializer(this))
|
||||||
register(net.corda.nodeapi.internal.serialization.amqp.custom.X509CertificateHolderSerializer)
|
register(net.corda.nodeapi.internal.serialization.amqp.custom.X509CertificateHolderSerializer)
|
||||||
register(net.corda.nodeapi.internal.serialization.amqp.custom.PartyAndCertificateSerializer(factory))
|
register(net.corda.nodeapi.internal.serialization.amqp.custom.PartyAndCertificateSerializer(factory))
|
||||||
|
register(net.corda.nodeapi.internal.serialization.amqp.custom.StringBufferSerializer)
|
||||||
|
register(net.corda.nodeapi.internal.serialization.amqp.custom.SimpleStringSerializer)
|
||||||
|
register(net.corda.nodeapi.internal.serialization.amqp.custom.InputStreamSerializer)
|
||||||
|
register(net.corda.nodeapi.internal.serialization.amqp.custom.BitSetSerializer(this))
|
||||||
|
register(net.corda.nodeapi.internal.serialization.amqp.custom.EnumSetSerializer(this))
|
||||||
}
|
}
|
||||||
val customizer = AMQPSerializationCustomization(factory)
|
val customizer = AMQPSerializationCustomization(factory)
|
||||||
pluginRegistries.forEach { it.customizeSerialization(customizer) }
|
pluginRegistries.forEach { it.customizeSerialization(customizer) }
|
||||||
|
@ -29,6 +29,11 @@ abstract class CustomSerializer<T> : AMQPSerializer<T> {
|
|||||||
*/
|
*/
|
||||||
abstract val schemaForDocumentation: Schema
|
abstract val schemaForDocumentation: Schema
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether subclasses using this serializer via inheritance should have a mapping in the schema.
|
||||||
|
*/
|
||||||
|
open val revealSubclassesInSchema: Boolean = false
|
||||||
|
|
||||||
override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput) {
|
override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput) {
|
||||||
data.withDescribed(descriptor) {
|
data.withDescribed(descriptor) {
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
@ -86,7 +86,7 @@ class DeserializedParameterizedType(private val rawType: Class<*>, private val p
|
|||||||
if (pos == typeStart) {
|
if (pos == typeStart) {
|
||||||
skippingWhitespace = false
|
skippingWhitespace = false
|
||||||
if (params[pos].isWhitespace()) {
|
if (params[pos].isWhitespace()) {
|
||||||
typeStart = pos++
|
typeStart = ++pos
|
||||||
} else if (!needAType) {
|
} else if (!needAType) {
|
||||||
throw NotSerializableException("Not expecting a type")
|
throw NotSerializableException("Not expecting a type")
|
||||||
} else if (params[pos] == '?') {
|
} else if (params[pos] == '?') {
|
||||||
|
@ -18,7 +18,7 @@ private typealias MapCreationFunction = (Map<*, *>) -> Map<*, *>
|
|||||||
*/
|
*/
|
||||||
class MapSerializer(private val declaredType: ParameterizedType, factory: SerializerFactory) : AMQPSerializer<Any> {
|
class MapSerializer(private 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 = Symbol.valueOf("$DESCRIPTOR_DOMAIN:${fingerprintForType(type, factory)}")
|
override val typeDescriptor: Symbol = Symbol.valueOf("$DESCRIPTOR_DOMAIN:${fingerprintForType(type, factory)}")
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
// NB: Order matters in this map, the most specific classes should be listed at the end
|
// NB: Order matters in this map, the most specific classes should be listed at the end
|
||||||
@ -29,7 +29,11 @@ class MapSerializer(private val declaredType: ParameterizedType, factory: Serial
|
|||||||
NavigableMap::class.java to { map -> Collections.unmodifiableNavigableMap(TreeMap(map)) },
|
NavigableMap::class.java to { map -> Collections.unmodifiableNavigableMap(TreeMap(map)) },
|
||||||
// concrete classes for user convenience
|
// concrete classes for user convenience
|
||||||
LinkedHashMap::class.java to { map -> LinkedHashMap(map) },
|
LinkedHashMap::class.java to { map -> LinkedHashMap(map) },
|
||||||
TreeMap::class.java to { map -> TreeMap(map) }
|
TreeMap::class.java to { map -> TreeMap(map) },
|
||||||
|
EnumMap::class.java to { map ->
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
EnumMap(map as Map<EnumJustUsedForCasting, Any>)
|
||||||
|
}
|
||||||
))
|
))
|
||||||
|
|
||||||
private fun findConcreteType(clazz: Class<*>): MapCreationFunction {
|
private fun findConcreteType(clazz: Class<*>): MapCreationFunction {
|
||||||
@ -95,6 +99,10 @@ class MapSerializer(private val declaredType: ParameterizedType, factory: Serial
|
|||||||
private fun readEntry(schema: Schema, input: DeserializationInput, entry: Map.Entry<Any?, Any?>) =
|
private fun readEntry(schema: Schema, input: DeserializationInput, entry: Map.Entry<Any?, Any?>) =
|
||||||
input.readObjectOrNull(entry.key, schema, declaredType.actualTypeArguments[0]) to
|
input.readObjectOrNull(entry.key, schema, declaredType.actualTypeArguments[0]) to
|
||||||
input.readObjectOrNull(entry.value, schema, declaredType.actualTypeArguments[1])
|
input.readObjectOrNull(entry.value, schema, declaredType.actualTypeArguments[1])
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
internal enum class EnumJustUsedForCasting { NOT_USED }
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun Class<*>.checkSupportedMapType() {
|
internal fun Class<*>.checkSupportedMapType() {
|
||||||
|
@ -468,7 +468,8 @@ private fun fingerprintForType(type: Type, contextType: Type?, alreadySeen: Muta
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isCollectionOrMap(type: Class<*>) = Collection::class.java.isAssignableFrom(type) || Map::class.java.isAssignableFrom(type)
|
private fun isCollectionOrMap(type: Class<*>) = (Collection::class.java.isAssignableFrom(type) || Map::class.java.isAssignableFrom(type)) &&
|
||||||
|
!EnumSet::class.java.isAssignableFrom(type)
|
||||||
|
|
||||||
private fun fingerprintForObject(type: Type, contextType: Type?, alreadySeen: MutableSet<Type>, hasher: Hasher, factory: SerializerFactory): Hasher {
|
private fun fingerprintForObject(type: Type, contextType: Type?, alreadySeen: MutableSet<Type>, hasher: Hasher, factory: SerializerFactory): Hasher {
|
||||||
// Hash the class + properties + interfaces
|
// Hash the class + properties + interfaces
|
||||||
|
@ -13,7 +13,7 @@ import java.util.concurrent.ConcurrentHashMap
|
|||||||
import java.util.concurrent.CopyOnWriteArrayList
|
import java.util.concurrent.CopyOnWriteArrayList
|
||||||
import javax.annotation.concurrent.ThreadSafe
|
import javax.annotation.concurrent.ThreadSafe
|
||||||
|
|
||||||
data class schemaAndDescriptor(val schema: Schema, val typeDescriptor: Any)
|
data class FactorySchemaAndDescriptor(val schema: Schema, val typeDescriptor: Any)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Factory of serializers designed to be shared across threads and invocations.
|
* Factory of serializers designed to be shared across threads and invocations.
|
||||||
@ -22,8 +22,8 @@ data class schemaAndDescriptor(val schema: Schema, val typeDescriptor: Any)
|
|||||||
// TODO: maybe support for caching of serialized form of some core types for performance
|
// TODO: maybe support for caching of serialized form of some core types for performance
|
||||||
// TODO: profile for performance in general
|
// TODO: profile for performance in general
|
||||||
// TODO: use guava caches etc so not unbounded
|
// TODO: use guava caches etc so not unbounded
|
||||||
// TODO: do we need to support a transient annotation to exclude certain properties?
|
|
||||||
// TODO: allow definition of well known types that are left out of the schema.
|
// TODO: allow definition of well known types that are left out of the schema.
|
||||||
|
// TODO: migrate some core types to unsigned integer descriptor
|
||||||
// TODO: document and alert to the fact that classes cannot default superclass/interface properties otherwise they are "erased" due to matching with constructor.
|
// TODO: document and alert to the fact that classes cannot default superclass/interface properties otherwise they are "erased" due to matching with constructor.
|
||||||
// TODO: type name prefixes for interfaces and abstract classes? Or use label?
|
// TODO: type name prefixes for interfaces and abstract classes? Or use label?
|
||||||
// TODO: generic types should define restricted type alias with source of the wildcarded version, I think, if we're to generate classes from schema
|
// TODO: generic types should define restricted type alias with source of the wildcarded version, I think, if we're to generate classes from schema
|
||||||
@ -64,7 +64,8 @@ class SerializerFactory(val whitelist: ClassWhitelist, cl: ClassLoader) {
|
|||||||
// Declared class may not be set to Collection, but actual class could be a collection.
|
// Declared class may not be set to Collection, but actual class could be a collection.
|
||||||
// In this case use of CollectionSerializer is perfectly appropriate.
|
// In this case use of CollectionSerializer is perfectly appropriate.
|
||||||
(Collection::class.java.isAssignableFrom(declaredClass) ||
|
(Collection::class.java.isAssignableFrom(declaredClass) ||
|
||||||
(actualClass != null && Collection::class.java.isAssignableFrom(actualClass))) -> {
|
(actualClass != null && Collection::class.java.isAssignableFrom(actualClass))) &&
|
||||||
|
!EnumSet::class.java.isAssignableFrom(actualClass ?: declaredClass) -> {
|
||||||
val declaredTypeAmended= CollectionSerializer.deriveParameterizedType(declaredType, declaredClass, actualClass)
|
val declaredTypeAmended= CollectionSerializer.deriveParameterizedType(declaredType, declaredClass, actualClass)
|
||||||
serializersByType.computeIfAbsent(declaredTypeAmended) {
|
serializersByType.computeIfAbsent(declaredTypeAmended) {
|
||||||
CollectionSerializer(declaredTypeAmended, this)
|
CollectionSerializer(declaredTypeAmended, this)
|
||||||
@ -79,7 +80,7 @@ class SerializerFactory(val whitelist: ClassWhitelist, cl: ClassLoader) {
|
|||||||
makeMapSerializer(declaredTypeAmended)
|
makeMapSerializer(declaredTypeAmended)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Enum::class.java.isAssignableFrom(declaredClass) -> serializersByType.computeIfAbsent(declaredClass) {
|
Enum::class.java.isAssignableFrom(actualClass ?: declaredClass) -> serializersByType.computeIfAbsent(actualClass ?: declaredClass) {
|
||||||
EnumSerializer(actualType, actualClass ?: declaredClass, this)
|
EnumSerializer(actualType, actualClass ?: declaredClass, this)
|
||||||
}
|
}
|
||||||
else -> makeClassSerializer(actualClass ?: declaredClass, actualType, declaredType)
|
else -> makeClassSerializer(actualClass ?: declaredClass, actualType, declaredType)
|
||||||
@ -164,7 +165,7 @@ class SerializerFactory(val whitelist: ClassWhitelist, cl: ClassLoader) {
|
|||||||
@Throws(NotSerializableException::class)
|
@Throws(NotSerializableException::class)
|
||||||
fun get(typeDescriptor: Any, schema: Schema): AMQPSerializer<Any> {
|
fun get(typeDescriptor: Any, schema: Schema): AMQPSerializer<Any> {
|
||||||
return serializersByDescriptor[typeDescriptor] ?: {
|
return serializersByDescriptor[typeDescriptor] ?: {
|
||||||
processSchema(schemaAndDescriptor(schema, typeDescriptor))
|
processSchema(FactorySchemaAndDescriptor(schema, typeDescriptor))
|
||||||
serializersByDescriptor[typeDescriptor] ?: throw NotSerializableException(
|
serializersByDescriptor[typeDescriptor] ?: throw NotSerializableException(
|
||||||
"Could not find type matching descriptor $typeDescriptor.")
|
"Could not find type matching descriptor $typeDescriptor.")
|
||||||
}()
|
}()
|
||||||
@ -188,7 +189,7 @@ class SerializerFactory(val whitelist: ClassWhitelist, cl: ClassLoader) {
|
|||||||
* Iterate over an AMQP schema, for each type ascertain weather it's on ClassPath of [classloader] amd
|
* Iterate over an AMQP schema, for each type ascertain weather it's on ClassPath of [classloader] amd
|
||||||
* if not use the [ClassCarpenter] to generate a class to use in it's place
|
* if not use the [ClassCarpenter] to generate a class to use in it's place
|
||||||
*/
|
*/
|
||||||
private fun processSchema(schema: schemaAndDescriptor, sentinel: Boolean = false) {
|
private fun processSchema(schema: FactorySchemaAndDescriptor, sentinel: Boolean = false) {
|
||||||
val carpenterSchemas = CarpenterSchemas.newInstance()
|
val carpenterSchemas = CarpenterSchemas.newInstance()
|
||||||
for (typeNotation in schema.schema.types) {
|
for (typeNotation in schema.schema.types) {
|
||||||
try {
|
try {
|
||||||
@ -234,8 +235,7 @@ class SerializerFactory(val whitelist: ClassWhitelist, cl: ClassLoader) {
|
|||||||
} else {
|
} else {
|
||||||
findCustomSerializer(clazz, declaredType) ?: run {
|
findCustomSerializer(clazz, declaredType) ?: run {
|
||||||
if (type.isArray()) {
|
if (type.isArray()) {
|
||||||
// Allow Object[] since this can be quite common (i.e. an untyped array)
|
// Don't need to check the whitelist since each element will come back through the whitelisting process.
|
||||||
if (type.componentType() != Object::class.java) whitelisted(type.componentType())
|
|
||||||
if (clazz.componentType.isPrimitive) PrimArraySerializer.make(type, this)
|
if (clazz.componentType.isPrimitive) PrimArraySerializer.make(type, this)
|
||||||
else ArraySerializer.make(type, this)
|
else ArraySerializer.make(type, this)
|
||||||
} else if (clazz.kotlin.objectInstance != null) {
|
} else if (clazz.kotlin.objectInstance != null) {
|
||||||
@ -256,7 +256,7 @@ class SerializerFactory(val whitelist: ClassWhitelist, cl: ClassLoader) {
|
|||||||
for (customSerializer in customSerializers) {
|
for (customSerializer in customSerializers) {
|
||||||
if (customSerializer.isSerializerFor(clazz)) {
|
if (customSerializer.isSerializerFor(clazz)) {
|
||||||
val declaredSuperClass = declaredType.asClass()?.superclass
|
val declaredSuperClass = declaredType.asClass()?.superclass
|
||||||
if (declaredSuperClass == null || !customSerializer.isSerializerFor(declaredSuperClass)) {
|
if (declaredSuperClass == null || !customSerializer.isSerializerFor(declaredSuperClass) || !customSerializer.revealSubclassesInSchema) {
|
||||||
return customSerializer
|
return customSerializer
|
||||||
} else {
|
} else {
|
||||||
// Make a subclass serializer for the subclass and return that...
|
// Make a subclass serializer for the subclass and return that...
|
||||||
|
@ -0,0 +1,17 @@
|
|||||||
|
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.util.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A serializer that writes out a [BitSet] as an integer number of bits, plus the necessary number of bytes to encode that
|
||||||
|
* many bits.
|
||||||
|
*/
|
||||||
|
class BitSetSerializer(factory: SerializerFactory) : CustomSerializer.Proxy<BitSet, BitSetSerializer.BitSetProxy>(BitSet::class.java, BitSetProxy::class.java, factory) {
|
||||||
|
override fun toProxy(obj: BitSet): BitSetProxy = BitSetProxy(obj.toByteArray())
|
||||||
|
|
||||||
|
override fun fromProxy(proxy: BitSetProxy): BitSet = BitSet.valueOf(proxy.bytes)
|
||||||
|
|
||||||
|
data class BitSetProxy(val bytes: ByteArray)
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
package net.corda.nodeapi.internal.serialization.amqp.custom
|
||||||
|
|
||||||
|
import net.corda.nodeapi.internal.serialization.amqp.CustomSerializer
|
||||||
|
import net.corda.nodeapi.internal.serialization.amqp.MapSerializer
|
||||||
|
import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
/**
|
||||||
|
* A serializer that writes out an [EnumSet] as a type, plus list of instances in the set.
|
||||||
|
*/
|
||||||
|
class EnumSetSerializer(factory: SerializerFactory) : CustomSerializer.Proxy<EnumSet<*>, EnumSetSerializer.EnumSetProxy>(EnumSet::class.java, EnumSetProxy::class.java, factory) {
|
||||||
|
override val additionalSerializers: Iterable<CustomSerializer<out Any>> = listOf(ClassSerializer(factory))
|
||||||
|
|
||||||
|
override fun toProxy(obj: EnumSet<*>): EnumSetProxy = EnumSetProxy(elementType(obj), obj.toList())
|
||||||
|
|
||||||
|
private fun elementType(set: EnumSet<*>): Class<*> {
|
||||||
|
return if (set.isEmpty()) {
|
||||||
|
EnumSet.complementOf(set as EnumSet<MapSerializer.EnumJustUsedForCasting>).first().javaClass
|
||||||
|
} else {
|
||||||
|
set.first().javaClass
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun fromProxy(proxy: EnumSetProxy): EnumSet<*> {
|
||||||
|
return if (proxy.elements.isEmpty()) {
|
||||||
|
EnumSet.noneOf(proxy.clazz as Class<MapSerializer.EnumJustUsedForCasting>)
|
||||||
|
} else {
|
||||||
|
EnumSet.copyOf(proxy.elements as List<MapSerializer.EnumJustUsedForCasting>)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class EnumSetProxy(val clazz: Class<*>, val elements: List<Any>)
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
package net.corda.nodeapi.internal.serialization.amqp.custom
|
||||||
|
|
||||||
|
import net.corda.nodeapi.internal.serialization.amqp.*
|
||||||
|
import org.apache.qpid.proton.amqp.Binary
|
||||||
|
import org.apache.qpid.proton.codec.Data
|
||||||
|
import java.io.ByteArrayInputStream
|
||||||
|
import java.io.InputStream
|
||||||
|
import java.lang.reflect.Type
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A serializer that writes out the content of an input stream as bytes and deserializes into a [ByteArrayInputStream].
|
||||||
|
*/
|
||||||
|
object InputStreamSerializer : CustomSerializer.Implements<InputStream>(InputStream::class.java) {
|
||||||
|
override val revealSubclassesInSchema: Boolean = true
|
||||||
|
|
||||||
|
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) {
|
||||||
|
val startingSize = maxOf(4096, obj.available() + 1)
|
||||||
|
var buffer = ByteArray(startingSize)
|
||||||
|
var pos = 0
|
||||||
|
while (true) {
|
||||||
|
val numberOfBytesRead = obj.read(buffer, pos, buffer.size - pos)
|
||||||
|
if (numberOfBytesRead != -1) {
|
||||||
|
pos += numberOfBytesRead
|
||||||
|
// If the buffer is now full, resize it.
|
||||||
|
if (pos == buffer.size) {
|
||||||
|
buffer = buffer.copyOf(buffer.size + maxOf(4096, obj.available() + 1))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
data.putBinary(Binary(buffer, 0, pos))
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun readObject(obj: Any, schema: Schema, input: DeserializationInput): InputStream {
|
||||||
|
val bits = input.readObject(obj, schema, ByteArray::class.java) as ByteArray
|
||||||
|
return ByteArrayInputStream(bits)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
package net.corda.nodeapi.internal.serialization.amqp.custom
|
||||||
|
|
||||||
|
import net.corda.nodeapi.internal.serialization.amqp.CustomSerializer
|
||||||
|
import org.apache.activemq.artemis.api.core.SimpleString
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A serializer for [SimpleString].
|
||||||
|
*/
|
||||||
|
object SimpleStringSerializer : CustomSerializer.ToString<SimpleString>(SimpleString::class.java)
|
@ -0,0 +1,8 @@
|
|||||||
|
package net.corda.nodeapi.internal.serialization.amqp.custom
|
||||||
|
|
||||||
|
import net.corda.nodeapi.internal.serialization.amqp.CustomSerializer
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A serializer for [StringBuffer].
|
||||||
|
*/
|
||||||
|
object StringBufferSerializer : CustomSerializer.ToString<StringBuffer>(StringBuffer::class.java)
|
@ -13,6 +13,8 @@ class ThrowableSerializer(factory: SerializerFactory) : CustomSerializer.Proxy<T
|
|||||||
private val logger = loggerFor<ThrowableSerializer>()
|
private val logger = loggerFor<ThrowableSerializer>()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override val revealSubclassesInSchema: Boolean = true
|
||||||
|
|
||||||
override val additionalSerializers: Iterable<CustomSerializer<out Any>> = listOf(StackTraceElementSerializer(factory))
|
override val additionalSerializers: Iterable<CustomSerializer<out Any>> = listOf(StackTraceElementSerializer(factory))
|
||||||
|
|
||||||
override fun toProxy(obj: Throwable): ThrowableProxy {
|
override fun toProxy(obj: Throwable): ThrowableProxy {
|
||||||
|
@ -2,12 +2,14 @@ package net.corda.nodeapi.internal.serialization.amqp.custom
|
|||||||
|
|
||||||
import net.corda.nodeapi.internal.serialization.amqp.CustomSerializer
|
import net.corda.nodeapi.internal.serialization.amqp.CustomSerializer
|
||||||
import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory
|
import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory
|
||||||
import java.time.*
|
import java.time.ZoneId
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A serializer for [ZoneId] that uses a proxy object to write out the string form.
|
* A serializer for [ZoneId] that uses a proxy object to write out the string form.
|
||||||
*/
|
*/
|
||||||
class ZoneIdSerializer(factory: SerializerFactory) : CustomSerializer.Proxy<ZoneId, ZoneIdSerializer.ZoneIdProxy>(ZoneId::class.java, ZoneIdProxy::class.java, factory) {
|
class ZoneIdSerializer(factory: SerializerFactory) : CustomSerializer.Proxy<ZoneId, ZoneIdSerializer.ZoneIdProxy>(ZoneId::class.java, ZoneIdProxy::class.java, factory) {
|
||||||
|
override val revealSubclassesInSchema: Boolean = true
|
||||||
|
|
||||||
override fun toProxy(obj: ZoneId): ZoneIdProxy = ZoneIdProxy(obj.id)
|
override fun toProxy(obj: ZoneId): ZoneIdProxy = ZoneIdProxy(obj.id)
|
||||||
|
|
||||||
override fun fromProxy(proxy: ZoneIdProxy): ZoneId = ZoneId.of(proxy.id)
|
override fun fromProxy(proxy: ZoneIdProxy): ZoneId = ZoneId.of(proxy.id)
|
||||||
|
@ -20,6 +20,7 @@ import net.corda.testing.BOB_IDENTITY
|
|||||||
import net.corda.testing.MEGA_CORP
|
import net.corda.testing.MEGA_CORP
|
||||||
import net.corda.testing.MEGA_CORP_PUBKEY
|
import net.corda.testing.MEGA_CORP_PUBKEY
|
||||||
import net.corda.testing.withTestSerialization
|
import net.corda.testing.withTestSerialization
|
||||||
|
import org.apache.activemq.artemis.api.core.SimpleString
|
||||||
import org.apache.qpid.proton.amqp.*
|
import org.apache.qpid.proton.amqp.*
|
||||||
import org.apache.qpid.proton.codec.DecoderImpl
|
import org.apache.qpid.proton.codec.DecoderImpl
|
||||||
import org.apache.qpid.proton.codec.EncoderImpl
|
import org.apache.qpid.proton.codec.EncoderImpl
|
||||||
@ -27,6 +28,7 @@ import org.junit.Assert.assertNotSame
|
|||||||
import org.junit.Assert.assertSame
|
import org.junit.Assert.assertSame
|
||||||
import org.junit.Ignore
|
import org.junit.Ignore
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
import java.io.ByteArrayInputStream
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.io.NotSerializableException
|
import java.io.NotSerializableException
|
||||||
import java.math.BigDecimal
|
import java.math.BigDecimal
|
||||||
@ -180,7 +182,7 @@ class SerializationOutputTests {
|
|||||||
assertTrue(Objects.deepEquals(desObj, desObj2) == expectDeserializedEqual)
|
assertTrue(Objects.deepEquals(desObj, desObj2) == expectDeserializedEqual)
|
||||||
|
|
||||||
// TODO: add some schema assertions to check correctly formed.
|
// TODO: add some schema assertions to check correctly formed.
|
||||||
return desObj2
|
return desObj
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -355,7 +357,7 @@ class SerializationOutputTests {
|
|||||||
serdes(obj)
|
serdes(obj)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = NotSerializableException::class)
|
@Test
|
||||||
fun `test TreeMap`() {
|
fun `test TreeMap`() {
|
||||||
val obj = TreeMap<Int, Foo>()
|
val obj = TreeMap<Int, Foo>()
|
||||||
obj[456] = Foo("Fred", 123)
|
obj[456] = Foo("Fred", 123)
|
||||||
@ -720,8 +722,18 @@ class SerializationOutputTests {
|
|||||||
serdes(obj, factory, factory2)
|
serdes(obj, factory, factory2)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: ignored due to Proton-J bug https://issues.apache.org/jira/browse/PROTON-1551
|
@Test
|
||||||
@Ignore
|
fun `test month serialize`() {
|
||||||
|
val obj = Month.APRIL
|
||||||
|
serdes(obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test day of week serialize`() {
|
||||||
|
val obj = DayOfWeek.FRIDAY
|
||||||
|
serdes(obj)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `test certificate holder serialize`() {
|
fun `test certificate holder serialize`() {
|
||||||
val factory = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader())
|
val factory = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader())
|
||||||
@ -734,8 +746,6 @@ class SerializationOutputTests {
|
|||||||
serdes(obj, factory, factory2)
|
serdes(obj, factory, factory2)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: ignored due to Proton-J bug https://issues.apache.org/jira/browse/PROTON-1551
|
|
||||||
@Ignore
|
|
||||||
@Test
|
@Test
|
||||||
fun `test party and certificate serialize`() {
|
fun `test party and certificate serialize`() {
|
||||||
val factory = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader())
|
val factory = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader())
|
||||||
@ -806,7 +816,6 @@ class SerializationOutputTests {
|
|||||||
|
|
||||||
data class Bob(val byteArrays: List<ByteArray>)
|
data class Bob(val byteArrays: List<ByteArray>)
|
||||||
|
|
||||||
@Ignore("Causes DeserializedParameterizedType.make() to fail")
|
|
||||||
@Test
|
@Test
|
||||||
fun `test list of byte arrays`() {
|
fun `test list of byte arrays`() {
|
||||||
val a = ByteArray(1)
|
val a = ByteArray(1)
|
||||||
@ -815,7 +824,9 @@ class SerializationOutputTests {
|
|||||||
|
|
||||||
val factory = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader())
|
val factory = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader())
|
||||||
val factory2 = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader())
|
val factory2 = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader())
|
||||||
serdes(obj, factory, factory2)
|
val obj2 = serdes(obj, factory, factory2, false, false)
|
||||||
|
|
||||||
|
assertNotSame(obj2.byteArrays[0], obj2.byteArrays[2])
|
||||||
}
|
}
|
||||||
|
|
||||||
data class Vic(val a: List<String>, val b: List<String>)
|
data class Vic(val a: List<String>, val b: List<String>)
|
||||||
@ -874,4 +885,88 @@ class SerializationOutputTests {
|
|||||||
val objCopy = serdes(obj, factory, factory2, false, false)
|
val objCopy = serdes(obj, factory, factory2, false, false)
|
||||||
assertNotSame(objCopy.a, objCopy.b)
|
assertNotSame(objCopy.a, objCopy.b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test StringBuffer serialize`() {
|
||||||
|
val factory = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader())
|
||||||
|
factory.register(net.corda.nodeapi.internal.serialization.amqp.custom.StringBufferSerializer)
|
||||||
|
|
||||||
|
val factory2 = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader())
|
||||||
|
factory2.register(net.corda.nodeapi.internal.serialization.amqp.custom.StringBufferSerializer)
|
||||||
|
|
||||||
|
val obj = StringBuffer("Bob")
|
||||||
|
val obj2 = serdes(obj, factory, factory2, false, false)
|
||||||
|
assertEquals(obj.toString(), obj2.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test SimpleString serialize`() {
|
||||||
|
val factory = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader())
|
||||||
|
factory.register(net.corda.nodeapi.internal.serialization.amqp.custom.SimpleStringSerializer)
|
||||||
|
|
||||||
|
val factory2 = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader())
|
||||||
|
factory2.register(net.corda.nodeapi.internal.serialization.amqp.custom.SimpleStringSerializer)
|
||||||
|
|
||||||
|
val obj = SimpleString("Bob")
|
||||||
|
serdes(obj, factory, factory2)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test kotlin Unit serialize`() {
|
||||||
|
val obj = Unit
|
||||||
|
serdes(obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test kotlin Pair serialize`() {
|
||||||
|
val obj = Pair("a", 3)
|
||||||
|
serdes(obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test InputStream serialize`() {
|
||||||
|
val factory = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader())
|
||||||
|
factory.register(net.corda.nodeapi.internal.serialization.amqp.custom.InputStreamSerializer)
|
||||||
|
|
||||||
|
val factory2 = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader())
|
||||||
|
factory2.register(net.corda.nodeapi.internal.serialization.amqp.custom.InputStreamSerializer)
|
||||||
|
val bytes = ByteArray(10) { it.toByte() }
|
||||||
|
val obj = ByteArrayInputStream(bytes)
|
||||||
|
val obj2 = serdes(obj, factory, factory2, expectedEqual = false, expectDeserializedEqual = false)
|
||||||
|
val obj3 = ByteArrayInputStream(bytes) // Can't use original since the stream pointer has moved.
|
||||||
|
assertEquals(obj3.available(), obj2.available())
|
||||||
|
assertEquals(obj3.read(), obj2.read())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test EnumSet serialize`() {
|
||||||
|
val factory = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader())
|
||||||
|
factory.register(net.corda.nodeapi.internal.serialization.amqp.custom.EnumSetSerializer(factory))
|
||||||
|
|
||||||
|
val factory2 = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader())
|
||||||
|
factory2.register(net.corda.nodeapi.internal.serialization.amqp.custom.EnumSetSerializer(factory2))
|
||||||
|
|
||||||
|
val obj = EnumSet.of(Month.APRIL, Month.AUGUST)
|
||||||
|
serdes(obj, factory, factory2)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test BitSet serialize`() {
|
||||||
|
val factory = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader())
|
||||||
|
factory.register(net.corda.nodeapi.internal.serialization.amqp.custom.BitSetSerializer(factory))
|
||||||
|
|
||||||
|
val factory2 = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader())
|
||||||
|
factory2.register(net.corda.nodeapi.internal.serialization.amqp.custom.BitSetSerializer(factory2))
|
||||||
|
|
||||||
|
val obj = BitSet.valueOf(kotlin.ByteArray(16) { it.toByte() }).get(0, 123)
|
||||||
|
serdes(obj, factory, factory2)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test EnumMap serialize`() {
|
||||||
|
val obj = EnumMap<Month, Int>(Month::class.java)
|
||||||
|
obj[Month.APRIL] = Month.APRIL.value
|
||||||
|
obj[Month.AUGUST] = Month.AUGUST.value
|
||||||
|
serdes(obj)
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user