Cleanup and improvements to the serialisation format of JacksonSupport (needed for CORDA-1238) (#3102)

Also deprecated all the public members that shouldn't have leaked into the public API.
This commit is contained in:
Shams Asari 2018-05-09 21:42:55 +01:00 committed by GitHub
parent 3f21c47f39
commit 3bb95c3ed1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 488 additions and 281 deletions

View File

@ -3618,9 +3618,9 @@ public final class net.corda.client.jackson.JacksonSupport extends java.lang.Obj
@org.jetbrains.annotations.NotNull public static final com.fasterxml.jackson.databind.ObjectMapper createDefaultMapper(net.corda.core.messaging.CordaRPCOps) @org.jetbrains.annotations.NotNull public static final com.fasterxml.jackson.databind.ObjectMapper createDefaultMapper(net.corda.core.messaging.CordaRPCOps)
@org.jetbrains.annotations.NotNull public static final com.fasterxml.jackson.databind.ObjectMapper createDefaultMapper(net.corda.core.messaging.CordaRPCOps, com.fasterxml.jackson.core.JsonFactory) @org.jetbrains.annotations.NotNull public static final com.fasterxml.jackson.databind.ObjectMapper createDefaultMapper(net.corda.core.messaging.CordaRPCOps, com.fasterxml.jackson.core.JsonFactory)
@org.jetbrains.annotations.NotNull public static final com.fasterxml.jackson.databind.ObjectMapper createDefaultMapper(net.corda.core.messaging.CordaRPCOps, com.fasterxml.jackson.core.JsonFactory, boolean) @org.jetbrains.annotations.NotNull public static final com.fasterxml.jackson.databind.ObjectMapper createDefaultMapper(net.corda.core.messaging.CordaRPCOps, com.fasterxml.jackson.core.JsonFactory, boolean)
@org.jetbrains.annotations.NotNull public static final com.fasterxml.jackson.databind.ObjectMapper createInMemoryMapper(net.corda.core.node.services.IdentityService) @kotlin.Deprecated @org.jetbrains.annotations.NotNull public static final com.fasterxml.jackson.databind.ObjectMapper createInMemoryMapper(net.corda.core.node.services.IdentityService)
@org.jetbrains.annotations.NotNull public static final com.fasterxml.jackson.databind.ObjectMapper createInMemoryMapper(net.corda.core.node.services.IdentityService, com.fasterxml.jackson.core.JsonFactory) @kotlin.Deprecated @org.jetbrains.annotations.NotNull public static final com.fasterxml.jackson.databind.ObjectMapper createInMemoryMapper(net.corda.core.node.services.IdentityService, com.fasterxml.jackson.core.JsonFactory)
@org.jetbrains.annotations.NotNull public static final com.fasterxml.jackson.databind.ObjectMapper createInMemoryMapper(net.corda.core.node.services.IdentityService, com.fasterxml.jackson.core.JsonFactory, boolean) @kotlin.Deprecated @org.jetbrains.annotations.NotNull public static final com.fasterxml.jackson.databind.ObjectMapper createInMemoryMapper(net.corda.core.node.services.IdentityService, com.fasterxml.jackson.core.JsonFactory, boolean)
@org.jetbrains.annotations.NotNull public static final com.fasterxml.jackson.databind.ObjectMapper createNonRpcMapper() @org.jetbrains.annotations.NotNull public static final com.fasterxml.jackson.databind.ObjectMapper createNonRpcMapper()
@org.jetbrains.annotations.NotNull public static final com.fasterxml.jackson.databind.ObjectMapper createNonRpcMapper(com.fasterxml.jackson.core.JsonFactory) @org.jetbrains.annotations.NotNull public static final com.fasterxml.jackson.databind.ObjectMapper createNonRpcMapper(com.fasterxml.jackson.core.JsonFactory)
@org.jetbrains.annotations.NotNull public final com.fasterxml.jackson.databind.Module getCordaModule() @org.jetbrains.annotations.NotNull public final com.fasterxml.jackson.databind.Module getCordaModule()
@ -3650,7 +3650,7 @@ public static final class net.corda.client.jackson.JacksonSupport$CordaX500NameS
public void serialize(net.corda.core.identity.CordaX500Name, com.fasterxml.jackson.core.JsonGenerator, com.fasterxml.jackson.databind.SerializerProvider) public void serialize(net.corda.core.identity.CordaX500Name, com.fasterxml.jackson.core.JsonGenerator, com.fasterxml.jackson.databind.SerializerProvider)
public static final net.corda.client.jackson.JacksonSupport$CordaX500NameSerializer INSTANCE public static final net.corda.client.jackson.JacksonSupport$CordaX500NameSerializer INSTANCE
## ##
public static final class net.corda.client.jackson.JacksonSupport$IdentityObjectMapper extends com.fasterxml.jackson.databind.ObjectMapper implements net.corda.client.jackson.JacksonSupport$PartyObjectMapper @net.corda.core.DoNotImplement public static final class net.corda.client.jackson.JacksonSupport$IdentityObjectMapper extends com.fasterxml.jackson.databind.ObjectMapper implements net.corda.client.jackson.JacksonSupport$PartyObjectMapper
public <init>(net.corda.core.node.services.IdentityService, com.fasterxml.jackson.core.JsonFactory, boolean) public <init>(net.corda.core.node.services.IdentityService, com.fasterxml.jackson.core.JsonFactory, boolean)
public final boolean getFuzzyIdentityMatch() public final boolean getFuzzyIdentityMatch()
@org.jetbrains.annotations.NotNull public final net.corda.core.node.services.IdentityService getIdentityService() @org.jetbrains.annotations.NotNull public final net.corda.core.node.services.IdentityService getIdentityService()
@ -3658,9 +3658,9 @@ public static final class net.corda.client.jackson.JacksonSupport$IdentityObject
@org.jetbrains.annotations.Nullable public net.corda.core.identity.Party partyFromKey(java.security.PublicKey) @org.jetbrains.annotations.Nullable public net.corda.core.identity.Party partyFromKey(java.security.PublicKey)
@org.jetbrains.annotations.Nullable public net.corda.core.identity.Party wellKnownPartyFromX500Name(net.corda.core.identity.CordaX500Name) @org.jetbrains.annotations.Nullable public net.corda.core.identity.Party wellKnownPartyFromX500Name(net.corda.core.identity.CordaX500Name)
## ##
public static final class net.corda.client.jackson.JacksonSupport$NoPartyObjectMapper extends com.fasterxml.jackson.databind.ObjectMapper implements net.corda.client.jackson.JacksonSupport$PartyObjectMapper @net.corda.core.DoNotImplement public static final class net.corda.client.jackson.JacksonSupport$NoPartyObjectMapper extends com.fasterxml.jackson.databind.ObjectMapper implements net.corda.client.jackson.JacksonSupport$PartyObjectMapper
public <init>(com.fasterxml.jackson.core.JsonFactory) public <init>(com.fasterxml.jackson.core.JsonFactory)
@org.jetbrains.annotations.NotNull public Void partiesFromName(String) @org.jetbrains.annotations.NotNull public Set partiesFromName(String)
@org.jetbrains.annotations.Nullable public net.corda.core.identity.Party partyFromKey(java.security.PublicKey) @org.jetbrains.annotations.Nullable public net.corda.core.identity.Party partyFromKey(java.security.PublicKey)
@org.jetbrains.annotations.Nullable public net.corda.core.identity.Party wellKnownPartyFromX500Name(net.corda.core.identity.CordaX500Name) @org.jetbrains.annotations.Nullable public net.corda.core.identity.Party wellKnownPartyFromX500Name(net.corda.core.identity.CordaX500Name)
## ##
@ -3684,7 +3684,7 @@ public static final class net.corda.client.jackson.JacksonSupport$PartyDeseriali
@org.jetbrains.annotations.NotNull public net.corda.core.identity.Party deserialize(com.fasterxml.jackson.core.JsonParser, com.fasterxml.jackson.databind.DeserializationContext) @org.jetbrains.annotations.NotNull public net.corda.core.identity.Party deserialize(com.fasterxml.jackson.core.JsonParser, com.fasterxml.jackson.databind.DeserializationContext)
public static final net.corda.client.jackson.JacksonSupport$PartyDeserializer INSTANCE public static final net.corda.client.jackson.JacksonSupport$PartyDeserializer INSTANCE
## ##
public static interface net.corda.client.jackson.JacksonSupport$PartyObjectMapper @net.corda.core.DoNotImplement public static interface net.corda.client.jackson.JacksonSupport$PartyObjectMapper
@org.jetbrains.annotations.NotNull public abstract Set partiesFromName(String) @org.jetbrains.annotations.NotNull public abstract Set partiesFromName(String)
@org.jetbrains.annotations.Nullable public abstract net.corda.core.identity.Party partyFromKey(java.security.PublicKey) @org.jetbrains.annotations.Nullable public abstract net.corda.core.identity.Party partyFromKey(java.security.PublicKey)
@org.jetbrains.annotations.Nullable public abstract net.corda.core.identity.Party wellKnownPartyFromX500Name(net.corda.core.identity.CordaX500Name) @org.jetbrains.annotations.Nullable public abstract net.corda.core.identity.Party wellKnownPartyFromX500Name(net.corda.core.identity.CordaX500Name)
@ -3701,7 +3701,7 @@ public static final class net.corda.client.jackson.JacksonSupport$PublicKeySeria
public void serialize(java.security.PublicKey, com.fasterxml.jackson.core.JsonGenerator, com.fasterxml.jackson.databind.SerializerProvider) public void serialize(java.security.PublicKey, com.fasterxml.jackson.core.JsonGenerator, com.fasterxml.jackson.databind.SerializerProvider)
public static final net.corda.client.jackson.JacksonSupport$PublicKeySerializer INSTANCE public static final net.corda.client.jackson.JacksonSupport$PublicKeySerializer INSTANCE
## ##
public static final class net.corda.client.jackson.JacksonSupport$RpcObjectMapper extends com.fasterxml.jackson.databind.ObjectMapper implements net.corda.client.jackson.JacksonSupport$PartyObjectMapper @net.corda.core.DoNotImplement public static final class net.corda.client.jackson.JacksonSupport$RpcObjectMapper extends com.fasterxml.jackson.databind.ObjectMapper implements net.corda.client.jackson.JacksonSupport$PartyObjectMapper
public <init>(net.corda.core.messaging.CordaRPCOps, com.fasterxml.jackson.core.JsonFactory, boolean) public <init>(net.corda.core.messaging.CordaRPCOps, com.fasterxml.jackson.core.JsonFactory, boolean)
public final boolean getFuzzyIdentityMatch() public final boolean getFuzzyIdentityMatch()
@org.jetbrains.annotations.NotNull public final net.corda.core.messaging.CordaRPCOps getRpc() @org.jetbrains.annotations.NotNull public final net.corda.core.messaging.CordaRPCOps getRpc()

View File

@ -40,7 +40,6 @@ buildscript {
ext.jackson_version = '2.9.3' ext.jackson_version = '2.9.3'
ext.jetty_version = '9.4.7.v20170914' ext.jetty_version = '9.4.7.v20170914'
ext.jersey_version = '2.25' ext.jersey_version = '2.25'
ext.json_version = '20180130'
ext.assertj_version = '3.8.0' ext.assertj_version = '3.8.0'
ext.slf4j_version = '1.7.25' ext.slf4j_version = '1.7.25'
ext.log4j_version = '2.9.1' ext.log4j_version = '2.9.1'

View File

@ -2,56 +2,42 @@ package net.corda.client.jackson
import com.fasterxml.jackson.annotation.JsonIgnore import com.fasterxml.jackson.annotation.JsonIgnore
import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.annotation.JsonProperty
import com.fasterxml.jackson.core.JsonFactory import com.fasterxml.jackson.core.*
import com.fasterxml.jackson.core.JsonGenerator import com.fasterxml.jackson.databind.*
import com.fasterxml.jackson.core.JsonParseException
import com.fasterxml.jackson.core.JsonParser
import com.fasterxml.jackson.core.JsonToken
import com.fasterxml.jackson.databind.DeserializationContext
import com.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.databind.JsonDeserializer
import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.databind.JsonSerializer
import com.fasterxml.jackson.databind.Module
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.SerializationFeature
import com.fasterxml.jackson.databind.SerializerProvider
import com.fasterxml.jackson.databind.deser.std.NumberDeserializers import com.fasterxml.jackson.databind.deser.std.NumberDeserializers
import com.fasterxml.jackson.databind.deser.std.StdDeserializer import com.fasterxml.jackson.databind.deser.std.StdDeserializer
import com.fasterxml.jackson.databind.module.SimpleModule import com.fasterxml.jackson.databind.module.SimpleModule
import com.fasterxml.jackson.databind.node.ObjectNode
import com.fasterxml.jackson.databind.ser.std.StdSerializer import com.fasterxml.jackson.databind.ser.std.StdSerializer
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule
import com.fasterxml.jackson.module.kotlin.KotlinModule import com.fasterxml.jackson.module.kotlin.KotlinModule
import com.fasterxml.jackson.module.kotlin.convertValue
import net.corda.client.jackson.internal.addSerAndDeser
import net.corda.client.jackson.internal.jsonObject
import net.corda.client.jackson.internal.readValueAs
import net.corda.core.CordaInternal
import net.corda.core.DoNotImplement
import net.corda.core.contracts.Amount import net.corda.core.contracts.Amount
import net.corda.core.contracts.ContractState import net.corda.core.contracts.ContractState
import net.corda.core.contracts.StateRef import net.corda.core.contracts.StateRef
import net.corda.core.crypto.AddressFormatException import net.corda.core.crypto.*
import net.corda.core.crypto.Base58
import net.corda.core.crypto.Crypto
import net.corda.core.crypto.MerkleTree
import net.corda.core.crypto.PartialMerkleTree
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.SignatureMetadata
import net.corda.core.crypto.TransactionSignature import net.corda.core.crypto.TransactionSignature
import net.corda.core.crypto.toStringShort import net.corda.core.identity.*
import net.corda.core.identity.AbstractParty import net.corda.core.internal.VisibleForTesting
import net.corda.core.identity.AnonymousParty
import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party
import net.corda.core.internal.uncheckedCast import net.corda.core.internal.uncheckedCast
import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.CordaRPCOps
import net.corda.core.node.NodeInfo import net.corda.core.node.NodeInfo
import net.corda.core.node.services.IdentityService import net.corda.core.node.services.IdentityService
import net.corda.core.serialization.SerializedBytes import net.corda.core.serialization.SerializedBytes
import net.corda.core.serialization.deserialize
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
import net.corda.core.transactions.CoreTransaction import net.corda.core.transactions.CoreTransaction
import net.corda.core.transactions.NotaryChangeWireTransaction import net.corda.core.transactions.NotaryChangeWireTransaction
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.WireTransaction import net.corda.core.transactions.WireTransaction
import net.corda.core.utilities.NetworkHostAndPort
import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.OpaqueBytes
import net.corda.core.utilities.base64ToByteArray import net.corda.core.utilities.parsePublicKeyBase58
import net.corda.core.utilities.toBase64 import net.corda.core.utilities.toBase58String
import java.math.BigDecimal import java.math.BigDecimal
import java.security.PublicKey import java.security.PublicKey
import java.util.* import java.util.*
@ -62,73 +48,68 @@ import java.util.*
* *
* Note that Jackson can also be used to serialise/deserialise other formats such as Yaml and XML. * Note that Jackson can also be used to serialise/deserialise other formats such as Yaml and XML.
*/ */
@Suppress("DEPRECATION")
object JacksonSupport { object JacksonSupport {
// TODO: This API could use some tidying up - there should really only need to be one kind of mapper.
// If you change this API please update the docs in the docsite (json.rst) // If you change this API please update the docs in the docsite (json.rst)
@DoNotImplement
interface PartyObjectMapper { interface PartyObjectMapper {
fun wellKnownPartyFromX500Name(name: CordaX500Name): Party? fun wellKnownPartyFromX500Name(name: CordaX500Name): Party?
fun partyFromKey(owningKey: PublicKey): Party? fun partyFromKey(owningKey: PublicKey): Party?
fun partiesFromName(query: String): Set<Party> fun partiesFromName(query: String): Set<Party>
fun nodeInfoFromParty(party: AbstractParty): NodeInfo?
} }
class RpcObjectMapper(val rpc: CordaRPCOps, factory: JsonFactory, val fuzzyIdentityMatch: Boolean) : PartyObjectMapper, ObjectMapper(factory) { @Deprecated("This is an internal class, do not use", replaceWith = ReplaceWith("JacksonSupport.createDefaultMapper"))
class RpcObjectMapper(val rpc: CordaRPCOps,
factory: JsonFactory,
val fuzzyIdentityMatch: Boolean) : PartyObjectMapper, ObjectMapper(factory) {
override fun wellKnownPartyFromX500Name(name: CordaX500Name): Party? = rpc.wellKnownPartyFromX500Name(name) override fun wellKnownPartyFromX500Name(name: CordaX500Name): Party? = rpc.wellKnownPartyFromX500Name(name)
override fun partyFromKey(owningKey: PublicKey): Party? = rpc.partyFromKey(owningKey) override fun partyFromKey(owningKey: PublicKey): Party? = rpc.partyFromKey(owningKey)
override fun partiesFromName(query: String) = rpc.partiesFromName(query, fuzzyIdentityMatch) override fun partiesFromName(query: String) = rpc.partiesFromName(query, fuzzyIdentityMatch)
override fun nodeInfoFromParty(party: AbstractParty): NodeInfo? = rpc.nodeInfoFromParty(party)
} }
class IdentityObjectMapper(val identityService: IdentityService, factory: JsonFactory, val fuzzyIdentityMatch: Boolean) : PartyObjectMapper, ObjectMapper(factory) { @Deprecated("This is an internal class, do not use")
class IdentityObjectMapper(val identityService: IdentityService,
factory: JsonFactory,
val fuzzyIdentityMatch: Boolean) : PartyObjectMapper, ObjectMapper(factory) {
override fun wellKnownPartyFromX500Name(name: CordaX500Name): Party? = identityService.wellKnownPartyFromX500Name(name) override fun wellKnownPartyFromX500Name(name: CordaX500Name): Party? = identityService.wellKnownPartyFromX500Name(name)
override fun partyFromKey(owningKey: PublicKey): Party? = identityService.partyFromKey(owningKey) override fun partyFromKey(owningKey: PublicKey): Party? = identityService.partyFromKey(owningKey)
override fun partiesFromName(query: String) = identityService.partiesFromName(query, fuzzyIdentityMatch) override fun partiesFromName(query: String) = identityService.partiesFromName(query, fuzzyIdentityMatch)
override fun nodeInfoFromParty(party: AbstractParty): NodeInfo? = null
} }
@Deprecated("This is an internal class, do not use", replaceWith = ReplaceWith("JacksonSupport.createNonRpcMapper"))
class NoPartyObjectMapper(factory: JsonFactory) : PartyObjectMapper, ObjectMapper(factory) { class NoPartyObjectMapper(factory: JsonFactory) : PartyObjectMapper, ObjectMapper(factory) {
override fun wellKnownPartyFromX500Name(name: CordaX500Name): Party? = throw UnsupportedOperationException() override fun wellKnownPartyFromX500Name(name: CordaX500Name): Party? = null
override fun partyFromKey(owningKey: PublicKey): Party? = throw UnsupportedOperationException() override fun partyFromKey(owningKey: PublicKey): Party? = null
override fun partiesFromName(query: String) = throw UnsupportedOperationException() override fun partiesFromName(query: String): Set<Party> = emptySet()
override fun nodeInfoFromParty(party: AbstractParty): NodeInfo? = null
} }
val cordaModule: Module by lazy { val cordaModule: Module by lazy {
SimpleModule("core").apply { SimpleModule("core").apply {
addSerializer(AnonymousParty::class.java, AnonymousPartySerializer) addSerAndDeser(AnonymousPartySerializer, AnonymousPartyDeserializer)
addDeserializer(AnonymousParty::class.java, AnonymousPartyDeserializer) addSerAndDeser(PartySerializer, PartyDeserializer)
addSerializer(Party::class.java, PartySerializer)
addDeserializer(Party::class.java, PartyDeserializer)
addDeserializer(AbstractParty::class.java, PartyDeserializer) addDeserializer(AbstractParty::class.java, PartyDeserializer)
addSerializer(BigDecimal::class.java, ToStringSerializer) addSerAndDeser<BigDecimal>(toStringSerializer, NumberDeserializers.BigDecimalDeserializer())
addDeserializer(BigDecimal::class.java, NumberDeserializers.BigDecimalDeserializer()) addSerAndDeser<SecureHash.SHA256>(toStringSerializer, SecureHashDeserializer())
addSerializer(SecureHash::class.java, SecureHashSerializer) addSerAndDeser(toStringSerializer, AmountDeserializer)
addSerializer(SecureHash.SHA256::class.java, SecureHashSerializer) addSerAndDeser(OpaqueBytesSerializer, OpaqueBytesDeserializer)
addDeserializer(SecureHash::class.java, SecureHashDeserializer()) addSerAndDeser(toStringSerializer, CordaX500NameDeserializer)
addDeserializer(SecureHash.SHA256::class.java, SecureHashDeserializer()) addSerAndDeser(PublicKeySerializer, PublicKeyDeserializer)
addDeserializer(CompositeKey::class.java, CompositeKeyDeseriaizer)
// Public key types addSerAndDeser(toStringSerializer, NetworkHostAndPortDeserializer)
addSerializer(PublicKey::class.java, PublicKeySerializer) // TODO Add deserialization which follows the same lookup logic as Party
addDeserializer(PublicKey::class.java, PublicKeyDeserializer) addSerializer(PartyAndCertificate::class.java, PartyAndCertificateSerializer)
// For NodeInfo
// TODO this tunnels the Kryo representation as a Base58 encoded string. Replace when RPC supports this.
addSerializer(NodeInfo::class.java, NodeInfoSerializer)
addDeserializer(NodeInfo::class.java, NodeInfoDeserializer) addDeserializer(NodeInfo::class.java, NodeInfoDeserializer)
// For Amount
addSerializer(Amount::class.java, AmountSerializer)
addDeserializer(Amount::class.java, AmountDeserializer)
// For OpaqueBytes
addDeserializer(OpaqueBytes::class.java, OpaqueBytesDeserializer)
addSerializer(OpaqueBytes::class.java, OpaqueBytesSerializer)
listOf(TransactionSignatureSerde, SignedTransactionSerde).forEach { serde -> serde.applyTo(this) } listOf(TransactionSignatureSerde, SignedTransactionSerde).forEach { serde -> serde.applyTo(this) }
// For X.500 distinguished names // Using mixins to fine-tune the default serialised output
addDeserializer(CordaX500Name::class.java, CordaX500NameDeserializer)
addSerializer(CordaX500Name::class.java, CordaX500NameSerializer)
// Mixins for transaction types to prevent some properties from being serialized
setMixInAnnotation(WireTransaction::class.java, WireTransactionMixin::class.java) setMixInAnnotation(WireTransaction::class.java, WireTransactionMixin::class.java)
setMixInAnnotation(NodeInfo::class.java, NodeInfoMixin::class.java)
} }
} }
@ -141,8 +122,11 @@ object JacksonSupport {
*/ */
@JvmStatic @JvmStatic
@JvmOverloads @JvmOverloads
fun createDefaultMapper(rpc: CordaRPCOps, factory: JsonFactory = JsonFactory(), fun createDefaultMapper(rpc: CordaRPCOps,
fuzzyIdentityMatch: Boolean = false): ObjectMapper = configureMapper(RpcObjectMapper(rpc, factory, fuzzyIdentityMatch)) factory: JsonFactory = JsonFactory(),
fuzzyIdentityMatch: Boolean = false): ObjectMapper {
return configureMapper(RpcObjectMapper(rpc, factory, fuzzyIdentityMatch))
}
/** For testing or situations where deserialising parties is not required */ /** For testing or situations where deserialising parties is not required */
@JvmStatic @JvmStatic
@ -156,20 +140,72 @@ object JacksonSupport {
* match an identity known from the network map. If true, the name is matched more leniently but if the match * match an identity known from the network map. If true, the name is matched more leniently but if the match
* is ambiguous a [JsonParseException] is thrown. * is ambiguous a [JsonParseException] is thrown.
*/ */
@Deprecated("This is an internal method, do not use")
@JvmStatic @JvmStatic
@JvmOverloads @JvmOverloads
fun createInMemoryMapper(identityService: IdentityService, factory: JsonFactory = JsonFactory(), fun createInMemoryMapper(identityService: IdentityService,
fuzzyIdentityMatch: Boolean = false) = configureMapper(IdentityObjectMapper(identityService, factory, fuzzyIdentityMatch)) factory: JsonFactory = JsonFactory(),
fuzzyIdentityMatch: Boolean = false): ObjectMapper {
return configureMapper(IdentityObjectMapper(identityService, factory, fuzzyIdentityMatch))
}
private fun configureMapper(mapper: ObjectMapper): ObjectMapper = mapper.apply { @CordaInternal
@VisibleForTesting
internal fun createPartyObjectMapper(partyObjectMapper: PartyObjectMapper, factory: JsonFactory = JsonFactory()): ObjectMapper {
val mapper = object : ObjectMapper(factory), PartyObjectMapper by partyObjectMapper {}
return configureMapper(mapper)
}
private fun configureMapper(mapper: ObjectMapper): ObjectMapper {
return mapper.apply {
enable(SerializationFeature.INDENT_OUTPUT) enable(SerializationFeature.INDENT_OUTPUT)
enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY) enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS) enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)
disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
registerModule(JavaTimeModule()) registerModule(JavaTimeModule().apply {
addSerializer(Date::class.java, DateSerializer)
})
registerModule(cordaModule) registerModule(cordaModule)
registerModule(KotlinModule()) registerModule(KotlinModule())
} }
}
private val toStringSerializer = com.fasterxml.jackson.databind.ser.std.ToStringSerializer.instance
private object DateSerializer : JsonSerializer<Date>() {
override fun serialize(value: Date, gen: JsonGenerator, serializers: SerializerProvider) {
gen.writeObject(value.toInstant())
}
}
private object NetworkHostAndPortDeserializer : JsonDeserializer<NetworkHostAndPort>() {
override fun deserialize(parser: JsonParser, ctxt: DeserializationContext): NetworkHostAndPort {
return NetworkHostAndPort.parse(parser.text)
}
}
private object CompositeKeyDeseriaizer : JsonDeserializer<CompositeKey>() {
override fun deserialize(parser: JsonParser, ctxt: DeserializationContext): CompositeKey {
val publicKey = parser.readValueAs<PublicKey>()
return publicKey as? CompositeKey ?: throw JsonParseException(parser, "Not a CompositeKey: $publicKey")
}
}
private object PartyAndCertificateSerializer : JsonSerializer<PartyAndCertificate>() {
override fun serialize(value: PartyAndCertificate, gen: JsonGenerator, serializers: SerializerProvider) {
gen.jsonObject {
writeObjectField("name", value.name)
writeObjectField("owningKey", value.owningKey)
// TODO Add configurable option to output the certPath
}
}
}
@Suppress("unused")
private interface NodeInfoMixin {
@get:JsonIgnore val legalIdentities: Any // This is already covered by legalIdentitiesAndCerts
}
private interface JsonSerde<TYPE> { private interface JsonSerde<TYPE> {
val type: Class<TYPE> val type: Class<TYPE>
@ -185,7 +221,6 @@ object JacksonSupport {
} }
private inline fun <reified RESULT> JsonNode.get(fieldName: String, condition: (JsonNode) -> Boolean, mapper: ObjectMapper, parser: JsonParser): RESULT { private inline fun <reified RESULT> JsonNode.get(fieldName: String, condition: (JsonNode) -> Boolean, mapper: ObjectMapper, parser: JsonParser): RESULT {
if (get(fieldName)?.let(condition) != true) { if (get(fieldName)?.let(condition) != true) {
JsonParseException(parser, "Missing required object field \"$fieldName\".") JsonParseException(parser, "Missing required object field \"$fieldName\".")
} }
@ -196,14 +231,12 @@ object JacksonSupport {
override val type: Class<TransactionSignature> = TransactionSignature::class.java override val type: Class<TransactionSignature> = TransactionSignature::class.java
override val serializer = object : StdSerializer<TransactionSignature>(type) { override val serializer = object : StdSerializer<TransactionSignature>(type) {
override fun serialize(value: TransactionSignature, json: JsonGenerator, serializers: SerializerProvider) { override fun serialize(value: TransactionSignature, gen: JsonGenerator, serializers: SerializerProvider) {
with(json) { gen.jsonObject {
writeStartObject()
writeObjectField("by", value.by) writeObjectField("by", value.by)
writeObjectField("signatureMetadata", value.signatureMetadata) writeObjectField("signatureMetadata", value.signatureMetadata)
writeObjectField("bytes", value.bytes) writeObjectField("bytes", value.bytes)
writeObjectField("partialMerkleTree", value.partialMerkleTree) writeObjectField("partialMerkleTree", value.partialMerkleTree)
writeEndObject()
} }
} }
} }
@ -212,11 +245,7 @@ object JacksonSupport {
override fun deserialize(parser: JsonParser, context: DeserializationContext): TransactionSignature { override fun deserialize(parser: JsonParser, context: DeserializationContext): TransactionSignature {
val mapper = parser.codec as ObjectMapper val mapper = parser.codec as ObjectMapper
val json = mapper.readTree<JsonNode>(parser) val json = mapper.readTree<JsonNode>(parser)
val by = mapper.convertValue<PublicKey>(json["by"])
if (json.get("by")?.isTextual != true) {
JsonParseException(parser, "Missing required text field \"by\".")
}
val by = PublicKeyDeserializer.deserializeValue(json.get("by").textValue())
val signatureMetadata = json.get<SignatureMetadata>("signatureMetadata", JsonNode::isObject, mapper, parser) val signatureMetadata = json.get<SignatureMetadata>("signatureMetadata", JsonNode::isObject, mapper, parser)
val bytes = json.get<ByteArray>("bytes", JsonNode::isObject, mapper, parser) val bytes = json.get<ByteArray>("bytes", JsonNode::isObject, mapper, parser)
val partialMerkleTree = json.get<PartialMerkleTree>("partialMerkleTree", JsonNode::isObject, mapper, parser) val partialMerkleTree = json.get<PartialMerkleTree>("partialMerkleTree", JsonNode::isObject, mapper, parser)
@ -230,12 +259,10 @@ object JacksonSupport {
override val type: Class<SignedTransaction> = SignedTransaction::class.java override val type: Class<SignedTransaction> = SignedTransaction::class.java
override val serializer = object : StdSerializer<SignedTransaction>(type) { override val serializer = object : StdSerializer<SignedTransaction>(type) {
override fun serialize(value: SignedTransaction, json: JsonGenerator, serializers: SerializerProvider) { override fun serialize(value: SignedTransaction, gen: JsonGenerator, serializers: SerializerProvider) {
with(json) { gen.jsonObject {
writeStartObject()
writeObjectField("txBits", value.txBits.bytes) writeObjectField("txBits", value.txBits.bytes)
writeObjectField("signatures", value.sigs) writeObjectField("signatures", value.sigs)
writeEndObject()
} }
} }
} }
@ -255,110 +282,106 @@ object JacksonSupport {
private class TransactionSignatures : ArrayList<TransactionSignature>() private class TransactionSignatures : ArrayList<TransactionSignature>()
} }
//
// The following should not have been made public and are thus deprecated with warnings.
//
@Deprecated("No longer used as jackson already has a toString serializer",
replaceWith = ReplaceWith("com.fasterxml.jackson.databind.ser.std.ToStringSerializer.instance"))
object ToStringSerializer : JsonSerializer<Any>() { object ToStringSerializer : JsonSerializer<Any>() {
override fun serialize(obj: Any, generator: JsonGenerator, provider: SerializerProvider) { override fun serialize(obj: Any, generator: JsonGenerator, provider: SerializerProvider) {
generator.writeString(obj.toString()) generator.writeString(obj.toString())
} }
} }
@Deprecated("This is an internal class, do not use")
object AnonymousPartySerializer : JsonSerializer<AnonymousParty>() { object AnonymousPartySerializer : JsonSerializer<AnonymousParty>() {
override fun serialize(obj: AnonymousParty, generator: JsonGenerator, provider: SerializerProvider) { override fun serialize(value: AnonymousParty, generator: JsonGenerator, provider: SerializerProvider) {
PublicKeySerializer.serialize(obj.owningKey, generator, provider) generator.writeObject(value.owningKey)
} }
} }
@Deprecated("This is an internal class, do not use")
object AnonymousPartyDeserializer : JsonDeserializer<AnonymousParty>() { object AnonymousPartyDeserializer : JsonDeserializer<AnonymousParty>() {
override fun deserialize(parser: JsonParser, context: DeserializationContext): AnonymousParty { override fun deserialize(parser: JsonParser, context: DeserializationContext): AnonymousParty {
if (parser.currentToken == JsonToken.FIELD_NAME) { return AnonymousParty(parser.readValueAs(PublicKey::class.java))
parser.nextToken()
}
val key = PublicKeyDeserializer.deserialize(parser, context)
return AnonymousParty(key)
} }
} }
@Deprecated("This is an internal class, do not use")
object PartySerializer : JsonSerializer<Party>() { object PartySerializer : JsonSerializer<Party>() {
override fun serialize(obj: Party, generator: JsonGenerator, provider: SerializerProvider) { override fun serialize(value: Party, generator: JsonGenerator, provider: SerializerProvider) {
generator.writeString(obj.name.toString()) // TODO Add configurable option to output this as an object which includes the owningKey
generator.writeObject(value.name)
} }
} }
@Deprecated("This is an internal class, do not use")
object PartyDeserializer : JsonDeserializer<Party>() { object PartyDeserializer : JsonDeserializer<Party>() {
override fun deserialize(parser: JsonParser, context: DeserializationContext): Party { override fun deserialize(parser: JsonParser, context: DeserializationContext): Party {
if (parser.currentToken == JsonToken.FIELD_NAME) {
parser.nextToken()
}
val mapper = parser.codec as PartyObjectMapper val mapper = parser.codec as PartyObjectMapper
// The comma character is invalid in base64, and required as a separator for X.500 names. As Corda // The comma character is invalid in Base58, and required as a separator for X.500 names. As Corda
// X.500 names all involve at least three attributes (organisation, locality, country), they must // X.500 names all involve at least three attributes (organisation, locality, country), they must
// include a comma. As such we can use it as a distinguisher between the two types. // include a comma. As such we can use it as a distinguisher between the two types.
return if (parser.text.contains(",")) { return if ("," in parser.text) {
val principal = CordaX500Name.parse(parser.text) val principal = CordaX500Name.parse(parser.text)
mapper.wellKnownPartyFromX500Name(principal) ?: throw JsonParseException(parser, "Could not find a Party with name $principal") mapper.wellKnownPartyFromX500Name(principal) ?: throw JsonParseException(parser, "Could not find a Party with name $principal")
} else { } else {
val nameMatches = mapper.partiesFromName(parser.text) val nameMatches = mapper.partiesFromName(parser.text)
if (nameMatches.isEmpty()) { if (nameMatches.isEmpty()) {
val derBytes = try { val publicKey = parser.readValueAs<PublicKey>()
parser.text.base64ToByteArray() mapper.partyFromKey(publicKey)
} catch (e: AddressFormatException) { ?: throw JsonParseException(parser, "Could not find a Party with key ${publicKey.toStringShort()}")
throw JsonParseException(parser, "Could not find a matching party for '${parser.text}' and is not a base64 encoded public key: " + e.message)
}
val key = try {
Crypto.decodePublicKey(derBytes)
} catch (e: Exception) {
throw JsonParseException(parser, "Could not find a matching party for '${parser.text}' and is not a valid public key: " + e.message)
}
mapper.partyFromKey(key) ?: throw JsonParseException(parser, "Could not find a Party with key ${key.toStringShort()}")
} else if (nameMatches.size == 1) { } else if (nameMatches.size == 1) {
nameMatches.first() nameMatches.first()
} else { } else {
throw JsonParseException(parser, "Ambiguous name match '${parser.text}': could be any of " + nameMatches.map { it.name }.joinToString(" ... or ...")) throw JsonParseException(parser, "Ambiguous name match '${parser.text}': could be any of " +
nameMatches.map { it.name }.joinToString(" ... or ... "))
} }
} }
} }
} }
@Deprecated("This is an internal class, do not use")
// This is no longer used
object CordaX500NameSerializer : JsonSerializer<CordaX500Name>() { object CordaX500NameSerializer : JsonSerializer<CordaX500Name>() {
override fun serialize(obj: CordaX500Name, generator: JsonGenerator, provider: SerializerProvider) { override fun serialize(obj: CordaX500Name, generator: JsonGenerator, provider: SerializerProvider) {
generator.writeString(obj.toString()) generator.writeString(obj.toString())
} }
} }
@Deprecated("This is an internal class, do not use")
object CordaX500NameDeserializer : JsonDeserializer<CordaX500Name>() { object CordaX500NameDeserializer : JsonDeserializer<CordaX500Name>() {
override fun deserialize(parser: JsonParser, context: DeserializationContext): CordaX500Name { override fun deserialize(parser: JsonParser, context: DeserializationContext): CordaX500Name {
if (parser.currentToken == JsonToken.FIELD_NAME) {
parser.nextToken()
}
return try { return try {
CordaX500Name.parse(parser.text) CordaX500Name.parse(parser.text)
} catch (ex: IllegalArgumentException) { } catch (e: IllegalArgumentException) {
throw JsonParseException(parser, "Invalid Corda X.500 name ${parser.text}: ${ex.message}", ex) throw JsonParseException(parser, "Invalid Corda X.500 name ${parser.text}: ${e.message}", e)
} }
} }
} }
@Deprecated("This is an internal class, do not use")
// This is no longer used
object NodeInfoSerializer : JsonSerializer<NodeInfo>() { object NodeInfoSerializer : JsonSerializer<NodeInfo>() {
override fun serialize(value: NodeInfo, gen: JsonGenerator, serializers: SerializerProvider) { override fun serialize(value: NodeInfo, gen: JsonGenerator, serializers: SerializerProvider) {
gen.writeString(Base58.encode(value.serialize().bytes)) gen.writeString(Base58.encode(value.serialize().bytes))
} }
} }
@Deprecated("This is an internal class, do not use")
object NodeInfoDeserializer : JsonDeserializer<NodeInfo>() { object NodeInfoDeserializer : JsonDeserializer<NodeInfo>() {
override fun deserialize(parser: JsonParser, context: DeserializationContext): NodeInfo { override fun deserialize(parser: JsonParser, context: DeserializationContext): NodeInfo {
if (parser.currentToken == JsonToken.FIELD_NAME) { val mapper = parser.codec as PartyObjectMapper
parser.nextToken() val party = parser.readValueAs<AbstractParty>()
} return mapper.nodeInfoFromParty(party) ?: throw JsonParseException(parser, "Cannot find node with $party")
try {
return Base58.decode(parser.text).deserialize<NodeInfo>()
} catch (e: Exception) {
throw JsonParseException(parser, "Invalid NodeInfo ${parser.text}: ${e.message}")
}
} }
} }
@Deprecated("This is an internal class, do not use")
// This is no longer used
object SecureHashSerializer : JsonSerializer<SecureHash>() { object SecureHashSerializer : JsonSerializer<SecureHash>() {
override fun serialize(obj: SecureHash, generator: JsonGenerator, provider: SerializerProvider) { override fun serialize(obj: SecureHash, generator: JsonGenerator, provider: SerializerProvider) {
generator.writeString(obj.toString()) generator.writeString(obj.toString())
@ -368,11 +391,9 @@ object JacksonSupport {
/** /**
* Implemented as a class so that we can instantiate for T. * Implemented as a class so that we can instantiate for T.
*/ */
@Deprecated("This is an internal class, do not use")
class SecureHashDeserializer<T : SecureHash> : JsonDeserializer<T>() { class SecureHashDeserializer<T : SecureHash> : JsonDeserializer<T>() {
override fun deserialize(parser: JsonParser, context: DeserializationContext): T { override fun deserialize(parser: JsonParser, context: DeserializationContext): T {
if (parser.currentToken == JsonToken.FIELD_NAME) {
parser.nextToken()
}
try { try {
return uncheckedCast(SecureHash.parse(parser.text)) return uncheckedCast(SecureHash.parse(parser.text))
} catch (e: Exception) { } catch (e: Exception) {
@ -381,69 +402,72 @@ object JacksonSupport {
} }
} }
@Deprecated("This is an internal class, do not use")
object PublicKeySerializer : JsonSerializer<PublicKey>() { object PublicKeySerializer : JsonSerializer<PublicKey>() {
override fun serialize(obj: PublicKey, generator: JsonGenerator, provider: SerializerProvider) { override fun serialize(value: PublicKey, generator: JsonGenerator, provider: SerializerProvider) {
generator.writeString(obj.encoded.toBase64()) generator.writeString(value.toBase58String())
} }
} }
@Deprecated("This is an internal class, do not use")
object PublicKeyDeserializer : JsonDeserializer<PublicKey>() { object PublicKeyDeserializer : JsonDeserializer<PublicKey>() {
override fun deserialize(parser: JsonParser, context: DeserializationContext): PublicKey { override fun deserialize(parser: JsonParser, context: DeserializationContext): PublicKey {
return deserializeValue(parser.text, parser)
}
internal fun deserializeValue(value: String, parser: JsonParser? = null): PublicKey {
return try { return try {
val derBytes = value.base64ToByteArray() parsePublicKeyBase58(parser.text)
Crypto.decodePublicKey(derBytes)
} catch (e: Exception) { } catch (e: Exception) {
throw JsonParseException(parser, "Invalid public key $value: ${e.message}") throw JsonParseException(parser, "Invalid public key ${parser.text}: ${e.message}")
} }
} }
} }
@Deprecated("This is an internal class, do not use")
// This is no longer used
object AmountSerializer : JsonSerializer<Amount<*>>() { object AmountSerializer : JsonSerializer<Amount<*>>() {
override fun serialize(value: Amount<*>, gen: JsonGenerator, serializers: SerializerProvider) { override fun serialize(value: Amount<*>, gen: JsonGenerator, serializers: SerializerProvider) {
gen.writeString(value.toString()) gen.writeString(value.toString())
} }
} }
@Deprecated("This is an internal class, do not use")
object AmountDeserializer : JsonDeserializer<Amount<*>>() { object AmountDeserializer : JsonDeserializer<Amount<*>>() {
override fun deserialize(parser: JsonParser, context: DeserializationContext): Amount<*> { override fun deserialize(parser: JsonParser, context: DeserializationContext): Amount<*> {
return if (parser.currentToken == JsonToken.VALUE_STRING) {
Amount.parseCurrency(parser.text)
} else {
try { try {
return Amount.parseCurrency(parser.text) val tree = parser.readValueAsTree<ObjectNode>()
} catch (e: Exception) { val quantity = tree["quantity"].apply { require(canConvertToLong()) }
try { val token = tree["token"]
val tree = parser.readValueAsTree<JsonNode>()
require(tree["quantity"].canConvertToLong() && tree["token"].asText().isNotBlank())
val quantity = tree["quantity"].asLong()
val token = tree["token"].asText()
// Attempt parsing as a currency token. TODO: This needs thought about how to extend to other token types. // Attempt parsing as a currency token. TODO: This needs thought about how to extend to other token types.
val currency = Currency.getInstance(token) val currency = (parser.codec as ObjectMapper).convertValue<Currency>(token)
return Amount(quantity, currency) Amount(quantity.longValue(), currency)
} catch (e2: Exception) { } catch (e: Exception) {
throw JsonParseException(parser, "Invalid amount ${parser.text}", e2) throw JsonParseException(parser, "Invalid amount", e)
} }
} }
} }
} }
@Deprecated("This is an internal class, do not use")
object OpaqueBytesDeserializer : JsonDeserializer<OpaqueBytes>() { object OpaqueBytesDeserializer : JsonDeserializer<OpaqueBytes>() {
override fun deserialize(parser: JsonParser, ctxt: DeserializationContext): OpaqueBytes { override fun deserialize(parser: JsonParser, ctxt: DeserializationContext): OpaqueBytes {
return OpaqueBytes(parser.text.toByteArray()) return OpaqueBytes(parser.binaryValue)
} }
} }
@Deprecated("This is an internal class, do not use")
object OpaqueBytesSerializer : JsonSerializer<OpaqueBytes>() { object OpaqueBytesSerializer : JsonSerializer<OpaqueBytes>() {
override fun serialize(value: OpaqueBytes, gen: JsonGenerator, serializers: SerializerProvider) { override fun serialize(value: OpaqueBytes, gen: JsonGenerator, serializers: SerializerProvider) {
gen.writeBinary(value.bytes) gen.writeBinary(value.bytes)
} }
} }
@Deprecated("This is an internal class, do not use")
@Suppress("unused")
abstract class SignedTransactionMixin { abstract class SignedTransactionMixin {
@JsonIgnore abstract fun getTxBits(): SerializedBytes<CoreTransaction> @JsonIgnore abstract fun getTxBits(): SerializedBytes<CoreTransaction>
@JsonProperty("signatures") protected abstract fun getSigs(): List<TransactionSignature> @JsonProperty("signatures") protected abstract fun getSigs(): List<TransactionSignature>
@JsonProperty protected abstract fun getTransaction(): CoreTransaction @JsonProperty protected abstract fun getTransaction(): CoreTransaction // TODO It seems this should be coreTransaction
@JsonIgnore abstract fun getTx(): WireTransaction @JsonIgnore abstract fun getTx(): WireTransaction
@JsonIgnore abstract fun getNotaryChangeTx(): NotaryChangeWireTransaction @JsonIgnore abstract fun getNotaryChangeTx(): NotaryChangeWireTransaction
@JsonIgnore abstract fun getInputs(): List<StateRef> @JsonIgnore abstract fun getInputs(): List<StateRef>
@ -452,6 +476,8 @@ object JacksonSupport {
@JsonIgnore abstract fun getRequiredSigningKeys(): Set<PublicKey> @JsonIgnore abstract fun getRequiredSigningKeys(): Set<PublicKey>
} }
@Deprecated("This is an internal class, do not use")
@Suppress("unused")
abstract class WireTransactionMixin { abstract class WireTransactionMixin {
@JsonIgnore abstract fun getMerkleTree(): MerkleTree @JsonIgnore abstract fun getMerkleTree(): MerkleTree
@JsonIgnore abstract fun getAvailableComponents(): List<Any> @JsonIgnore abstract fun getAvailableComponents(): List<Any>
@ -459,4 +485,3 @@ object JacksonSupport {
@JsonIgnore abstract fun getOutputStates(): List<ContractState> @JsonIgnore abstract fun getOutputStates(): List<ContractState>
} }
} }

View File

@ -0,0 +1,21 @@
package net.corda.client.jackson.internal
import com.fasterxml.jackson.core.JsonGenerator
import com.fasterxml.jackson.core.JsonParser
import com.fasterxml.jackson.databind.JsonDeserializer
import com.fasterxml.jackson.databind.JsonSerializer
import com.fasterxml.jackson.databind.module.SimpleModule
inline fun <reified T : Any> SimpleModule.addSerAndDeser(serializer: JsonSerializer<in T>, deserializer: JsonDeserializer<T>) {
addSerializer(T::class.java, serializer)
addDeserializer(T::class.java, deserializer)
}
inline fun JsonGenerator.jsonObject(fieldName: String? = null, gen: JsonGenerator.() -> Unit) {
fieldName?.let { writeFieldName(it) }
writeStartObject()
gen()
writeEndObject()
}
inline fun <reified T> JsonParser.readValueAs(): T = readValueAs(T::class.java)

View File

@ -1,49 +1,61 @@
package net.corda.client.jackson package net.corda.client.jackson
import com.fasterxml.jackson.databind.SerializationFeature import com.fasterxml.jackson.databind.SerializationFeature
import com.fasterxml.jackson.databind.node.ArrayNode
import com.fasterxml.jackson.databind.node.BinaryNode
import com.fasterxml.jackson.databind.node.ObjectNode
import com.fasterxml.jackson.databind.node.TextNode
import com.fasterxml.jackson.module.kotlin.convertValue
import com.nhaarman.mockito_kotlin.doReturn import com.nhaarman.mockito_kotlin.doReturn
import com.nhaarman.mockito_kotlin.whenever import com.nhaarman.mockito_kotlin.whenever
import net.corda.core.contracts.Amount import net.corda.core.contracts.Amount
import net.corda.core.cordapp.CordappProvider import net.corda.core.cordapp.CordappProvider
import net.corda.core.crypto.*
import net.corda.core.crypto.CompositeKey import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.Crypto import net.corda.core.identity.AbstractParty
import net.corda.core.crypto.SecureHash import net.corda.core.identity.AnonymousParty
import net.corda.core.crypto.SignatureMetadata
import net.corda.core.crypto.TransactionSignature
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party
import net.corda.core.node.NodeInfo
import net.corda.core.node.ServiceHub import net.corda.core.node.ServiceHub
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.NetworkHostAndPort
import net.corda.core.utilities.OpaqueBytes
import net.corda.core.utilities.toBase58String
import net.corda.core.utilities.toBase64
import net.corda.finance.USD import net.corda.finance.USD
import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.common.internal.testNetworkParameters
import net.corda.testing.contracts.DummyContract import net.corda.testing.contracts.DummyContract
import net.corda.testing.core.ALICE_NAME import net.corda.testing.core.*
import net.corda.testing.core.BOB_NAME import net.corda.testing.internal.createNodeInfoAndSigned
import net.corda.testing.core.DUMMY_NOTARY_NAME
import net.corda.testing.core.SerializationEnvironmentRule
import net.corda.testing.core.TestIdentity
import net.corda.testing.internal.rigorousMock import net.corda.testing.internal.rigorousMock
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.Before import org.junit.Before
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
import java.math.BigInteger import java.math.BigInteger
import java.security.PublicKey import java.security.PublicKey
import java.util.* import java.util.*
import kotlin.collections.ArrayList
import kotlin.test.assertEquals import kotlin.test.assertEquals
class JacksonSupportTest { class JacksonSupportTest {
private companion object { private companion object {
val SEED = BigInteger.valueOf(20170922L)!! val SEED: BigInteger = BigInteger.valueOf(20170922L)
val mapper = JacksonSupport.createNonRpcMapper()
val ALICE_PUBKEY = TestIdentity(ALICE_NAME, 70).publicKey val ALICE_PUBKEY = TestIdentity(ALICE_NAME, 70).publicKey
val BOB_PUBKEY = TestIdentity(BOB_NAME, 70).publicKey val BOB_PUBKEY = TestIdentity(BOB_NAME, 70).publicKey
val DUMMY_NOTARY = TestIdentity(DUMMY_NOTARY_NAME, 20).party val DUMMY_NOTARY = TestIdentity(DUMMY_NOTARY_NAME, 20).party
val MINI_CORP = TestIdentity(CordaX500Name("MiniCorp", "London", "GB")).party val MINI_CORP = TestIdentity(CordaX500Name("MiniCorp", "London", "GB"))
} }
@Rule @Rule
@JvmField @JvmField
val testSerialization = SerializationEnvironmentRule() val testSerialization = SerializationEnvironmentRule()
private val partyObjectMapper = TestPartyObjectMapper()
private val mapper = JacksonSupport.createPartyObjectMapper(partyObjectMapper)
private lateinit var services: ServiceHub private lateinit var services: ServiceHub
private lateinit var cordappProvider: CordappProvider private lateinit var cordappProvider: CordappProvider
@ -54,37 +66,10 @@ class JacksonSupportTest {
doReturn(cordappProvider).whenever(services).cordappProvider doReturn(cordappProvider).whenever(services).cordappProvider
} }
@Test
fun `should serialize Composite keys`() {
val expected = "\"MIHAMBUGE2mtoq+J1bjir/ONk6yd5pab0FoDgaYAMIGiAgECMIGcMDIDLQAwKjAFBgMrZXADIQAgIX1QlJRgaLlD0ttLlJF5kNqT/7P7QwCvrWc9+/248gIBATAyAy0AMCowBQYDK2VwAyEAqS0JPGlzdviBZjB9FaNY+w6cVs3/CQ2A5EimE9Lyng4CAQEwMgMtADAqMAUGAytlcAMhALq4GG0gBQZIlaKE6ucooZsuoKUbH4MtGSmA6cwj136+AgEB\""
val innerKeys = (1..3).map { i ->
Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, SEED.plus(BigInteger.valueOf(i.toLong()))).public
}
// Build a 2 of 3 composite key
val publicKey = CompositeKey.Builder().let {
innerKeys.forEach { key -> it.addKey(key, 1) }
it.build(2)
}
val serialized = mapper.writeValueAsString(publicKey)
assertEquals(expected, serialized)
val parsedKey = mapper.readValue(serialized, PublicKey::class.java)
assertEquals(publicKey, parsedKey)
}
private class Dummy(val notional: Amount<Currency>) private class Dummy(val notional: Amount<Currency>)
@Test @Test
fun `should serialize EdDSA keys`() { fun `read Amount`() {
val expected = "\"MCowBQYDK2VwAyEACFTgLk1NOqYXAfxLoR7ctSbZcl9KMXu58Mq31Kv1Dwk=\""
val publicKey = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, SEED).public
val serialized = mapper.writeValueAsString(publicKey)
assertEquals(expected, serialized)
val parsedKey = mapper.readValue(serialized, PublicKey::class.java)
assertEquals(publicKey, parsedKey)
}
@Test
fun readAmount() {
val oldJson = """ val oldJson = """
{ {
"notional": { "notional": {
@ -100,27 +85,186 @@ class JacksonSupportTest {
} }
@Test @Test
fun writeAmount() { fun `write Amount`() {
val writer = mapper.writer().without(SerializationFeature.INDENT_OUTPUT) val writer = mapper.writer().without(SerializationFeature.INDENT_OUTPUT)
assertEquals("""{"notional":"25000000.00 USD"}""", writer.writeValueAsString(Dummy(Amount.parseCurrency("$25000000")))) assertEquals("""{"notional":"25000000.00 GBP"}""", writer.writeValueAsString(Dummy(Amount.parseCurrency("£25000000"))))
assertEquals("""{"notional":"250000.00 USD"}""", writer.writeValueAsString(Dummy(Amount.parseCurrency("$250000"))))
} }
@Test @Test
fun `wire transaction can be serialized and de-serialized`() { fun SignedTransaction() {
val attachmentRef = SecureHash.randomSHA256() val attachmentRef = SecureHash.randomSHA256()
doReturn(attachmentRef).whenever(cordappProvider).getContractAttachmentID(DummyContract.PROGRAM_ID) doReturn(attachmentRef).whenever(cordappProvider).getContractAttachmentID(DummyContract.PROGRAM_ID)
doReturn(testNetworkParameters()).whenever(services).networkParameters doReturn(testNetworkParameters()).whenever(services).networkParameters
val writer = mapper.writer() val writer = mapper.writer()
val transaction = makeDummyTx() val stx = makeDummyStx()
val json = writer.writeValueAsString(transaction) val json = writer.writeValueAsString(stx)
val deserializedTransaction = mapper.readValue(json, SignedTransaction::class.java) val deserializedTransaction = mapper.readValue(json, SignedTransaction::class.java)
assertThat(deserializedTransaction).isEqualTo(transaction) assertThat(deserializedTransaction).isEqualTo(stx)
} }
private fun makeDummyTx(): SignedTransaction { @Test
fun OpaqueBytes() {
val opaqueBytes = OpaqueBytes(secureRandomBytes(128))
val json = mapper.valueToTree<BinaryNode>(opaqueBytes)
assertThat(json.binaryValue()).isEqualTo(opaqueBytes.bytes)
assertThat(json.asText()).isEqualTo(opaqueBytes.bytes.toBase64())
assertThat(mapper.convertValue<OpaqueBytes>(json)).isEqualTo(opaqueBytes)
}
@Test
fun CordaX500Name() {
testToStringSerialisation(CordaX500Name(commonName = "COMMON", organisationUnit = "ORG UNIT", organisation = "ORG", locality = "NYC", state = "NY", country = "US"))
}
@Test
fun `SecureHash SHA256`() {
testToStringSerialisation(SecureHash.randomSHA256())
}
@Test
fun NetworkHostAndPort() {
testToStringSerialisation(NetworkHostAndPort("localhost", 9090))
}
@Test
fun UUID() {
testToStringSerialisation(UUID.randomUUID())
}
@Test
fun `Date is treated as Instant`() {
val date = Date()
val json = mapper.valueToTree<TextNode>(date)
assertThat(json.textValue()).isEqualTo(date.toInstant().toString())
assertThat(mapper.convertValue<Date>(json)).isEqualTo(date)
}
@Test
fun `Party serialization`() {
val json = mapper.valueToTree<TextNode>(MINI_CORP.party)
assertThat(json.textValue()).isEqualTo(MINI_CORP.name.toString())
}
@Test
fun `Party deserialization on full name`() {
fun convertToParty() = mapper.convertValue<Party>(TextNode(MINI_CORP.name.toString()))
assertThatThrownBy { convertToParty() }
partyObjectMapper.identities += MINI_CORP.party
assertThat(convertToParty()).isEqualTo(MINI_CORP.party)
}
@Test
fun `Party deserialization on part of name`() {
fun convertToParty() = mapper.convertValue<Party>(TextNode(MINI_CORP.name.organisation))
assertThatThrownBy { convertToParty() }
partyObjectMapper.identities += MINI_CORP.party
assertThat(convertToParty()).isEqualTo(MINI_CORP.party)
}
@Test
fun `Party deserialization on public key`() {
fun convertToParty() = mapper.convertValue<Party>(TextNode(MINI_CORP.publicKey.toBase58String()))
assertThatThrownBy { convertToParty() }
partyObjectMapper.identities += MINI_CORP.party
assertThat(convertToParty()).isEqualTo(MINI_CORP.party)
}
@Test
fun PublicKey() {
val json = mapper.valueToTree<TextNode>(MINI_CORP.publicKey)
assertThat(json.textValue()).isEqualTo(MINI_CORP.publicKey.toBase58String())
assertThat(mapper.convertValue<PublicKey>(json)).isEqualTo(MINI_CORP.publicKey)
}
@Test
fun `EdDSA public key`() {
val publicKey = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, SEED).public
val json = mapper.valueToTree<TextNode>(publicKey)
assertThat(json.textValue()).isEqualTo(publicKey.toBase58String())
assertThat(mapper.convertValue<PublicKey>(json)).isEqualTo(publicKey)
}
@Test
fun CompositeKey() {
val innerKeys = (1..3).map { i ->
Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, SEED + i.toBigInteger()).public
}
// Build a 2 of 3 composite key
val publicKey = CompositeKey.Builder().let {
innerKeys.forEach { key -> it.addKey(key, 1) }
it.build(2)
}
val json = mapper.valueToTree<TextNode>(publicKey)
assertThat(json.textValue()).isEqualTo(publicKey.toBase58String())
assertThat(mapper.convertValue<CompositeKey>(json)).isEqualTo(publicKey)
}
@Test
fun AnonymousParty() {
val anon = AnonymousParty(ALICE_PUBKEY)
val json = mapper.valueToTree<TextNode>(anon)
assertThat(json.textValue()).isEqualTo(ALICE_PUBKEY.toBase58String())
assertThat(mapper.convertValue<AnonymousParty>(json)).isEqualTo(anon)
}
@Test
fun `PartyAndCertificate serialisation`() {
val json = mapper.valueToTree<ObjectNode>(MINI_CORP.identity)
assertThat(json.fieldNames()).containsOnly("name", "owningKey")
assertThat(mapper.convertValue<CordaX500Name>(json["name"])).isEqualTo(MINI_CORP.name)
assertThat(mapper.convertValue<PublicKey>(json["owningKey"])).isEqualTo(MINI_CORP.publicKey)
}
@Test
fun `NodeInfo serialisation`() {
val (nodeInfo) = createNodeInfoAndSigned(ALICE_NAME)
val json = mapper.valueToTree<ObjectNode>(nodeInfo)
assertThat(json.fieldNames()).containsOnly("addresses", "legalIdentitiesAndCerts", "platformVersion", "serial")
val address = (json["addresses"] as ArrayNode).also { assertThat(it).hasSize(1) }[0]
assertThat(mapper.convertValue<NetworkHostAndPort>(address)).isEqualTo(nodeInfo.addresses[0])
val identity = (json["legalIdentitiesAndCerts"] as ArrayNode).also { assertThat(it).hasSize(1) }[0]
assertThat(mapper.convertValue<CordaX500Name>(identity["name"])).isEqualTo(ALICE_NAME)
assertThat(mapper.convertValue<Int>(json["platformVersion"])).isEqualTo(nodeInfo.platformVersion)
assertThat(mapper.convertValue<Long>(json["serial"])).isEqualTo(nodeInfo.serial)
}
@Test
fun `NodeInfo deserialisation on name`() {
val (nodeInfo) = createNodeInfoAndSigned(ALICE_NAME)
fun convertToNodeInfo() = mapper.convertValue<NodeInfo>(TextNode(ALICE_NAME.toString()))
assertThatThrownBy { convertToNodeInfo() }
partyObjectMapper.identities += nodeInfo.legalIdentities
partyObjectMapper.nodes += nodeInfo
assertThat(convertToNodeInfo()).isEqualTo(nodeInfo)
}
@Test
fun `NodeInfo deserialisation on public key`() {
val (nodeInfo) = createNodeInfoAndSigned(ALICE_NAME)
fun convertToNodeInfo() = mapper.convertValue<NodeInfo>(TextNode(nodeInfo.legalIdentities[0].owningKey.toBase58String()))
assertThatThrownBy { convertToNodeInfo() }
partyObjectMapper.identities += nodeInfo.legalIdentities
partyObjectMapper.nodes += nodeInfo
assertThat(convertToNodeInfo()).isEqualTo(nodeInfo)
}
private fun makeDummyStx(): SignedTransaction {
val wtx = DummyContract.generateInitial(1, DUMMY_NOTARY, MINI_CORP.ref(1)) val wtx = DummyContract.generateInitial(1, DUMMY_NOTARY, MINI_CORP.ref(1))
.toWireTransaction(services) .toWireTransaction(services)
val signatures = listOf( val signatures = listOf(
@ -129,4 +273,27 @@ class JacksonSupportTest {
) )
return SignedTransaction(wtx, signatures) return SignedTransaction(wtx, signatures)
} }
private inline fun <reified T : Any> testToStringSerialisation(value: T) {
val json = mapper.valueToTree<TextNode>(value)
assertThat(json.textValue()).isEqualTo(value.toString())
assertThat(mapper.convertValue<T>(json)).isEqualTo(value)
}
private class TestPartyObjectMapper : JacksonSupport.PartyObjectMapper {
val identities = ArrayList<Party>()
val nodes = ArrayList<NodeInfo>()
override fun wellKnownPartyFromX500Name(name: CordaX500Name): Party? {
return identities.find { it.name == name }
}
override fun partyFromKey(owningKey: PublicKey): Party? {
return identities.find { it.owningKey == owningKey }
}
override fun partiesFromName(query: String): Set<Party> {
return identities.filter { query in it.name.toString() }.toSet()
}
override fun nodeInfoFromParty(party: AbstractParty): NodeInfo? {
return nodes.find { party in it.legalIdentities }
}
}
} }

View File

@ -8,10 +8,20 @@ Unreleased
========== ==========
* Fixed an error thrown by NodeVaultService upon recording a transaction with a number of inputs greater than the default page size. * Fixed an error thrown by NodeVaultService upon recording a transaction with a number of inputs greater than the default page size.
* ``SignedTransaction`` can now be serialized to JSON and deserialized back into an object.
* Fixed incorrect computation of ``totalStates`` from ``otherResults`` in ``NodeVaultService``. * Fixed incorrect computation of ``totalStates`` from ``otherResults`` in ``NodeVaultService``.
* Changes to the JSON/YAML serialisation format from ``JacksonSupport``, which also applies to the node shell:
* ``Instant`` and ``Date`` objects are serialised as ISO-8601 formatted strings rather than timestamps
* ``PublicKey`` objects are serialised and looked up according to their Base58 encoded string
* ``Party`` objects can be deserialised by looking up their public key, in addition to their name
* ``NodeInfo`` objects are serialised as an object and can be looked up using the same mechanism as ``Party``
* ``NetworkHostAndPort`` serialised according to its ``toString()``
* ``PartyAndCertificate`` is serialised as an object containing the name and owning key
* ``SignedTransaction`` can now be serialized to JSON and deserialized back into an object.
* Several members of ``JacksonSupport`` have been deprecated to highlight that they are internal and not to be used
* Refactor AMQP Serializer to pass context object down the serialization call hierarchy. Will allow per thread * Refactor AMQP Serializer to pass context object down the serialization call hierarchy. Will allow per thread
extensions to be set and used by the RPC work (Observable Context Key) extensions to be set and used by the RPC work (Observable Context Key)

View File

@ -91,13 +91,13 @@ Windows
Windows does not provide a built-in SSH tool. An alternative such as PuTTY should be used. Windows does not provide a built-in SSH tool. An alternative such as PuTTY should be used.
The standalone shell The standalone shell
------------------------------ --------------------
The standalone shell is a standalone application interacting with a Corda node via RPC calls. The standalone shell is a standalone application interacting with a Corda node via RPC calls.
RPC node permissions are necessary for authentication and authorisation. RPC node permissions are necessary for authentication and authorisation.
Certain operations, such as starting flows, require access to CordApps jars. Certain operations, such as starting flows, require access to CordApps jars.
Starting the standalone shell Starting the standalone shell
************************* *****************************
Run the following command from the terminal: Run the following command from the terminal:
@ -177,7 +177,7 @@ The format of ``config-file``:
Standalone Shell via SSH Standalone Shell via SSH
------------------------------------------ ------------------------
The standalone shell can embed an SSH server which redirects interactions via RPC calls to the Corda node. The standalone shell can embed an SSH server which redirects interactions via RPC calls to the Corda node.
To run SSH server use ``--sshd-port`` option when starting standalone shell or ``extensions.sshd`` entry in the configuration file. To run SSH server use ``--sshd-port`` option when starting standalone shell or ``extensions.sshd`` entry in the configuration file.
For connection to SSH refer to `Connecting to the shell`_. For connection to SSH refer to `Connecting to the shell`_.
@ -253,8 +253,8 @@ Where ``newCampaign`` is a parameter of type ``Campaign``.
Mappings from strings to types Mappings from strings to types
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Several parameter types can automatically be mapped from strings. See the `defined parsers`_ for more information. We In addition to the types already supported by Jackson, several parameter types can automatically be mapped from strings.
cover the most common types here. We cover the most common types here.
Amount Amount
~~~~~~ ~~~~~~
@ -263,23 +263,44 @@ A parameter of type ``Amount<Currency>`` can be written as either:
* A dollar ($), pound (£) or euro (€) symbol followed by the amount as a decimal * A dollar ($), pound (£) or euro (€) symbol followed by the amount as a decimal
* The amount as a decimal followed by the ISO currency code (e.g. "100.12 CHF") * The amount as a decimal followed by the ISO currency code (e.g. "100.12 CHF")
SecureHash
~~~~~~~~~~
A parameter of type ``SecureHash`` can be written as a hexadecimal string: ``F69A7626ACC27042FEEAE187E6BFF4CE666E6F318DC2B32BE9FAF87DF687930C``
OpaqueBytes OpaqueBytes
~~~~~~~~~~~ ~~~~~~~~~~~
A parameter of type ``OpaqueBytes`` can be provided as a string, which will be automatically converted to A parameter of type ``OpaqueBytes`` can be provided as a string in Base64.
``OpaqueBytes``.
PublicKey and CompositeKey
~~~~~~~~~~~~~~~~~~~~~~~~~~
A parameter of type ``PublicKey`` can be written as a Base58 string of its encoded format: ``GfHq2tTVk9z4eXgyQXzegw6wNsZfHcDhfw8oTt6fCHySFGp3g7XHPAyc2o6D``.
``net.corda.core.utilities.EncodingUtils.toBase58String`` will convert a ``PublicKey`` to this string format.
Party Party
~~~~~ ~~~~~
A parameter of type ``Party`` can be written in several ways: A parameter of type ``Party`` can be written in several ways:
* By using the node's full name: ``"O=Monogram Bank,L=Sao Paulo,C=GB"`` * By using the full name: ``"O=Monogram Bank,L=Sao Paulo,C=GB"``
* By specifying the organisation name only: ``"Monogram Bank"`` * By specifying the organisation name only: ``"Monogram Bank"``
* By specifying any other non-ambiguous part of the name: ``"Sao Paulo"`` (if only one network node is located in Sao * By specifying any other non-ambiguous part of the name: ``"Sao Paulo"`` (if only one network node is located in Sao
Paulo) Paulo)
* By specifying the public key (see above)
Instant NodeInfo
~~~~~~~ ~~~~~~~~
A parameter of type ``Instant`` can be written as follows: ``"2017-12-22T00:00:00Z"``. A parameter of type ``NodeInfo`` can be written in terms of one of its identities (see ``Party`` above)
AnonymousParty
~~~~~~~~~~~~~~
A parameter of type ``AnonymousParty`` can be written in terms of its ``PublicKey`` (see above)
NetworkHostAndPort
~~~~~~~~~~~~~~~~~~
A parameter of type ``NetworkHostAndPort`` can be written as a "host:port" string: ``"localhost:1010"``
Instant and Date
~~~~~~~~~~~~~~~~
A parameter of ``Instant`` and ``Date`` can be written as an ISO-8601 string: ``"2017-12-22T00:00:00Z"``
Examples Examples
^^^^^^^^ ^^^^^^^^
@ -365,6 +386,5 @@ The shell will be enhanced over time. The currently known limitations include:
* The ``jul`` command advertises access to logs, but it doesn't work with the logging framework we're using * The ``jul`` command advertises access to logs, but it doesn't work with the logging framework we're using
.. _Yaml: http://www.yaml.org/spec/1.2/spec.html .. _Yaml: http://www.yaml.org/spec/1.2/spec.html
.. _defined parsers: api/kotlin/corda/net.corda.client.jackson/-jackson-support/index.html
.. _Groovy: http://groovy-lang.org/ .. _Groovy: http://groovy-lang.org/
.. _CRaSH: http://www.crashub.org/ .. _CRaSH: http://www.crashub.org/

View File

@ -1,6 +1,6 @@
{ {
"fixedLeg": { "fixedLeg": {
"fixedRatePayer": "MCowBQYDK2VwAyEAzswVB9wd3XKVlRwpCIjwla25BE0bc9aW5t8GXWg71Pw=", "fixedRatePayer": "GfHq2tTVk9z4eXgyUEefbHpUFfpnDvsFoZVZe3ikrLbwdRA4jebSJPykJwgw",
"notional": "$25000000", "notional": "$25000000",
"paymentFrequency": "SemiAnnual", "paymentFrequency": "SemiAnnual",
"effectiveDate": "2016-03-11", "effectiveDate": "2016-03-11",
@ -22,7 +22,7 @@
"interestPeriodAdjustment": "Adjusted" "interestPeriodAdjustment": "Adjusted"
}, },
"floatingLeg": { "floatingLeg": {
"floatingRatePayer": "MCowBQYDK2VwAyEAa3nFfmoJUjkoLASBjpYRLz8DpAAbqXpWTCOFKj8epfw=", "floatingRatePayer": "GfHq2tTVk9z4eXgyMYwWRYKSgGpARSquPTt8V4Z54RmNe2SJ7BUq2jSUUfvT",
"notional": { "notional": {
"quantity": 2500000000, "quantity": 2500000000,
"token": "USD" "token": "USD"

View File

@ -64,7 +64,7 @@ class TestNodeInfoBuilder(private val intermediateAndRoot: Pair<CertificateAndKe
fun build(serial: Long = 1, platformVersion: Int = 1): NodeInfo { fun build(serial: Long = 1, platformVersion: Int = 1): NodeInfo {
return NodeInfo( return NodeInfo(
listOf(NetworkHostAndPort("my.${identitiesAndPrivateKeys[0].first.party.name.organisation}.com", 1234)), listOf(NetworkHostAndPort("my.${identitiesAndPrivateKeys[0].first.party.name.organisation.replace(' ', '-')}.com", 1234)),
identitiesAndPrivateKeys.map { it.first }, identitiesAndPrivateKeys.map { it.first },
platformVersion, platformVersion,
serial serial

View File

@ -39,7 +39,6 @@ dependencies {
// Jackson support: serialisation to/from JSON, YAML, etc // Jackson support: serialisation to/from JSON, YAML, etc
compile project(':client:jackson') compile project(':client:jackson')
compile group: 'org.json', name: 'json', version: json_version
// JOpt: for command line flags. // JOpt: for command line flags.

View File

@ -1,10 +1,7 @@
package net.corda.tools.shell package net.corda.tools.shell
import com.fasterxml.jackson.core.JsonGenerator
import com.fasterxml.jackson.databind.JsonSerializer
import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.SerializationFeature import com.fasterxml.jackson.databind.SerializationFeature
import com.fasterxml.jackson.databind.SerializerProvider
import com.fasterxml.jackson.databind.module.SimpleModule import com.fasterxml.jackson.databind.module.SimpleModule
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory import com.fasterxml.jackson.dataformat.yaml.YAMLFactory
import net.corda.client.jackson.JacksonSupport import net.corda.client.jackson.JacksonSupport
@ -17,13 +14,10 @@ import net.corda.core.CordaException
import net.corda.core.concurrent.CordaFuture import net.corda.core.concurrent.CordaFuture
import net.corda.core.contracts.UniqueIdentifier import net.corda.core.contracts.UniqueIdentifier
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
import net.corda.core.identity.Party
import net.corda.core.internal.* import net.corda.core.internal.*
import net.corda.core.internal.concurrent.doneFuture import net.corda.core.internal.concurrent.doneFuture
import net.corda.core.internal.concurrent.openFuture import net.corda.core.internal.concurrent.openFuture
import net.corda.core.messaging.* import net.corda.core.messaging.*
import net.corda.core.node.NodeInfo
import net.corda.core.utilities.NetworkHostAndPort
import net.corda.tools.shell.utlities.ANSIProgressRenderer import net.corda.tools.shell.utlities.ANSIProgressRenderer
import net.corda.tools.shell.utlities.StdoutANSIProgressRenderer import net.corda.tools.shell.utlities.StdoutANSIProgressRenderer
import org.crsh.command.InvocationContext import org.crsh.command.InvocationContext
@ -45,7 +39,6 @@ import org.crsh.util.Utils
import org.crsh.vfs.FS import org.crsh.vfs.FS
import org.crsh.vfs.spi.file.FileMountFactory import org.crsh.vfs.spi.file.FileMountFactory
import org.crsh.vfs.spi.url.ClassPathMountFactory import org.crsh.vfs.spi.url.ClassPathMountFactory
import org.json.JSONObject
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import rx.Observable import rx.Observable
import rx.Subscriber import rx.Subscriber
@ -187,43 +180,22 @@ object InteractiveShell {
// Return a standard Corda Jackson object mapper, configured to use YAML by default and with extra // Return a standard Corda Jackson object mapper, configured to use YAML by default and with extra
// serializers. // serializers.
return JacksonSupport.createDefaultMapper(rpcOps, YAMLFactory(), true).apply { return JacksonSupport.createDefaultMapper(rpcOps, YAMLFactory(), true).apply {
val rpcModule = SimpleModule() val rpcModule = SimpleModule().apply {
rpcModule.addDeserializer(InputStream::class.java, InputStreamDeserializer) addDeserializer(InputStream::class.java, InputStreamDeserializer)
rpcModule.addDeserializer(UniqueIdentifier::class.java, UniqueIdentifierDeserializer) addDeserializer(UniqueIdentifier::class.java, UniqueIdentifierDeserializer)
rpcModule.addDeserializer(UUID::class.java, UUIDDeserializer) }
registerModule(rpcModule) registerModule(rpcModule)
} }
} }
private object NodeInfoSerializer : JsonSerializer<NodeInfo>() {
override fun serialize(nodeInfo: NodeInfo, gen: JsonGenerator, serializers: SerializerProvider) {
val json = JSONObject()
json["addresses"] = nodeInfo.addresses.map { address -> address.serialise() }
json["legalIdentities"] = nodeInfo.legalIdentities.map { address -> address.serialise() }
json["platformVersion"] = nodeInfo.platformVersion
json["serial"] = nodeInfo.serial
gen.writeRaw(json.toString())
}
private fun NetworkHostAndPort.serialise() = this.toString()
private fun Party.serialise() = JSONObject().put("name", this.name)
private operator fun JSONObject.set(key: String, value: Any?): JSONObject {
return put(key, value)
}
}
private fun createOutputMapper(): ObjectMapper { private fun createOutputMapper(): ObjectMapper {
return JacksonSupport.createNonRpcMapper().apply { return JacksonSupport.createNonRpcMapper().apply {
// Register serializers for stateful objects from libraries that are special to the RPC system and don't // Register serializers for stateful objects from libraries that are special to the RPC system and don't
// make sense to print out to the screen. For classes we own, annotations can be used instead. // make sense to print out to the screen. For classes we own, annotations can be used instead.
val rpcModule = SimpleModule() val rpcModule = SimpleModule().apply {
rpcModule.addSerializer(Observable::class.java, ObservableSerializer) addSerializer(Observable::class.java, ObservableSerializer)
rpcModule.addSerializer(InputStream::class.java, InputStreamSerializer) addSerializer(InputStream::class.java, InputStreamSerializer)
rpcModule.addSerializer(NodeInfo::class.java, NodeInfoSerializer) }
registerModule(rpcModule) registerModule(rpcModule)
disable(SerializationFeature.FAIL_ON_EMPTY_BEANS) disable(SerializationFeature.FAIL_ON_EMPTY_BEANS)
@ -240,7 +212,12 @@ object InteractiveShell {
* the [runFlowFromString] method and starts the requested flow. Ctrl-C can be used to cancel. * the [runFlowFromString] method and starts the requested flow. Ctrl-C can be used to cancel.
*/ */
@JvmStatic @JvmStatic
fun runFlowByNameFragment(nameFragment: String, inputData: String, output: RenderPrintWriter, rpcOps: CordaRPCOps, ansiProgressRenderer: ANSIProgressRenderer, om: ObjectMapper) { fun runFlowByNameFragment(nameFragment: String,
inputData: String,
output: RenderPrintWriter,
rpcOps: CordaRPCOps,
ansiProgressRenderer: ANSIProgressRenderer,
om: ObjectMapper) {
val matches = try { val matches = try {
rpcOps.registeredFlows().filter { nameFragment in it } rpcOps.registeredFlows().filter { nameFragment in it }
} catch (e: PermissionException) { } catch (e: PermissionException) {

View File

@ -50,16 +50,6 @@ object UniqueIdentifierDeserializer : JsonDeserializer<UniqueIdentifier>() {
} }
} }
/**
* String value deserialized to [UUID].
* */
object UUIDDeserializer : JsonDeserializer<UUID>() {
override fun deserialize(p: JsonParser, ctxt: DeserializationContext): UUID {
//Create UUID object from string.
return UUID.fromString(p.text)
}
}
// An InputStream found in a response triggers a request to the user to provide somewhere to save it. // An InputStream found in a response triggers a request to the user to provide somewhere to save it.
object InputStreamSerializer : JsonSerializer<InputStream>() { object InputStreamSerializer : JsonSerializer<InputStream>() {
var invokeContext: InvocationContext<*>? = null var invokeContext: InvocationContext<*>? = null

View File

@ -28,7 +28,6 @@ class CustomTypeJsonParsingTests {
objectMapper = ObjectMapper() objectMapper = ObjectMapper()
val simpleModule = SimpleModule() val simpleModule = SimpleModule()
simpleModule.addDeserializer(UniqueIdentifier::class.java, UniqueIdentifierDeserializer) simpleModule.addDeserializer(UniqueIdentifier::class.java, UniqueIdentifierDeserializer)
simpleModule.addDeserializer(UUID::class.java, UUIDDeserializer)
objectMapper.registerModule(simpleModule) objectMapper.registerModule(simpleModule)
} }