mirror of
https://github.com/corda/corda.git
synced 2025-04-19 16:41:13 +00:00
Ensure that described properties are associated with a descriptor.
This commit is contained in:
parent
8f0c7c947a
commit
e4f38d1945
serialization-deterministic
serialization/src/main/kotlin/net/corda/serialization/internal
@ -22,12 +22,12 @@ dependencies {
|
||||
// Configure these by hand. It should be a minimal subset of dependencies,
|
||||
// and without any obviously non-deterministic ones such as Hibernate.
|
||||
|
||||
// This dependency will become "compile" scoped in our published POM.
|
||||
// These dependencies will become "compile" scoped in our published POM.
|
||||
// See publish.dependenciesFrom.defaultScope.
|
||||
deterministicLibraries project(path: ':core-deterministic', configuration: 'deterministicArtifacts')
|
||||
deterministicLibraries "org.apache.qpid:proton-j:$protonj_version"
|
||||
|
||||
// These "implementation" dependencies will become "runtime" scoped in our published POM.
|
||||
implementation "org.apache.qpid:proton-j:$protonj_version"
|
||||
implementation "org.iq80.snappy:snappy:$snappy_version"
|
||||
implementation "com.google.guava:guava:$guava_version"
|
||||
}
|
||||
|
@ -88,7 +88,9 @@ open class ArraySerializer(override val type: Type, factory: LocalSerializerFact
|
||||
context: SerializationContext
|
||||
): Any {
|
||||
if (obj is List<*>) {
|
||||
return obj.map { input.readObjectOrNull(it, schemas, elementType, context) }.toArrayOfType(elementType)
|
||||
return obj.map {
|
||||
input.readObjectOrNull(redescribe(it, elementType), schemas, elementType, context)
|
||||
}.toArrayOfType(elementType)
|
||||
} else throw AMQPNotSerializableException(type, "Expected a List but found $obj")
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
package net.corda.serialization.internal.amqp
|
||||
|
||||
import net.corda.core.serialization.SerializationContext
|
||||
import net.corda.serialization.internal.amqp.AMQPTypeIdentifiers.isPrimitive
|
||||
import net.corda.serialization.internal.model.*
|
||||
import org.apache.qpid.proton.amqp.Binary
|
||||
import org.apache.qpid.proton.codec.Data
|
||||
@ -18,7 +19,7 @@ interface PropertyReadStrategy {
|
||||
* Select the correct strategy for reading properties, based on the property type.
|
||||
*/
|
||||
fun make(name: String, typeIdentifier: TypeIdentifier, type: Type): PropertyReadStrategy =
|
||||
if (AMQPTypeIdentifiers.isPrimitive(typeIdentifier)) {
|
||||
if (isPrimitive(typeIdentifier)) {
|
||||
when (typeIdentifier) {
|
||||
in characterTypes -> AMQPCharPropertyReadStrategy
|
||||
else -> AMQPPropertyReadStrategy
|
||||
@ -47,7 +48,7 @@ interface PropertyWriteStrategy {
|
||||
fun make(name: String, propertyInformation: LocalPropertyInformation, factory: LocalSerializerFactory): PropertyWriteStrategy {
|
||||
val reader = PropertyReader.make(propertyInformation)
|
||||
val type = propertyInformation.type
|
||||
return if (AMQPTypeIdentifiers.isPrimitive(type.typeIdentifier)) {
|
||||
return if (isPrimitive(type.typeIdentifier)) {
|
||||
when (type.typeIdentifier) {
|
||||
in characterTypes -> AMQPCharPropertyWriteStategy(reader)
|
||||
else -> AMQPPropertyWriteStrategy(reader)
|
||||
@ -191,15 +192,14 @@ object EvolutionPropertyWriteStrategy : PropertyWriteStrategy {
|
||||
* Read a type that comes with its own [TypeDescriptor], by calling back into [RemoteSerializerFactory] to obtain a suitable
|
||||
* serializer for that descriptor.
|
||||
*/
|
||||
class DescribedTypeReadStrategy(name: String,
|
||||
typeIdentifier: TypeIdentifier,
|
||||
class DescribedTypeReadStrategy(name: String, typeIdentifier: TypeIdentifier,
|
||||
private val type: Type): PropertyReadStrategy {
|
||||
|
||||
private val nameForDebug = "$name(${typeIdentifier.prettyPrint(false)})"
|
||||
|
||||
override fun readProperty(obj: Any?, schemas: SerializationSchemas, input: DeserializationInput, context: SerializationContext): Any? =
|
||||
ifThrowsAppend({ nameForDebug }) {
|
||||
input.readObjectOrNull(obj, schemas, type, context)
|
||||
input.readObjectOrNull(redescribe(obj, type), schemas, type, context)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -123,11 +123,13 @@ abstract class CustomSerializer<T : Any> : AMQPSerializer<T>, SerializerFor {
|
||||
/**
|
||||
* Additional base features for a custom serializer for a particular class, that excludes subclasses.
|
||||
*/
|
||||
@KeepForDJVM
|
||||
abstract class Is<T : Any>(clazz: Class<T>) : CustomSerializerImp<T>(clazz, false)
|
||||
|
||||
/**
|
||||
* Additional base features for a custom serializer for all implementations of a particular interface or super class.
|
||||
*/
|
||||
@KeepForDJVM
|
||||
abstract class Implements<T : Any>(clazz: Class<T>) : CustomSerializerImp<T>(clazz, true)
|
||||
|
||||
/**
|
||||
@ -137,6 +139,7 @@ abstract class CustomSerializer<T : Any> : AMQPSerializer<T>, SerializerFor {
|
||||
* The proxy class must use only types which are either native AMQP or other types for which there are pre-registered
|
||||
* custom serializers.
|
||||
*/
|
||||
@KeepForDJVM
|
||||
abstract class Proxy<T : Any, P : Any>(clazz: Class<T>,
|
||||
protected val proxyClass: Class<P>,
|
||||
protected val factory: LocalSerializerFactory,
|
||||
@ -195,6 +198,7 @@ abstract class CustomSerializer<T : Any> : AMQPSerializer<T>, SerializerFor {
|
||||
* @param maker A lambda for constructing an instance, that defaults to calling a constructor that expects a string.
|
||||
* @param unmaker A lambda that extracts the string value for an instance, that defaults to the [toString] method.
|
||||
*/
|
||||
@KeepForDJVM
|
||||
abstract class ToString<T : Any>(clazz: Class<T>, withInheritance: Boolean = false,
|
||||
private val maker: (String) -> T = clazz.getConstructor(String::class.java).let { `constructor` ->
|
||||
{ string -> `constructor`.newInstance(string) }
|
||||
|
@ -3,10 +3,10 @@ package net.corda.serialization.internal.amqp
|
||||
import net.corda.core.KeepForDJVM
|
||||
import net.corda.core.internal.uncheckedCast
|
||||
import net.corda.serialization.internal.CordaSerializationMagic
|
||||
import org.apache.qpid.proton.amqp.DescribedType
|
||||
import org.apache.qpid.proton.amqp.Symbol
|
||||
import org.apache.qpid.proton.amqp.UnsignedInteger
|
||||
import org.apache.qpid.proton.amqp.UnsignedLong
|
||||
import net.corda.serialization.internal.amqp.AMQPTypeIdentifiers.isPrimitive
|
||||
import net.corda.serialization.internal.model.TypeIdentifier.TopType
|
||||
import net.corda.serialization.internal.model.TypeIdentifier.Companion.forGenericType
|
||||
import org.apache.qpid.proton.amqp.*
|
||||
import org.apache.qpid.proton.codec.DescribedTypeConstructor
|
||||
import java.io.NotSerializableException
|
||||
import java.lang.reflect.Type
|
||||
@ -16,6 +16,26 @@ val amqpMagic = CordaSerializationMagic("corda".toByteArray() + byteArrayOf(1, 0
|
||||
|
||||
fun typeDescriptorFor(type: Type): Symbol = Symbol.valueOf("$DESCRIPTOR_DOMAIN:${AMQPTypeIdentifiers.nameForType(type)}")
|
||||
|
||||
fun redescribe(obj: Any?, type: Type): Any? {
|
||||
return if (obj == null || obj is DescribedType || obj is Binary || forGenericType(type).run { isPrimitive(this) || this == TopType }) {
|
||||
obj
|
||||
} else {
|
||||
/**
|
||||
* This must be a primitive [obj] that has a non-primitive [type].
|
||||
* Rewrap it with the required descriptor for further deserialization.
|
||||
*/
|
||||
RedescribedType(typeDescriptorFor(type), obj)
|
||||
}
|
||||
}
|
||||
|
||||
private class RedescribedType(
|
||||
private val descriptor: Symbol,
|
||||
private val described: Any?
|
||||
) : DescribedType {
|
||||
override fun getDescriptor(): Symbol = descriptor
|
||||
override fun getDescribed(): Any? = described
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
|
@ -206,7 +206,10 @@ sealed class TypeIdentifier {
|
||||
|
||||
override fun toString() = "Parameterised(${prettyPrint()})"
|
||||
override fun getLocalType(classLoader: ClassLoader): Type {
|
||||
val rawType = Class.forName(name, false, classLoader)
|
||||
// We need to invoke ClassLoader.loadClass() directly, because
|
||||
// the JVM will complain if Class.forName() returns a class
|
||||
// that has a name other than the requested one.
|
||||
val rawType = classLoader.loadClass(name)
|
||||
if (rawType.typeParameters.size != parameters.size) {
|
||||
throw IncompatibleTypeIdentifierException(
|
||||
"Class $rawType expects ${rawType.typeParameters.size} type arguments, " +
|
||||
|
Loading…
x
Reference in New Issue
Block a user