mirror of
https://github.com/corda/corda.git
synced 2025-05-31 22:50:53 +00:00
CORDA-992 - Make the finger printer pluggable for serialization factory (#2479)
Facilitates easier testing
This commit is contained in:
parent
d072f6c275
commit
3c4212a3d6
@ -6,6 +6,8 @@ from the previous milestone release.
|
|||||||
|
|
||||||
UNRELEASED
|
UNRELEASED
|
||||||
----------
|
----------
|
||||||
|
* Make the serialisation finger-printer a pluggable entity rather than hard wiring into the factory
|
||||||
|
|
||||||
* Removed blacklisted word checks in Corda X.500 name to allow "Server" or "Node" to be use as part of the legal name.
|
* Removed blacklisted word checks in Corda X.500 name to allow "Server" or "Node" to be use as part of the legal name.
|
||||||
|
|
||||||
* Separated our pre-existing Artemis broker into an RPC broker and a P2P broker.
|
* Separated our pre-existing Artemis broker into an RPC broker and a P2P broker.
|
||||||
|
@ -31,7 +31,8 @@ open class ArraySerializer(override val type: Type, factory: SerializerFactory)
|
|||||||
"${type.componentType().typeName}$arrayType"
|
"${type.componentType().typeName}$arrayType"
|
||||||
}
|
}
|
||||||
|
|
||||||
override val typeDescriptor by lazy { Symbol.valueOf("$DESCRIPTOR_DOMAIN:${fingerprintForType(type, factory)}") }
|
override val typeDescriptor by lazy {
|
||||||
|
Symbol.valueOf("$DESCRIPTOR_DOMAIN:${factory.fingerPrinter.fingerprint(type)}") }
|
||||||
internal val elementType: Type by lazy { type.componentType() }
|
internal val elementType: Type by lazy { type.componentType() }
|
||||||
internal open val typeName by lazy { calcTypeName(type) }
|
internal open val typeName by lazy { calcTypeName(type) }
|
||||||
|
|
||||||
|
@ -17,7 +17,9 @@ import kotlin.collections.Set
|
|||||||
*/
|
*/
|
||||||
class CollectionSerializer(val declaredType: ParameterizedType, factory: SerializerFactory) : AMQPSerializer<Any> {
|
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 = Symbol.valueOf("$DESCRIPTOR_DOMAIN:${fingerprintForType(type, factory)}")
|
override val typeDescriptor by lazy {
|
||||||
|
Symbol.valueOf("$DESCRIPTOR_DOMAIN:${factory.fingerPrinter.fingerprint(type)}")
|
||||||
|
}
|
||||||
|
|
||||||
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
|
||||||
|
@ -60,7 +60,9 @@ abstract class CustomSerializer<T : Any> : AMQPSerializer<T>, SerializerFor {
|
|||||||
|
|
||||||
override fun isSerializerFor(clazz: Class<*>): Boolean = clazz == this.clazz
|
override fun isSerializerFor(clazz: Class<*>): Boolean = clazz == this.clazz
|
||||||
override val type: Type get() = clazz
|
override val type: Type get() = clazz
|
||||||
override val typeDescriptor = Symbol.valueOf("$DESCRIPTOR_DOMAIN:${fingerprintForDescriptors(superClassSerializer.typeDescriptor.toString(), nameForType(clazz))}")
|
override val typeDescriptor by lazy {
|
||||||
|
Symbol.valueOf("$DESCRIPTOR_DOMAIN:${SerializerFingerPrinter().fingerprintForDescriptors(superClassSerializer.typeDescriptor.toString(), nameForType(clazz))}")
|
||||||
|
}
|
||||||
private val typeNotation: TypeNotation = RestrictedType(
|
private val typeNotation: TypeNotation = RestrictedType(
|
||||||
SerializerFactory.nameForType(clazz),
|
SerializerFactory.nameForType(clazz),
|
||||||
null,
|
null,
|
||||||
|
@ -39,7 +39,8 @@ class EnumEvolutionSerializer(
|
|||||||
factory: SerializerFactory,
|
factory: SerializerFactory,
|
||||||
private val conversions: Map<String, String>,
|
private val conversions: Map<String, String>,
|
||||||
private val ordinals: Map<String, Int>) : AMQPSerializer<Any> {
|
private val ordinals: Map<String, Int>) : AMQPSerializer<Any> {
|
||||||
override val typeDescriptor = Symbol.valueOf("$DESCRIPTOR_DOMAIN:${fingerprintForType(type, factory)}")!!
|
override val typeDescriptor = Symbol.valueOf(
|
||||||
|
"$DESCRIPTOR_DOMAIN:${factory.fingerPrinter.fingerprint(type)}")!!
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private fun MutableMap<String, String>.mapInPlace(f: (String) -> String) {
|
private fun MutableMap<String, String>.mapInPlace(f: (String) -> String) {
|
||||||
|
@ -11,8 +11,9 @@ import java.lang.reflect.Type
|
|||||||
*/
|
*/
|
||||||
class EnumSerializer(declaredType: Type, declaredClass: Class<*>, factory: SerializerFactory) : AMQPSerializer<Any> {
|
class EnumSerializer(declaredType: Type, declaredClass: Class<*>, factory: SerializerFactory) : AMQPSerializer<Any> {
|
||||||
override val type: Type = declaredType
|
override val type: Type = declaredType
|
||||||
override val typeDescriptor = Symbol.valueOf("$DESCRIPTOR_DOMAIN:${fingerprintForType(type, factory)}")!!
|
|
||||||
private val typeNotation: TypeNotation
|
private val typeNotation: TypeNotation
|
||||||
|
override val typeDescriptor = Symbol.valueOf(
|
||||||
|
"$DESCRIPTOR_DOMAIN:${factory.fingerPrinter.fingerprint(type)}")!!
|
||||||
|
|
||||||
init {
|
init {
|
||||||
typeNotation = RestrictedType(
|
typeNotation = RestrictedType(
|
||||||
|
@ -0,0 +1,202 @@
|
|||||||
|
package net.corda.nodeapi.internal.serialization.amqp
|
||||||
|
|
||||||
|
import com.google.common.hash.Hasher
|
||||||
|
import com.google.common.hash.Hashing
|
||||||
|
import net.corda.core.utilities.loggerFor
|
||||||
|
import net.corda.core.utilities.toBase64
|
||||||
|
import java.io.NotSerializableException
|
||||||
|
import java.lang.reflect.*
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should be implemented by classes which wish to provide plugable fingerprinting og types for a [SerializerFactory]
|
||||||
|
*/
|
||||||
|
interface FingerPrinter {
|
||||||
|
/**
|
||||||
|
* Return a unique identifier for a type, usually this will take into account the constituent elements
|
||||||
|
* of said type such that any modification to any sub element wll generate a different fingerprint
|
||||||
|
*/
|
||||||
|
fun fingerprint(type: Type): String
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If required, associate an instance of the fingerprinter with a specific serializer factory
|
||||||
|
*/
|
||||||
|
fun setOwner(factory: SerializerFactory)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of the finger printing mechanism used by default
|
||||||
|
*/
|
||||||
|
class SerializerFingerPrinter : FingerPrinter {
|
||||||
|
private var factory: SerializerFactory? = null
|
||||||
|
|
||||||
|
private val ARRAY_HASH: String = "Array = true"
|
||||||
|
private val ENUM_HASH: String = "Enum = true"
|
||||||
|
private val ALREADY_SEEN_HASH: String = "Already seen = true"
|
||||||
|
private val NULLABLE_HASH: String = "Nullable = true"
|
||||||
|
private val NOT_NULLABLE_HASH: String = "Nullable = false"
|
||||||
|
private val ANY_TYPE_HASH: String = "Any type = true"
|
||||||
|
private val TYPE_VARIABLE_HASH: String = "Type variable = true"
|
||||||
|
private val WILDCARD_TYPE_HASH: String = "Wild card = true"
|
||||||
|
|
||||||
|
private val logger by lazy { loggerFor<Schema>() }
|
||||||
|
|
||||||
|
override fun setOwner(factory: SerializerFactory) {
|
||||||
|
this.factory = factory
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The method generates a fingerprint for a given JVM [Type] that should be unique to the schema representation.
|
||||||
|
* Thus it only takes into account properties and types and only supports the same object graph subset as the overall
|
||||||
|
* serialization code.
|
||||||
|
*
|
||||||
|
* The idea being that even for two classes that share the same name but differ in a minor way, the fingerprint will be
|
||||||
|
* different.
|
||||||
|
*/
|
||||||
|
override fun fingerprint(type: Type): String {
|
||||||
|
return fingerprintForType(
|
||||||
|
type, null, HashSet(), Hashing.murmur3_128().newHasher(), debugIndent = 1).hash().asBytes().toBase64()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isCollectionOrMap(type: Class<*>) =
|
||||||
|
(Collection::class.java.isAssignableFrom(type) || Map::class.java.isAssignableFrom(type))
|
||||||
|
&& !EnumSet::class.java.isAssignableFrom(type)
|
||||||
|
|
||||||
|
internal fun fingerprintForDescriptors(vararg typeDescriptors: String): String {
|
||||||
|
val hasher = Hashing.murmur3_128().newHasher()
|
||||||
|
for (typeDescriptor in typeDescriptors) {
|
||||||
|
hasher.putUnencodedChars(typeDescriptor)
|
||||||
|
}
|
||||||
|
return hasher.hash().asBytes().toBase64()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Hasher.fingerprintWithCustomSerializerOrElse(
|
||||||
|
factory: SerializerFactory,
|
||||||
|
clazz: Class<*>,
|
||||||
|
declaredType: Type,
|
||||||
|
block: () -> Hasher): Hasher {
|
||||||
|
// Need to check if a custom serializer is applicable
|
||||||
|
val customSerializer = factory.findCustomSerializer(clazz, declaredType)
|
||||||
|
return if (customSerializer != null) {
|
||||||
|
putUnencodedChars(customSerializer.typeDescriptor)
|
||||||
|
} else {
|
||||||
|
block()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This method concatenates various elements of the types recursively as unencoded strings into the hasher,
|
||||||
|
// effectively creating a unique string for a type which we then hash in the calling function above.
|
||||||
|
private fun fingerprintForType(type: Type, contextType: Type?, alreadySeen: MutableSet<Type>,
|
||||||
|
hasher: Hasher, debugIndent: Int = 1): Hasher {
|
||||||
|
// We don't include Example<?> and Example<T> where type is ? or T in this otherwise we
|
||||||
|
// generate different fingerprints for class Outer<T>(val a: Inner<T>) when serialising
|
||||||
|
// and deserializing (assuming deserialization is occurring in a factory that didn't
|
||||||
|
// serialise the object in the first place (and thus the cache lookup fails). This is also
|
||||||
|
// true of Any, where we need Example<A, B> and Example<?, ?> to have the same fingerprint
|
||||||
|
return if ((type in alreadySeen)
|
||||||
|
&& (type !is SerializerFactory.AnyType)
|
||||||
|
&& (type !is TypeVariable<*>)
|
||||||
|
&& (type !is WildcardType)) {
|
||||||
|
hasher.putUnencodedChars(ALREADY_SEEN_HASH)
|
||||||
|
} else {
|
||||||
|
alreadySeen += type
|
||||||
|
try {
|
||||||
|
when (type) {
|
||||||
|
is ParameterizedType -> {
|
||||||
|
// Hash the rawType + params
|
||||||
|
val clazz = type.rawType as Class<*>
|
||||||
|
|
||||||
|
val startingHash = if (isCollectionOrMap(clazz)) {
|
||||||
|
hasher.putUnencodedChars(clazz.name)
|
||||||
|
} else {
|
||||||
|
hasher.fingerprintWithCustomSerializerOrElse(factory!!, clazz, type) {
|
||||||
|
fingerprintForObject(type, type, alreadySeen, hasher, factory!!, debugIndent + 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ... and concatenate the type data for each parameter type.
|
||||||
|
type.actualTypeArguments.fold(startingHash) { orig, paramType ->
|
||||||
|
fingerprintForType(paramType, type, alreadySeen, orig, debugIndent + 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Previously, we drew a distinction between TypeVariable, WildcardType, and AnyType, changing
|
||||||
|
// the signature of the fingerprinted object. This, however, doesn't work as it breaks bi-
|
||||||
|
// directional fingerprints. That is, fingerprinting a concrete instance of a generic
|
||||||
|
// type (Example<Int>), creates a different fingerprint from the generic type itself (Example<T>)
|
||||||
|
//
|
||||||
|
// On serialization Example<Int> is treated as Example<T>, a TypeVariable
|
||||||
|
// On deserialisation it is seen as Example<?>, A WildcardType *and* a TypeVariable
|
||||||
|
// Note: AnyType is a special case of WildcardType used in other parts of the
|
||||||
|
// serializer so both cases need to be dealt with here
|
||||||
|
//
|
||||||
|
// If we treat these types as fundamentally different and alter the fingerprint we will
|
||||||
|
// end up breaking into the evolver when we shouldn't or, worse, evoking the carpenter.
|
||||||
|
is SerializerFactory.AnyType,
|
||||||
|
is WildcardType,
|
||||||
|
is TypeVariable<*> -> {
|
||||||
|
hasher.putUnencodedChars("?").putUnencodedChars(ANY_TYPE_HASH)
|
||||||
|
}
|
||||||
|
is Class<*> -> {
|
||||||
|
if (type.isArray) {
|
||||||
|
fingerprintForType(type.componentType, contextType, alreadySeen, hasher, debugIndent + 1)
|
||||||
|
.putUnencodedChars(ARRAY_HASH)
|
||||||
|
} else if (SerializerFactory.isPrimitive(type)) {
|
||||||
|
hasher.putUnencodedChars(type.name)
|
||||||
|
} else if (isCollectionOrMap(type)) {
|
||||||
|
hasher.putUnencodedChars(type.name)
|
||||||
|
} else if (type.isEnum) {
|
||||||
|
// ensures any change to the enum (adding constants) will trigger the need for evolution
|
||||||
|
hasher.apply {
|
||||||
|
type.enumConstants.forEach {
|
||||||
|
putUnencodedChars(it.toString())
|
||||||
|
}
|
||||||
|
}.putUnencodedChars(type.name).putUnencodedChars(ENUM_HASH)
|
||||||
|
} else {
|
||||||
|
hasher.fingerprintWithCustomSerializerOrElse(factory!!, type, type) {
|
||||||
|
if (type.kotlin.objectInstance != null) {
|
||||||
|
// TODO: name collision is too likely for kotlin objects, we need to introduce some reference
|
||||||
|
// to the CorDapp but maybe reference to the JAR in the short term.
|
||||||
|
hasher.putUnencodedChars(type.name)
|
||||||
|
} else {
|
||||||
|
fingerprintForObject(type, type, alreadySeen, hasher, factory!!, debugIndent + 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Hash the element type + some array hash
|
||||||
|
is GenericArrayType -> {
|
||||||
|
fingerprintForType(type.genericComponentType, contextType, alreadySeen,
|
||||||
|
hasher, debugIndent + 1).putUnencodedChars(ARRAY_HASH)
|
||||||
|
}
|
||||||
|
else -> throw NotSerializableException("Don't know how to hash")
|
||||||
|
}
|
||||||
|
} catch (e: NotSerializableException) {
|
||||||
|
val msg = "${e.message} -> $type"
|
||||||
|
logger.error(msg, e)
|
||||||
|
throw NotSerializableException(msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun fingerprintForObject(
|
||||||
|
type: Type,
|
||||||
|
contextType: Type?,
|
||||||
|
alreadySeen: MutableSet<Type>,
|
||||||
|
hasher: Hasher,
|
||||||
|
factory: SerializerFactory,
|
||||||
|
debugIndent: Int = 0): Hasher {
|
||||||
|
// Hash the class + properties + interfaces
|
||||||
|
val name = type.asClass()?.name
|
||||||
|
?: throw NotSerializableException("Expected only Class or ParameterizedType but found $type")
|
||||||
|
|
||||||
|
propertiesForSerialization(constructorForDeserialization(type), contextType ?: type, factory)
|
||||||
|
.serializationOrder
|
||||||
|
.fold(hasher.putUnencodedChars(name)) { orig, prop ->
|
||||||
|
fingerprintForType(prop.getter.resolvedType, type, alreadySeen, orig, debugIndent + 1)
|
||||||
|
.putUnencodedChars(prop.getter.name)
|
||||||
|
.putUnencodedChars(if (prop.getter.mandatory) NOT_NULLABLE_HASH else NULLABLE_HASH)
|
||||||
|
}
|
||||||
|
interfacesForSerialization(type, factory).map { fingerprintForType(it, type, alreadySeen, hasher, debugIndent + 1) }
|
||||||
|
return hasher
|
||||||
|
}
|
||||||
|
}
|
@ -19,7 +19,8 @@ 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 = Symbol.valueOf("$DESCRIPTOR_DOMAIN:${fingerprintForType(type, factory)}")
|
override val typeDescriptor: Symbol = Symbol.valueOf(
|
||||||
|
"$DESCRIPTOR_DOMAIN:${factory.fingerPrinter.fingerprint(type)}")
|
||||||
|
|
||||||
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
|
||||||
|
@ -31,7 +31,8 @@ open class ObjectSerializer(val clazz: Type, factory: SerializerFactory) : AMQPS
|
|||||||
|
|
||||||
private val typeName = nameForType(clazz)
|
private val typeName = nameForType(clazz)
|
||||||
|
|
||||||
override val typeDescriptor = Symbol.valueOf("$DESCRIPTOR_DOMAIN:${fingerprintForType(type, factory)}")
|
override val typeDescriptor = Symbol.valueOf(
|
||||||
|
"$DESCRIPTOR_DOMAIN:${factory.fingerPrinter.fingerprint(type)}")
|
||||||
|
|
||||||
// We restrict to only those annotated or whitelisted
|
// We restrict to only those annotated or whitelisted
|
||||||
private val interfaces = interfacesForSerialization(clazz, factory)
|
private val interfaces = interfacesForSerialization(clazz, factory)
|
||||||
|
@ -1,19 +1,13 @@
|
|||||||
package net.corda.nodeapi.internal.serialization.amqp
|
package net.corda.nodeapi.internal.serialization.amqp
|
||||||
|
|
||||||
import com.google.common.hash.Hasher
|
|
||||||
import com.google.common.hash.Hashing
|
|
||||||
import net.corda.core.internal.uncheckedCast
|
import net.corda.core.internal.uncheckedCast
|
||||||
import net.corda.nodeapi.internal.serialization.CordaSerializationMagic
|
import net.corda.nodeapi.internal.serialization.CordaSerializationMagic
|
||||||
import net.corda.core.utilities.loggerFor
|
|
||||||
import net.corda.core.utilities.toBase64
|
|
||||||
import org.apache.qpid.proton.amqp.DescribedType
|
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.DescribedTypeConstructor
|
import org.apache.qpid.proton.codec.DescribedTypeConstructor
|
||||||
import java.io.NotSerializableException
|
import java.io.NotSerializableException
|
||||||
import java.lang.reflect.*
|
|
||||||
import java.util.*
|
|
||||||
import net.corda.nodeapi.internal.serialization.carpenter.Field as CarpenterField
|
import net.corda.nodeapi.internal.serialization.carpenter.Field as CarpenterField
|
||||||
import net.corda.nodeapi.internal.serialization.carpenter.Schema as CarpenterSchema
|
import net.corda.nodeapi.internal.serialization.carpenter.Schema as CarpenterSchema
|
||||||
|
|
||||||
@ -92,7 +86,14 @@ data class Descriptor(val name: Symbol?, val code: UnsignedLong? = null) : Descr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data class Field(val name: String, val type: String, val requires: List<String>, val default: String?, val label: String?, val mandatory: Boolean, val multiple: Boolean) : DescribedType {
|
data class Field(
|
||||||
|
val name: String,
|
||||||
|
val type: String,
|
||||||
|
val requires: List<String>,
|
||||||
|
val default: String?,
|
||||||
|
val label: String?,
|
||||||
|
val mandatory: Boolean,
|
||||||
|
val multiple: Boolean) : DescribedType {
|
||||||
companion object : DescribedTypeConstructor<Field> {
|
companion object : DescribedTypeConstructor<Field> {
|
||||||
val DESCRIPTOR = AMQPDescriptorRegistry.FIELD.amqpDescriptor
|
val DESCRIPTOR = AMQPDescriptorRegistry.FIELD.amqpDescriptor
|
||||||
|
|
||||||
@ -302,162 +303,4 @@ data class ReferencedObject(private val refCounter: Int) : DescribedType {
|
|||||||
override fun toString(): String = "<refObject refCounter=$refCounter/>"
|
override fun toString(): String = "<refObject refCounter=$refCounter/>"
|
||||||
}
|
}
|
||||||
|
|
||||||
private val ARRAY_HASH: String = "Array = true"
|
|
||||||
private val ENUM_HASH: String = "Enum = true"
|
|
||||||
private val ALREADY_SEEN_HASH: String = "Already seen = true"
|
|
||||||
private val NULLABLE_HASH: String = "Nullable = true"
|
|
||||||
private val NOT_NULLABLE_HASH: String = "Nullable = false"
|
|
||||||
private val ANY_TYPE_HASH: String = "Any type = true"
|
|
||||||
private val TYPE_VARIABLE_HASH: String = "Type variable = true"
|
|
||||||
private val WILDCARD_TYPE_HASH: String = "Wild card = true"
|
|
||||||
|
|
||||||
private val logger by lazy { loggerFor<Schema>() }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The method generates a fingerprint for a given JVM [Type] that should be unique to the schema representation.
|
|
||||||
* Thus it only takes into account properties and types and only supports the same object graph subset as the overall
|
|
||||||
* serialization code.
|
|
||||||
*
|
|
||||||
* The idea being that even for two classes that share the same name but differ in a minor way, the fingerprint will be
|
|
||||||
* different.
|
|
||||||
*/
|
|
||||||
// TODO: write tests
|
|
||||||
internal fun fingerprintForType(type: Type, factory: SerializerFactory): String {
|
|
||||||
return fingerprintForType(type, null, HashSet(), Hashing.murmur3_128().newHasher(), factory).hash().asBytes().toBase64()
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun fingerprintForDescriptors(vararg typeDescriptors: String): String {
|
|
||||||
val hasher = Hashing.murmur3_128().newHasher()
|
|
||||||
for (typeDescriptor in typeDescriptors) {
|
|
||||||
hasher.putUnencodedChars(typeDescriptor)
|
|
||||||
}
|
|
||||||
return hasher.hash().asBytes().toBase64()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun Hasher.fingerprintWithCustomSerializerOrElse(factory: SerializerFactory, clazz: Class<*>, declaredType: Type, block: () -> Hasher): Hasher {
|
|
||||||
// Need to check if a custom serializer is applicable
|
|
||||||
val customSerializer = factory.findCustomSerializer(clazz, declaredType)
|
|
||||||
return if (customSerializer != null) {
|
|
||||||
putUnencodedChars(customSerializer.typeDescriptor)
|
|
||||||
} else {
|
|
||||||
block()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This method concatenates various elements of the types recursively as unencoded strings into the hasher, effectively
|
|
||||||
// creating a unique string for a type which we then hash in the calling function above.
|
|
||||||
private fun fingerprintForType(type: Type, contextType: Type?, alreadySeen: MutableSet<Type>,
|
|
||||||
hasher: Hasher, factory: SerializerFactory, debugIndent: Int = 1): Hasher {
|
|
||||||
// We don't include Example<?> and Example<T> where type is ? or T in this otherwise we
|
|
||||||
// generate different fingerprints for class Outer<T>(val a: Inner<T>) when serialising
|
|
||||||
// and deserializing (assuming deserialization is occurring in a factory that didn't
|
|
||||||
// serialise the object in the first place (and thus the cache lookup fails). This is also
|
|
||||||
// true of Any, where we need Example<A, B> and Example<?, ?> to have the same fingerprint
|
|
||||||
return if ((type in alreadySeen)
|
|
||||||
&& (type !is SerializerFactory.AnyType)
|
|
||||||
&& (type !is TypeVariable<*>)
|
|
||||||
&& (type !is WildcardType)) {
|
|
||||||
hasher.putUnencodedChars(ALREADY_SEEN_HASH)
|
|
||||||
} else {
|
|
||||||
alreadySeen += type
|
|
||||||
try {
|
|
||||||
when (type) {
|
|
||||||
is ParameterizedType -> {
|
|
||||||
// Hash the rawType + params
|
|
||||||
val clazz = type.rawType as Class<*>
|
|
||||||
|
|
||||||
val startingHash = if (isCollectionOrMap(clazz)) {
|
|
||||||
hasher.putUnencodedChars(clazz.name)
|
|
||||||
} else {
|
|
||||||
hasher.fingerprintWithCustomSerializerOrElse(factory, clazz, type) {
|
|
||||||
fingerprintForObject(type, type, alreadySeen, hasher, factory, debugIndent+1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ... and concatenate the type data for each parameter type.
|
|
||||||
type.actualTypeArguments.fold(startingHash) { orig, paramType ->
|
|
||||||
fingerprintForType(paramType, type, alreadySeen, orig, factory, debugIndent+1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Previously, we drew a distinction between TypeVariable, WildcardType, and AnyType, changing
|
|
||||||
// the signature of the fingerprinted object. This, however, doesn't work as it breaks bi-
|
|
||||||
// directional fingerprints. That is, fingerprinting a concrete instance of a generic
|
|
||||||
// type (Example<Int>), creates a different fingerprint from the generic type itself (Example<T>)
|
|
||||||
//
|
|
||||||
// On serialization Example<Int> is treated as Example<T>, a TypeVariable
|
|
||||||
// On deserialisation it is seen as Example<?>, A WildcardType *and* a TypeVariable
|
|
||||||
// Note: AnyType is a special case of WildcardType used in other parts of the
|
|
||||||
// serializer so both cases need to be dealt with here
|
|
||||||
//
|
|
||||||
// If we treat these types as fundamentally different and alter the fingerprint we will
|
|
||||||
// end up breaking into the evolver when we shouldn't or, worse, evoking the carpenter.
|
|
||||||
is SerializerFactory.AnyType,
|
|
||||||
is WildcardType,
|
|
||||||
is TypeVariable<*> -> {
|
|
||||||
hasher.putUnencodedChars("?").putUnencodedChars(ANY_TYPE_HASH)
|
|
||||||
}
|
|
||||||
is Class<*> -> {
|
|
||||||
if (type.isArray) {
|
|
||||||
fingerprintForType(type.componentType, contextType, alreadySeen, hasher, factory, debugIndent+1)
|
|
||||||
.putUnencodedChars(ARRAY_HASH)
|
|
||||||
} else if (SerializerFactory.isPrimitive(type)) {
|
|
||||||
hasher.putUnencodedChars(type.name)
|
|
||||||
} else if (isCollectionOrMap(type)) {
|
|
||||||
hasher.putUnencodedChars(type.name)
|
|
||||||
} else if (type.isEnum) {
|
|
||||||
// ensures any change to the enum (adding constants) will trigger the need for evolution
|
|
||||||
hasher.apply {
|
|
||||||
type.enumConstants.forEach {
|
|
||||||
putUnencodedChars(it.toString())
|
|
||||||
}
|
|
||||||
}.putUnencodedChars(type.name).putUnencodedChars(ENUM_HASH)
|
|
||||||
} else {
|
|
||||||
hasher.fingerprintWithCustomSerializerOrElse(factory, type, type) {
|
|
||||||
if (type.kotlin.objectInstance != null) {
|
|
||||||
// TODO: name collision is too likely for kotlin objects, we need to introduce some reference
|
|
||||||
// to the CorDapp but maybe reference to the JAR in the short term.
|
|
||||||
hasher.putUnencodedChars(type.name)
|
|
||||||
} else {
|
|
||||||
fingerprintForObject(type, type, alreadySeen, hasher, factory, debugIndent+1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Hash the element type + some array hash
|
|
||||||
is GenericArrayType -> {
|
|
||||||
fingerprintForType(type.genericComponentType, contextType, alreadySeen,
|
|
||||||
hasher, factory, debugIndent+1).putUnencodedChars(ARRAY_HASH)
|
|
||||||
}
|
|
||||||
else -> throw NotSerializableException("Don't know how to hash")
|
|
||||||
}
|
|
||||||
} catch (e: NotSerializableException) {
|
|
||||||
val msg = "${e.message} -> $type"
|
|
||||||
logger.error(msg, e)
|
|
||||||
throw NotSerializableException(msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
|
||||||
debugIndent: Int = 0): Hasher {
|
|
||||||
// Hash the class + properties + interfaces
|
|
||||||
val name = type.asClass()?.name ?: throw NotSerializableException("Expected only Class or ParameterizedType but found $type")
|
|
||||||
|
|
||||||
propertiesForSerialization(constructorForDeserialization(type), contextType ?: type, factory)
|
|
||||||
.serializationOrder
|
|
||||||
.fold(hasher.putUnencodedChars(name)) { orig, prop ->
|
|
||||||
fingerprintForType(prop.getter.resolvedType, type, alreadySeen, orig, factory, debugIndent+1)
|
|
||||||
.putUnencodedChars(prop.getter.name)
|
|
||||||
.putUnencodedChars(if (prop.getter.mandatory) NOT_NULLABLE_HASH else NULLABLE_HASH)
|
|
||||||
}
|
|
||||||
interfacesForSerialization(type, factory).map { fingerprintForType(it, type, alreadySeen, hasher, factory, debugIndent+1) }
|
|
||||||
return hasher
|
|
||||||
}
|
|
||||||
|
@ -40,7 +40,13 @@ data class FactorySchemaAndDescriptor(val schemas: SerializationSchemas, val typ
|
|||||||
open class SerializerFactory(
|
open class SerializerFactory(
|
||||||
val whitelist: ClassWhitelist,
|
val whitelist: ClassWhitelist,
|
||||||
cl: ClassLoader,
|
cl: ClassLoader,
|
||||||
private val evolutionSerializerGetter: EvolutionSerializerGetterBase = EvolutionSerializerGetter()) {
|
private val evolutionSerializerGetter: EvolutionSerializerGetterBase = EvolutionSerializerGetter(),
|
||||||
|
val fingerPrinter: FingerPrinter = SerializerFingerPrinter()) {
|
||||||
|
|
||||||
|
init {
|
||||||
|
fingerPrinter.setOwner(this)
|
||||||
|
}
|
||||||
|
|
||||||
private val serializersByType = ConcurrentHashMap<Type, AMQPSerializer<Any>>()
|
private val serializersByType = ConcurrentHashMap<Type, AMQPSerializer<Any>>()
|
||||||
private val serializersByDescriptor = ConcurrentHashMap<Any, AMQPSerializer<Any>>()
|
private val serializersByDescriptor = ConcurrentHashMap<Any, AMQPSerializer<Any>>()
|
||||||
private val customSerializers = CopyOnWriteArrayList<SerializerFor>()
|
private val customSerializers = CopyOnWriteArrayList<SerializerFor>()
|
||||||
|
@ -10,7 +10,8 @@ import java.lang.reflect.Type
|
|||||||
* want converting back to that singleton instance on the receiving JVM.
|
* want converting back to that singleton instance on the receiving JVM.
|
||||||
*/
|
*/
|
||||||
class SingletonSerializer(override val type: Class<*>, val singleton: Any, factory: SerializerFactory) : AMQPSerializer<Any> {
|
class SingletonSerializer(override val type: Class<*>, val singleton: Any, factory: SerializerFactory) : AMQPSerializer<Any> {
|
||||||
override val typeDescriptor = Symbol.valueOf("$DESCRIPTOR_DOMAIN:${fingerprintForType(type, factory)}")
|
override val typeDescriptor = Symbol.valueOf(
|
||||||
|
"$DESCRIPTOR_DOMAIN:${factory.fingerPrinter.fingerprint(type)}")
|
||||||
|
|
||||||
private val interfaces = interfacesForSerialization(type, factory)
|
private val interfaces = interfacesForSerialization(type, factory)
|
||||||
|
|
||||||
|
@ -32,10 +32,12 @@ public class ErrorMessageTests {
|
|||||||
@Test
|
@Test
|
||||||
public void testJavaConstructorAnnotations() {
|
public void testJavaConstructorAnnotations() {
|
||||||
EvolutionSerializerGetterBase evolutionSerialiserGetter = new EvolutionSerializerGetter();
|
EvolutionSerializerGetterBase evolutionSerialiserGetter = new EvolutionSerializerGetter();
|
||||||
|
FingerPrinter fingerPrinter = new SerializerFingerPrinter();
|
||||||
SerializerFactory factory1 = new SerializerFactory(
|
SerializerFactory factory1 = new SerializerFactory(
|
||||||
AllWhitelist.INSTANCE,
|
AllWhitelist.INSTANCE,
|
||||||
ClassLoader.getSystemClassLoader(),
|
ClassLoader.getSystemClassLoader(),
|
||||||
evolutionSerialiserGetter);
|
evolutionSerialiserGetter,
|
||||||
|
fingerPrinter);
|
||||||
|
|
||||||
SerializationOutput ser = new SerializationOutput(factory1);
|
SerializationOutput ser = new SerializationOutput(factory1);
|
||||||
|
|
||||||
|
@ -29,7 +29,8 @@ public class JavaGenericsTest {
|
|||||||
SerializerFactory factory = new SerializerFactory(
|
SerializerFactory factory = new SerializerFactory(
|
||||||
AllWhitelist.INSTANCE,
|
AllWhitelist.INSTANCE,
|
||||||
ClassLoader.getSystemClassLoader(),
|
ClassLoader.getSystemClassLoader(),
|
||||||
new EvolutionSerializerGetter());
|
new EvolutionSerializerGetter(),
|
||||||
|
new SerializerFingerPrinter());
|
||||||
|
|
||||||
SerializationOutput ser = new SerializationOutput(factory);
|
SerializationOutput ser = new SerializationOutput(factory);
|
||||||
SerializedBytes<?> bytes = ser.serialize(a1);
|
SerializedBytes<?> bytes = ser.serialize(a1);
|
||||||
@ -44,7 +45,8 @@ public class JavaGenericsTest {
|
|||||||
SerializerFactory factory = new SerializerFactory(
|
SerializerFactory factory = new SerializerFactory(
|
||||||
AllWhitelist.INSTANCE,
|
AllWhitelist.INSTANCE,
|
||||||
ClassLoader.getSystemClassLoader(),
|
ClassLoader.getSystemClassLoader(),
|
||||||
new EvolutionSerializerGetter());
|
new EvolutionSerializerGetter(),
|
||||||
|
new SerializerFingerPrinter());
|
||||||
|
|
||||||
return (new SerializationOutput(factory)).serialize(a);
|
return (new SerializationOutput(factory)).serialize(a);
|
||||||
}
|
}
|
||||||
@ -59,7 +61,8 @@ public class JavaGenericsTest {
|
|||||||
SerializerFactory factory = new SerializerFactory(
|
SerializerFactory factory = new SerializerFactory(
|
||||||
AllWhitelist.INSTANCE,
|
AllWhitelist.INSTANCE,
|
||||||
ClassLoader.getSystemClassLoader(),
|
ClassLoader.getSystemClassLoader(),
|
||||||
new EvolutionSerializerGetter());
|
new EvolutionSerializerGetter(),
|
||||||
|
new SerializerFingerPrinter());
|
||||||
|
|
||||||
DeserializationInput des = new DeserializationInput(factory);
|
DeserializationInput des = new DeserializationInput(factory);
|
||||||
return des.deserialize(bytes, A.class);
|
return des.deserialize(bytes, A.class);
|
||||||
@ -83,7 +86,8 @@ public class JavaGenericsTest {
|
|||||||
SerializerFactory factory = new SerializerFactory(
|
SerializerFactory factory = new SerializerFactory(
|
||||||
AllWhitelist.INSTANCE,
|
AllWhitelist.INSTANCE,
|
||||||
ClassLoader.getSystemClassLoader(),
|
ClassLoader.getSystemClassLoader(),
|
||||||
new EvolutionSerializerGetter());
|
new EvolutionSerializerGetter(),
|
||||||
|
new SerializerFingerPrinter());
|
||||||
|
|
||||||
SerializedBytes<?> bytes = forceWildcardSerializeFactory(new A(new Inner(29)), factory);
|
SerializedBytes<?> bytes = forceWildcardSerializeFactory(new A(new Inner(29)), factory);
|
||||||
Inner i = (Inner)forceWildcardDeserializeFactory(bytes, factory).getT();
|
Inner i = (Inner)forceWildcardDeserializeFactory(bytes, factory).getT();
|
||||||
|
@ -76,9 +76,9 @@ public class JavaPrivatePropertyTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void singlePrivateBooleanWithConstructor() throws NotSerializableException, NoSuchFieldException, IllegalAccessException {
|
public void singlePrivateBooleanWithConstructor() throws NotSerializableException, NoSuchFieldException, IllegalAccessException {
|
||||||
EvolutionSerializerGetterBase evolutionSerializerGetter = new EvolutionSerializerGetter();
|
|
||||||
SerializerFactory factory = new SerializerFactory(AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader(),
|
SerializerFactory factory = new SerializerFactory(AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader(),
|
||||||
evolutionSerializerGetter);
|
new EvolutionSerializerGetter(),
|
||||||
|
new SerializerFingerPrinter());
|
||||||
SerializationOutput ser = new SerializationOutput(factory);
|
SerializationOutput ser = new SerializationOutput(factory);
|
||||||
DeserializationInput des = new DeserializationInput(factory);
|
DeserializationInput des = new DeserializationInput(factory);
|
||||||
|
|
||||||
@ -89,9 +89,10 @@ public class JavaPrivatePropertyTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void singlePrivateBooleanWithNoConstructor() throws NotSerializableException, NoSuchFieldException, IllegalAccessException {
|
public void singlePrivateBooleanWithNoConstructor() throws NotSerializableException, NoSuchFieldException, IllegalAccessException {
|
||||||
EvolutionSerializerGetterBase evolutionSerializerGetter = new EvolutionSerializerGetter();
|
|
||||||
SerializerFactory factory = new SerializerFactory(AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader(),
|
SerializerFactory factory = new SerializerFactory(AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader(),
|
||||||
evolutionSerializerGetter);
|
new EvolutionSerializerGetter(),
|
||||||
|
new SerializerFingerPrinter());
|
||||||
|
|
||||||
SerializationOutput ser = new SerializationOutput(factory);
|
SerializationOutput ser = new SerializationOutput(factory);
|
||||||
DeserializationInput des = new DeserializationInput(factory);
|
DeserializationInput des = new DeserializationInput(factory);
|
||||||
|
|
||||||
@ -103,9 +104,9 @@ public class JavaPrivatePropertyTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCapitilsationOfIs() throws NotSerializableException, NoSuchFieldException, IllegalAccessException {
|
public void testCapitilsationOfIs() throws NotSerializableException, NoSuchFieldException, IllegalAccessException {
|
||||||
EvolutionSerializerGetterBase evolutionSerializerGetter = new EvolutionSerializerGetter();
|
|
||||||
SerializerFactory factory = new SerializerFactory(AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader(),
|
SerializerFactory factory = new SerializerFactory(AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader(),
|
||||||
evolutionSerializerGetter);
|
new EvolutionSerializerGetter(),
|
||||||
|
new SerializerFingerPrinter());
|
||||||
SerializationOutput ser = new SerializationOutput(factory);
|
SerializationOutput ser = new SerializationOutput(factory);
|
||||||
DeserializationInput des = new DeserializationInput(factory);
|
DeserializationInput des = new DeserializationInput(factory);
|
||||||
|
|
||||||
@ -119,9 +120,9 @@ public class JavaPrivatePropertyTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void singlePrivateIntWithBoolean() throws NotSerializableException, NoSuchFieldException, IllegalAccessException {
|
public void singlePrivateIntWithBoolean() throws NotSerializableException, NoSuchFieldException, IllegalAccessException {
|
||||||
EvolutionSerializerGetterBase evolutionSerializerGetter = new EvolutionSerializerGetter();
|
|
||||||
SerializerFactory factory = new SerializerFactory(AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader(),
|
SerializerFactory factory = new SerializerFactory(AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader(),
|
||||||
evolutionSerializerGetter);
|
new EvolutionSerializerGetter(),
|
||||||
|
new SerializerFingerPrinter());
|
||||||
SerializationOutput ser = new SerializationOutput(factory);
|
SerializationOutput ser = new SerializationOutput(factory);
|
||||||
DeserializationInput des = new DeserializationInput(factory);
|
DeserializationInput des = new DeserializationInput(factory);
|
||||||
|
|
||||||
@ -134,9 +135,10 @@ public class JavaPrivatePropertyTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void singlePrivateWithConstructor() throws NotSerializableException, NoSuchFieldException, IllegalAccessException {
|
public void singlePrivateWithConstructor() throws NotSerializableException, NoSuchFieldException, IllegalAccessException {
|
||||||
EvolutionSerializerGetterBase evolutionSerializerGetter = new EvolutionSerializerGetter();
|
|
||||||
SerializerFactory factory = new SerializerFactory(AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader(),
|
SerializerFactory factory = new SerializerFactory(AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader(),
|
||||||
evolutionSerializerGetter);
|
new EvolutionSerializerGetter(),
|
||||||
|
new SerializerFingerPrinter());
|
||||||
|
|
||||||
SerializationOutput ser = new SerializationOutput(factory);
|
SerializationOutput ser = new SerializationOutput(factory);
|
||||||
DeserializationInput des = new DeserializationInput(factory);
|
DeserializationInput des = new DeserializationInput(factory);
|
||||||
|
|
||||||
@ -164,9 +166,11 @@ public class JavaPrivatePropertyTests {
|
|||||||
@Test
|
@Test
|
||||||
public void singlePrivateWithConstructorAndGetter()
|
public void singlePrivateWithConstructorAndGetter()
|
||||||
throws NotSerializableException, NoSuchFieldException, IllegalAccessException {
|
throws NotSerializableException, NoSuchFieldException, IllegalAccessException {
|
||||||
EvolutionSerializerGetterBase evolutionSerializerGetter = new EvolutionSerializerGetter();
|
|
||||||
SerializerFactory factory = new SerializerFactory(AllWhitelist.INSTANCE,
|
SerializerFactory factory = new SerializerFactory(AllWhitelist.INSTANCE,
|
||||||
ClassLoader.getSystemClassLoader(), evolutionSerializerGetter);
|
ClassLoader.getSystemClassLoader(),
|
||||||
|
new EvolutionSerializerGetter(),
|
||||||
|
new SerializerFingerPrinter());
|
||||||
|
|
||||||
SerializationOutput ser = new SerializationOutput(factory);
|
SerializationOutput ser = new SerializationOutput(factory);
|
||||||
DeserializationInput des = new DeserializationInput(factory);
|
DeserializationInput des = new DeserializationInput(factory);
|
||||||
|
|
||||||
|
@ -29,9 +29,9 @@ public class JavaSerialiseEnumTests {
|
|||||||
public void testJavaConstructorAnnotations() throws NotSerializableException {
|
public void testJavaConstructorAnnotations() throws NotSerializableException {
|
||||||
Bra bra = new Bra(Bras.UNDERWIRE);
|
Bra bra = new Bra(Bras.UNDERWIRE);
|
||||||
|
|
||||||
EvolutionSerializerGetterBase evolutionSerialiserGetter = new EvolutionSerializerGetter();
|
|
||||||
SerializerFactory factory1 = new SerializerFactory(AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader(),
|
SerializerFactory factory1 = new SerializerFactory(AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader(),
|
||||||
evolutionSerialiserGetter);
|
new EvolutionSerializerGetter(),
|
||||||
|
new SerializerFingerPrinter());
|
||||||
SerializationOutput ser = new SerializationOutput(factory1);
|
SerializationOutput ser = new SerializationOutput(factory1);
|
||||||
SerializedBytes<Object> bytes = ser.serialize(bra);
|
SerializedBytes<Object> bytes = ser.serialize(bra);
|
||||||
}
|
}
|
||||||
|
@ -173,10 +173,13 @@ public class JavaSerializationOutputTests {
|
|||||||
|
|
||||||
private Object serdes(Object obj) throws NotSerializableException {
|
private Object serdes(Object obj) throws NotSerializableException {
|
||||||
EvolutionSerializerGetterBase evolutionSerialiserGetter = new EvolutionSerializerGetter();
|
EvolutionSerializerGetterBase evolutionSerialiserGetter = new EvolutionSerializerGetter();
|
||||||
|
FingerPrinter fingerPrinter = new SerializerFingerPrinter();
|
||||||
SerializerFactory factory1 = new SerializerFactory(AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader(),
|
SerializerFactory factory1 = new SerializerFactory(AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader(),
|
||||||
evolutionSerialiserGetter);
|
evolutionSerialiserGetter,
|
||||||
|
fingerPrinter);
|
||||||
SerializerFactory factory2 = new SerializerFactory(AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader(),
|
SerializerFactory factory2 = new SerializerFactory(AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader(),
|
||||||
evolutionSerialiserGetter);
|
evolutionSerialiserGetter,
|
||||||
|
fingerPrinter);
|
||||||
SerializationOutput ser = new SerializationOutput(factory1);
|
SerializationOutput ser = new SerializationOutput(factory1);
|
||||||
SerializedBytes<Object> bytes = ser.serialize(obj);
|
SerializedBytes<Object> bytes = ser.serialize(obj);
|
||||||
|
|
||||||
|
@ -125,9 +125,12 @@ public class ListsSerializationJavaTest {
|
|||||||
|
|
||||||
// Have to have own version as Kotlin inline functions cannot be easily called from Java
|
// Have to have own version as Kotlin inline functions cannot be easily called from Java
|
||||||
private static <T> void assertEqualAfterRoundTripSerialization(T container, Class<T> clazz) throws Exception {
|
private static <T> void assertEqualAfterRoundTripSerialization(T container, Class<T> clazz) throws Exception {
|
||||||
EvolutionSerializerGetterBase evolutionSerialiserGetter = new EvolutionSerializerGetter();
|
EvolutionSerializerGetterBase evolutionSerializerGetter = new EvolutionSerializerGetter();
|
||||||
SerializerFactory factory1 = new SerializerFactory(AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader(),
|
FingerPrinter fingerPrinter = new SerializerFingerPrinter();
|
||||||
evolutionSerialiserGetter);
|
SerializerFactory factory1 = new SerializerFactory(
|
||||||
|
AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader(),
|
||||||
|
evolutionSerializerGetter,
|
||||||
|
fingerPrinter);
|
||||||
SerializationOutput ser = new SerializationOutput(factory1);
|
SerializationOutput ser = new SerializationOutput(factory1);
|
||||||
SerializedBytes<Object> bytes = ser.serialize(container);
|
SerializedBytes<Object> bytes = ser.serialize(container);
|
||||||
DeserializationInput des = new DeserializationInput(factory1);
|
DeserializationInput des = new DeserializationInput(factory1);
|
||||||
|
@ -110,10 +110,12 @@ public class SetterConstructorTests {
|
|||||||
@Test
|
@Test
|
||||||
public void serialiseC() throws NotSerializableException {
|
public void serialiseC() throws NotSerializableException {
|
||||||
EvolutionSerializerGetterBase evolutionSerialiserGetter = new EvolutionSerializerGetter();
|
EvolutionSerializerGetterBase evolutionSerialiserGetter = new EvolutionSerializerGetter();
|
||||||
|
FingerPrinter fingerPrinter = new SerializerFingerPrinter();
|
||||||
SerializerFactory factory1 = new SerializerFactory(
|
SerializerFactory factory1 = new SerializerFactory(
|
||||||
AllWhitelist.INSTANCE,
|
AllWhitelist.INSTANCE,
|
||||||
ClassLoader.getSystemClassLoader(),
|
ClassLoader.getSystemClassLoader(),
|
||||||
evolutionSerialiserGetter);
|
evolutionSerialiserGetter,
|
||||||
|
fingerPrinter);
|
||||||
|
|
||||||
SerializationOutput ser = new SerializationOutput(factory1);
|
SerializationOutput ser = new SerializationOutput(factory1);
|
||||||
|
|
||||||
@ -184,10 +186,12 @@ public class SetterConstructorTests {
|
|||||||
@Test
|
@Test
|
||||||
public void deserialiseC() throws NotSerializableException {
|
public void deserialiseC() throws NotSerializableException {
|
||||||
EvolutionSerializerGetterBase evolutionSerialiserGetter = new EvolutionSerializerGetter();
|
EvolutionSerializerGetterBase evolutionSerialiserGetter = new EvolutionSerializerGetter();
|
||||||
|
FingerPrinter fingerPrinter = new SerializerFingerPrinter();
|
||||||
SerializerFactory factory1 = new SerializerFactory(
|
SerializerFactory factory1 = new SerializerFactory(
|
||||||
AllWhitelist.INSTANCE,
|
AllWhitelist.INSTANCE,
|
||||||
ClassLoader.getSystemClassLoader(),
|
ClassLoader.getSystemClassLoader(),
|
||||||
evolutionSerialiserGetter);
|
evolutionSerialiserGetter,
|
||||||
|
fingerPrinter);
|
||||||
|
|
||||||
C cPre1 = new C();
|
C cPre1 = new C();
|
||||||
|
|
||||||
@ -251,10 +255,12 @@ public class SetterConstructorTests {
|
|||||||
@Test
|
@Test
|
||||||
public void serialiseOuterAndInner() throws NotSerializableException {
|
public void serialiseOuterAndInner() throws NotSerializableException {
|
||||||
EvolutionSerializerGetterBase evolutionSerialiserGetter = new EvolutionSerializerGetter();
|
EvolutionSerializerGetterBase evolutionSerialiserGetter = new EvolutionSerializerGetter();
|
||||||
|
FingerPrinter fingerPrinter = new SerializerFingerPrinter();
|
||||||
SerializerFactory factory1 = new SerializerFactory(
|
SerializerFactory factory1 = new SerializerFactory(
|
||||||
AllWhitelist.INSTANCE,
|
AllWhitelist.INSTANCE,
|
||||||
ClassLoader.getSystemClassLoader(),
|
ClassLoader.getSystemClassLoader(),
|
||||||
evolutionSerialiserGetter);
|
evolutionSerialiserGetter,
|
||||||
|
fingerPrinter);
|
||||||
|
|
||||||
Inner1 i1 = new Inner1("Hello");
|
Inner1 i1 = new Inner1("Hello");
|
||||||
Inner2 i2 = new Inner2();
|
Inner2 i2 = new Inner2();
|
||||||
@ -277,10 +283,12 @@ public class SetterConstructorTests {
|
|||||||
@Test
|
@Test
|
||||||
public void typeMistmatch() throws NotSerializableException {
|
public void typeMistmatch() throws NotSerializableException {
|
||||||
EvolutionSerializerGetterBase evolutionSerialiserGetter = new EvolutionSerializerGetter();
|
EvolutionSerializerGetterBase evolutionSerialiserGetter = new EvolutionSerializerGetter();
|
||||||
|
FingerPrinter fingerPrinter = new SerializerFingerPrinter();
|
||||||
SerializerFactory factory1 = new SerializerFactory(
|
SerializerFactory factory1 = new SerializerFactory(
|
||||||
AllWhitelist.INSTANCE,
|
AllWhitelist.INSTANCE,
|
||||||
ClassLoader.getSystemClassLoader(),
|
ClassLoader.getSystemClassLoader(),
|
||||||
evolutionSerialiserGetter);
|
evolutionSerialiserGetter,
|
||||||
|
fingerPrinter);
|
||||||
|
|
||||||
TypeMismatch tm = new TypeMismatch();
|
TypeMismatch tm = new TypeMismatch();
|
||||||
tm.setA(10);
|
tm.setA(10);
|
||||||
@ -293,10 +301,12 @@ public class SetterConstructorTests {
|
|||||||
@Test
|
@Test
|
||||||
public void typeMistmatch2() throws NotSerializableException {
|
public void typeMistmatch2() throws NotSerializableException {
|
||||||
EvolutionSerializerGetterBase evolutionSerialiserGetter = new EvolutionSerializerGetter();
|
EvolutionSerializerGetterBase evolutionSerialiserGetter = new EvolutionSerializerGetter();
|
||||||
|
FingerPrinter fingerPrinter = new SerializerFingerPrinter();
|
||||||
SerializerFactory factory1 = new SerializerFactory(
|
SerializerFactory factory1 = new SerializerFactory(
|
||||||
AllWhitelist.INSTANCE,
|
AllWhitelist.INSTANCE,
|
||||||
ClassLoader.getSystemClassLoader(),
|
ClassLoader.getSystemClassLoader(),
|
||||||
evolutionSerialiserGetter);
|
evolutionSerialiserGetter,
|
||||||
|
fingerPrinter);
|
||||||
|
|
||||||
TypeMismatch2 tm = new TypeMismatch2();
|
TypeMismatch2 tm = new TypeMismatch2();
|
||||||
tm.setA("10");
|
tm.setA("10");
|
||||||
|
@ -0,0 +1,55 @@
|
|||||||
|
package net.corda.nodeapi.internal.serialization.amqp
|
||||||
|
|
||||||
|
import org.junit.Test
|
||||||
|
import java.lang.reflect.Type
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
import net.corda.nodeapi.internal.serialization.AllWhitelist
|
||||||
|
|
||||||
|
class FingerPrinterTesting : FingerPrinter {
|
||||||
|
private var index = 0
|
||||||
|
private val cache = mutableMapOf<Type, String>()
|
||||||
|
|
||||||
|
override fun fingerprint(type: Type): String {
|
||||||
|
return cache.computeIfAbsent(type) { index++.toString() }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setOwner(factory: SerializerFactory) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("UNUSED")
|
||||||
|
fun changeFingerprint(type: Type) {
|
||||||
|
cache.computeIfAbsent(type) { "" }.apply { index++.toString() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class FingerPrinterTestingTests {
|
||||||
|
companion object {
|
||||||
|
val VERBOSE = true
|
||||||
|
}
|
||||||
|
@Test
|
||||||
|
fun testingTest() {
|
||||||
|
val fpt = FingerPrinterTesting()
|
||||||
|
assertEquals ("0", fpt.fingerprint(Integer::class.java))
|
||||||
|
assertEquals ("1", fpt.fingerprint(String::class.java))
|
||||||
|
assertEquals ("0", fpt.fingerprint(Integer::class.java))
|
||||||
|
assertEquals ("1", fpt.fingerprint(String::class.java))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun worksAsReplacement() {
|
||||||
|
data class C (val a: Int, val b: Long)
|
||||||
|
|
||||||
|
val factory = SerializerFactory(
|
||||||
|
AllWhitelist,
|
||||||
|
ClassLoader.getSystemClassLoader(),
|
||||||
|
EvolutionSerializerGetterTesting(),
|
||||||
|
FingerPrinterTesting())
|
||||||
|
|
||||||
|
val blob = TestSerializationOutput(VERBOSE, factory).serializeAndReturnSchema(C(1, 2L))
|
||||||
|
|
||||||
|
assertEquals (1, blob.schema.types.size)
|
||||||
|
assertEquals ("<descriptor name=\"net.corda:0\"/>", blob.schema.types[0].descriptor.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user