mirror of
https://github.com/corda/corda.git
synced 2024-12-21 05:53:23 +00:00
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:
parent
3f21c47f39
commit
3bb95c3ed1
@ -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()
|
||||||
|
@ -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'
|
||||||
|
@ -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>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
@ -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 }
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
@ -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/
|
||||||
|
@ -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"
|
||||||
|
@ -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
|
||||||
|
@ -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.
|
||||||
|
@ -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) {
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user