Some custom serializers, fixes to generics handling, some annotations and added NonEmptySet as a special case of Set. (#1330)

This commit is contained in:
Rick Parker 2017-08-25 15:09:43 +01:00 committed by GitHub
parent 34b7c89b40
commit c61b036844
10 changed files with 115 additions and 6 deletions

View File

@ -11,9 +11,9 @@ import java.security.SignatureException
// should be renamed to match.
/** A wrapper around a digital signature. */
@CordaSerializable
open class DigitalSignature(bits: ByteArray) : OpaqueBytes(bits) {
open class DigitalSignature(bytes: ByteArray) : OpaqueBytes(bytes) {
/** A digital signature that identifies who the public key is owned by. */
open class WithKey(val by: PublicKey, bits: ByteArray) : DigitalSignature(bits) {
open class WithKey(val by: PublicKey, bytes: ByteArray) : DigitalSignature(bytes) {
/**
* Utility to simplify the act of verifying a signature.
*

View File

@ -1,7 +1,10 @@
package net.corda.core.messaging
import net.corda.core.serialization.CordaSerializable
/** The interface for a group of message recipients (which may contain only one recipient) */
@CordaSerializable
interface MessageRecipients
/** A base class for the case of point-to-point messages */

View File

@ -1,5 +1,6 @@
package net.corda.core.utilities
import net.corda.core.serialization.CordaSerializable
import java.net.URI
/**
@ -7,6 +8,7 @@ import java.net.URI
* @param host a hostname or IP address. IPv6 addresses must not be enclosed in square brackets.
* @param port a valid port number.
*/
@CordaSerializable
data class NetworkHostAndPort(val host: String, val port: Int) {
companion object {
internal val invalidPortFormat = "Invalid port: %s"

View File

@ -37,6 +37,8 @@ abstract class AbstractAMQPSerializationScheme : SerializationScheme {
register(net.corda.nodeapi.internal.serialization.amqp.custom.MonthDaySerializer(this))
register(net.corda.nodeapi.internal.serialization.amqp.custom.PeriodSerializer(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.PartyAndCertificateSerializer(factory))
}
}
}

View File

@ -1,5 +1,6 @@
package net.corda.nodeapi.internal.serialization.amqp
import net.corda.core.utilities.NonEmptySet
import org.apache.qpid.proton.codec.Data
import java.io.NotSerializableException
import java.lang.reflect.ParameterizedType
@ -22,7 +23,8 @@ class CollectionSerializer(val declaredType: ParameterizedType, factory: Seriali
List::class.java to { list -> Collections.unmodifiableList(list) },
Set::class.java to { list -> Collections.unmodifiableSet(LinkedHashSet(list)) },
SortedSet::class.java to { list -> Collections.unmodifiableSortedSet(TreeSet(list)) },
NavigableSet::class.java to { list -> Collections.unmodifiableNavigableSet(TreeSet(list)) }
NavigableSet::class.java to { list -> Collections.unmodifiableNavigableSet(TreeSet(list)) },
NonEmptySet::class.java to { list -> NonEmptySet.copyOf(list) }
)
private fun findConcreteType(clazz: Class<*>): (List<*>) -> Collection<*> {

View File

@ -373,7 +373,7 @@ private fun fingerprintForType(type: Type, contextType: Type?, alreadySeen: Muta
// to the CorDapp but maybe reference to the JAR in the short term.
hasher.putUnencodedChars(type.name)
} else {
fingerprintForObject(type, contextType, alreadySeen, hasher, factory)
fingerprintForObject(type, type, alreadySeen, hasher, factory)
}
}
}

View File

@ -81,7 +81,7 @@ private fun <T : Any> propertiesForSerializationFromConstructor(kotlinConstructo
val getter = matchingProperty.readMethod ?: throw NotSerializableException("Property has no getter method for $name of $clazz." +
" If using Java and the parameter name looks anonymous, check that you have the -parameters option specified in the Java compiler.")
val returnType = resolveTypeVariables(getter.genericReturnType, type)
if (constructorParamTakesReturnTypeOfGetter(getter, param)) {
if (constructorParamTakesReturnTypeOfGetter(returnType, getter.genericReturnType, param)) {
rc += PropertySerializer.make(name, getter, returnType, factory)
} else {
throw NotSerializableException("Property type $returnType for $name of $clazz differs from constructor parameter type ${param.type.javaType}")
@ -90,7 +90,10 @@ private fun <T : Any> propertiesForSerializationFromConstructor(kotlinConstructo
return rc
}
private fun constructorParamTakesReturnTypeOfGetter(getter: Method, param: KParameter): Boolean = TypeToken.of(param.type.javaType).isSupertypeOf(getter.genericReturnType)
private fun constructorParamTakesReturnTypeOfGetter(getterReturnType: Type, rawGetterReturnType: Type, param: KParameter): Boolean {
val typeToken = TypeToken.of(param.type.javaType)
return typeToken.isSupertypeOf(getterReturnType) || typeToken.isSupertypeOf(rawGetterReturnType)
}
private fun propertiesForSerializationFromAbstract(clazz: Class<*>, type: Type, factory: SerializerFactory): Collection<PropertySerializer> {
// Kotlin reflection doesn't work with Java getters the way you might expect, so we drop back to good ol' beans.

View File

@ -0,0 +1,30 @@
package net.corda.nodeapi.internal.serialization.amqp.custom
import net.corda.core.crypto.Crypto
import net.corda.core.identity.PartyAndCertificate
import net.corda.nodeapi.internal.serialization.amqp.*
import java.io.ByteArrayInputStream
import java.io.NotSerializableException
import java.security.cert.CertPath
import java.security.cert.CertificateException
import java.security.cert.CertificateFactory
/**
* A serializer that writes out a party and certificate in encoded format.
*/
class PartyAndCertificateSerializer(factory: SerializerFactory) : CustomSerializer.Proxy<PartyAndCertificate, PartyAndCertificateSerializer.PartyAndCertificateProxy>(PartyAndCertificate::class.java, PartyAndCertificateProxy::class.java, factory) {
override fun toProxy(obj: PartyAndCertificate): PartyAndCertificateProxy = PartyAndCertificateProxy(obj.certPath.type, obj.certPath.encoded)
override fun fromProxy(proxy: PartyAndCertificateProxy): PartyAndCertificate {
try {
val cf = CertificateFactory.getInstance(proxy.type)
return PartyAndCertificate(cf.generateCertPath(ByteArrayInputStream(proxy.encoded)))
} catch (ce: CertificateException) {
val nse = NotSerializableException("java.security.cert.CertPath: " + type)
nse.initCause(ce)
throw nse
}
}
data class PartyAndCertificateProxy(val type: String, val encoded: ByteArray)
}

View File

@ -0,0 +1,23 @@
package net.corda.nodeapi.internal.serialization.amqp.custom
import net.corda.core.crypto.Crypto
import net.corda.nodeapi.internal.serialization.amqp.*
import org.apache.qpid.proton.codec.Data
import org.bouncycastle.cert.X509CertificateHolder
import java.lang.reflect.Type
/**
* A serializer that writes out a certificate in X.509 format.
*/
object X509CertificateHolderSerializer : CustomSerializer.Implements<X509CertificateHolder>(X509CertificateHolder::class.java) {
override val schemaForDocumentation = Schema(listOf(RestrictedType(type.toString(), "", listOf(type.toString()), SerializerFactory.primitiveTypeName(ByteArray::class.java)!!, descriptor, emptyList())))
override fun writeDescribedObject(obj: X509CertificateHolder, data: Data, type: Type, output: SerializationOutput) {
output.writeObject(obj.encoded, data, clazz)
}
override fun readObject(obj: Any, schema: Schema, input: DeserializationInput): X509CertificateHolder {
val bits = input.readObject(obj, schema, ByteArray::class.java) as ByteArray
return X509CertificateHolder(bits)
}
}

View File

@ -15,11 +15,13 @@ import net.corda.nodeapi.internal.serialization.AbstractAMQPSerializationScheme
import net.corda.nodeapi.internal.serialization.EmptyWhitelist
import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory.Companion.isPrimitive
import net.corda.nodeapi.internal.serialization.AllWhitelist
import net.corda.testing.BOB_IDENTITY
import net.corda.testing.MEGA_CORP
import net.corda.testing.MEGA_CORP_PUBKEY
import org.apache.qpid.proton.amqp.*
import org.apache.qpid.proton.codec.DecoderImpl
import org.apache.qpid.proton.codec.EncoderImpl
import org.junit.Ignore
import org.junit.Test
import java.io.IOException
import java.io.NotSerializableException
@ -710,6 +712,48 @@ class SerializationOutputTests {
serdes(obj, factory, factory2)
}
// TODO: ignored due to Proton-J bug https://issues.apache.org/jira/browse/PROTON-1551
@Ignore
@Test
fun `test certificate holder serialize`() {
val factory = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader())
factory.register(net.corda.nodeapi.internal.serialization.amqp.custom.X509CertificateHolderSerializer)
val factory2 = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader())
factory2.register(net.corda.nodeapi.internal.serialization.amqp.custom.X509CertificateHolderSerializer)
val obj = BOB_IDENTITY.certificate
serdes(obj, factory, factory2)
}
// TODO: ignored due to Proton-J bug https://issues.apache.org/jira/browse/PROTON-1551
@Ignore
@Test
fun `test party and certificate serialize`() {
val factory = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader())
factory.register(net.corda.nodeapi.internal.serialization.amqp.custom.PartyAndCertificateSerializer(factory))
val factory2 = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader())
factory2.register(net.corda.nodeapi.internal.serialization.amqp.custom.PartyAndCertificateSerializer(factory2))
val obj = BOB_IDENTITY
serdes(obj, factory, factory2)
}
class OtherGeneric<T : Any>
open class GenericSuperclass<T : Any>(val param: OtherGeneric<T>)
class GenericSubclass(param: OtherGeneric<String>) : GenericSuperclass<String>(param) {
override fun equals(other: Any?): Boolean = other is GenericSubclass // This is a bit lame but we just want to check it doesn't throw exceptions
}
@Test
fun `test generic in constructor serialize`() {
val obj = GenericSubclass(OtherGeneric<String>())
serdes(obj)
}
@Test
fun `test StateRef serialize`() {
val factory = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader())