Merge branch 'mike-json-support-module'

This commit is contained in:
Mike Hearn 2017-03-08 15:06:50 +01:00
commit ca70ca051c
11 changed files with 93 additions and 25 deletions

View File

@ -179,7 +179,7 @@ bintrayConfig {
projectUrl = 'https://github.com/corda/corda' projectUrl = 'https://github.com/corda/corda'
gpgSign = true gpgSign = true
gpgPassphrase = System.getenv('CORDA_BINTRAY_GPG_PASSPHRASE') gpgPassphrase = System.getenv('CORDA_BINTRAY_GPG_PASSPHRASE')
publications = ['client', 'core', 'corda', 'corda-webserver', 'finance', 'node', 'node-schemas', 'test-utils'] publications = ['client', 'core', 'corda', 'corda-webserver', 'finance', 'node', 'node-schemas', 'test-utils', 'jackson']
license { license {
name = 'Apache-2.0' name = 'Apache-2.0'
url = 'https://www.apache.org/licenses/LICENSE-2.0' url = 'https://www.apache.org/licenses/LICENSE-2.0'
@ -198,7 +198,7 @@ dokka {
moduleName = 'corda' moduleName = 'corda'
outputDirectory = 'docs/build/html/api/kotlin' outputDirectory = 'docs/build/html/api/kotlin'
processConfigurations = ['compile'] processConfigurations = ['compile']
sourceDirs = files('core/src/main/kotlin', 'client/src/main/kotlin', 'node/src/main/kotlin', 'finance/src/main/kotlin') sourceDirs = files('core/src/main/kotlin', 'client/src/main/kotlin', 'node/src/main/kotlin', 'finance/src/main/kotlin', 'client/jackson/src/main/kotlin')
} }
task dokkaJavadoc(type: org.jetbrains.dokka.gradle.DokkaTask) { task dokkaJavadoc(type: org.jetbrains.dokka.gradle.DokkaTask) {
@ -206,7 +206,7 @@ task dokkaJavadoc(type: org.jetbrains.dokka.gradle.DokkaTask) {
outputFormat = "javadoc" outputFormat = "javadoc"
outputDirectory = 'docs/build/html/api/javadoc' outputDirectory = 'docs/build/html/api/javadoc'
processConfigurations = ['compile'] processConfigurations = ['compile']
sourceDirs = files('core/src/main/kotlin', 'client/src/main/kotlin', 'node/src/main/kotlin', 'finance/src/main/kotlin') sourceDirs = files('core/src/main/kotlin', 'client/src/main/kotlin', 'node/src/main/kotlin', 'finance/src/main/kotlin', 'client/jackson/src/main/kotlin')
} }
task apidocs(dependsOn: ['dokka', 'dokkaJavadoc']) task apidocs(dependsOn: ['dokka', 'dokkaJavadoc'])

View File

@ -0,0 +1,19 @@
apply plugin: 'java'
apply plugin: 'kotlin'
apply plugin: 'net.corda.plugins.publish-utils'
repositories {
mavenLocal()
mavenCentral()
jcenter()
maven {
url 'https://dl.bintray.com/kotlin/exposed'
}
}
dependencies {
compile project(':core')
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
compile ("com.fasterxml.jackson.module:jackson-module-kotlin:${jackson_version}")
testCompile "junit:junit:$junit_version"
}

View File

@ -1,4 +1,4 @@
package net.corda.webserver.utilities package net.corda.jackson
import com.fasterxml.jackson.core.JsonGenerator import com.fasterxml.jackson.core.JsonGenerator
import com.fasterxml.jackson.core.JsonParseException import com.fasterxml.jackson.core.JsonParseException
@ -13,10 +13,10 @@ import net.corda.core.contracts.BusinessCalendar
import net.corda.core.crypto.* import net.corda.core.crypto.*
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.serialization.deserialize import net.corda.core.serialization.deserialize
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
import net.i2p.crypto.eddsa.EdDSAPublicKey import net.i2p.crypto.eddsa.EdDSAPublicKey
import net.corda.core.node.services.IdentityService
import java.math.BigDecimal import java.math.BigDecimal
import java.time.LocalDate import java.time.LocalDate
import java.time.LocalDateTime import java.time.LocalDateTime
@ -25,9 +25,11 @@ import java.time.LocalDateTime
* Utilities and serialisers for working with JSON representations of basic types. This adds Jackson support for * Utilities and serialisers for working with JSON representations of basic types. This adds Jackson support for
* the java.time API, some core types, and Kotlin data classes. * the java.time API, some core types, and Kotlin data classes.
* *
* TODO: This does not belong in node. It should be moved to the client module or a dedicated webserver module. * Note that Jackson can also be used to serialise/deserialise other formats such as Yaml and XML.
*/ */
object JsonSupport { object JacksonSupport {
// If you change this API please update the docs in the docsite (json.rst)
interface PartyObjectMapper { interface PartyObjectMapper {
fun partyFromName(partyName: String): Party? fun partyFromName(partyName: String): Party?
fun partyFromKey(owningKey: CompositeKey): Party? fun partyFromKey(owningKey: CompositeKey): Party?
@ -84,13 +86,16 @@ object JsonSupport {
} }
} }
/* Mapper requiring RPC support to deserialise parties from names */ /** Mapper requiring RPC support to deserialise parties from names */
@JvmStatic
fun createDefaultMapper(rpc: CordaRPCOps): ObjectMapper = configureMapper(RpcObjectMapper(rpc)) fun createDefaultMapper(rpc: CordaRPCOps): ObjectMapper = configureMapper(RpcObjectMapper(rpc))
/* For testing or situations where deserialising parties is not required */ /** For testing or situations where deserialising parties is not required */
@JvmStatic
fun createNonRpcMapper(): ObjectMapper = configureMapper(NoPartyObjectMapper()) fun createNonRpcMapper(): ObjectMapper = configureMapper(NoPartyObjectMapper())
/* For testing with an in memory identity service */ /** For testing with an in memory identity service */
@JvmStatic
fun createInMemoryMapper(identityService: IdentityService) = configureMapper(IdentityObjectMapper(identityService)) fun createInMemoryMapper(identityService: IdentityService) = configureMapper(IdentityObjectMapper(identityService))
private fun configureMapper(mapper: ObjectMapper): ObjectMapper = mapper.apply { private fun configureMapper(mapper: ObjectMapper): ObjectMapper = mapper.apply {
@ -249,3 +254,4 @@ object JsonSupport {
} }
} }
} }

View File

@ -1,20 +1,18 @@
package net.corda.webserver package net.corda.jackson
import com.pholser.junit.quickcheck.From import com.pholser.junit.quickcheck.From
import com.pholser.junit.quickcheck.Property import com.pholser.junit.quickcheck.Property
import com.pholser.junit.quickcheck.runner.JUnitQuickcheck import com.pholser.junit.quickcheck.runner.JUnitQuickcheck
import net.corda.core.testing.PublicKeyGenerator import net.corda.core.testing.PublicKeyGenerator
import net.corda.webserver.utilities.JsonSupport
import net.i2p.crypto.eddsa.EdDSAPublicKey import net.i2p.crypto.eddsa.EdDSAPublicKey
import org.junit.runner.RunWith import org.junit.runner.RunWith
import java.security.PublicKey import java.security.PublicKey
import kotlin.test.assertEquals import kotlin.test.assertEquals
@RunWith(JUnitQuickcheck::class) @RunWith(JUnitQuickcheck::class)
class JsonSupportTest { class JacksonSupportTest {
companion object { companion object {
val mapper = JsonSupport.createNonRpcMapper() val mapper = JacksonSupport.createNonRpcMapper()
} }
@Property @Property

View File

@ -109,6 +109,7 @@ Documentation Contents:
network-simulator network-simulator
clauses clauses
merkle-trees merkle-trees
json
.. toctree:: .. toctree::
:maxdepth: 2 :maxdepth: 2

42
docs/source/json.rst Normal file
View File

@ -0,0 +1,42 @@
.. highlight:: kotlin
.. raw:: html
<script type="text/javascript" src="_static/jquery.js"></script>
<script type="text/javascript" src="_static/codesets.js"></script>
JSON
====
Corda provides a module that extends the popular Jackson serialisation engine. Jackson is often used to serialise
to and from JSON, but also supports other formats such as YaML and XML. Jackson is itself very modular and has
a variety of plugins that extend its functionality. You can learn more at the `Jackson home page <https://github.com/FasterXML/jackson>`_.
To gain support for JSON serialisation of common Corda data types, include a dependency on ``net.corda:jackson:XXX``
in your Gradle or Maven build file, where XXX is of course the Corda version you are targeting (0.9 for M9, for instance).
Then you can obtain a Jackson ``ObjectMapper`` instance configured for use using the ``JacksonSupport.createNonRpcMapper()``
method. There are variants of this method for obtaining Jackson's configured in other ways: if you have an RPC
connection to the node (see ":doc:`clientrpc`") then your JSON mapper can resolve identities found in objects.
The API is described in detail here:
* `Kotlin API docs <api/kotlin/corda/net.corda.jackson/-jackson-support/index.html>`_
* `JavaDoc <api/javadoc/net/corda/jackson/package-summary.html>`_
.. container:: codeset
.. sourcecode:: kotlin
import net.corda.jackson.JacksonSupport
val mapper = JacksonSupport.createNonRpcMapper()
val json = mapper.writeValueAsString(myCordaState) // myCordaState can be any object.
.. sourcecode:: java
import net.corda.jackson.JacksonSupport
ObjectMapper mapper = JacksonSupport.createNonRpcMapper()
String json = mapper.writeValueAsString(myCordaState) // myCordaState can be any object.
.. note:: The way mappers interact with identity and RPC is likely to change in a future release.

View File

@ -44,6 +44,7 @@ sourceSets {
dependencies { dependencies {
compile project(':core') compile project(':core')
compile project(':node') // TODO: Break this dependency compile project(':node') // TODO: Break this dependency
compile project(':client:jackson')
// Web stuff: for HTTP[S] servlets // Web stuff: for HTTP[S] servlets
compile "org.eclipse.jetty:jetty-servlet:${jetty_version}" compile "org.eclipse.jetty:jetty-servlet:${jetty_version}"

View File

@ -2,7 +2,7 @@ package net.corda.webserver.servlets
import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.ObjectMapper
import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.CordaRPCOps
import net.corda.webserver.utilities.JsonSupport import net.corda.jackson.JacksonSupport
import javax.ws.rs.ext.ContextResolver import javax.ws.rs.ext.ContextResolver
import javax.ws.rs.ext.Provider import javax.ws.rs.ext.Provider
@ -12,6 +12,6 @@ import javax.ws.rs.ext.Provider
*/ */
@Provider @Provider
class ObjectMapperConfig(rpc: CordaRPCOps) : ContextResolver<ObjectMapper> { class ObjectMapperConfig(rpc: CordaRPCOps) : ContextResolver<ObjectMapper> {
val defaultObjectMapper = JsonSupport.createDefaultMapper(rpc) val defaultObjectMapper = JacksonSupport.createDefaultMapper(rpc)
override fun getContext(type: Class<*>) = defaultObjectMapper override fun getContext(type: Class<*>) = defaultObjectMapper
} }

View File

@ -18,13 +18,11 @@ import net.corda.flows.TwoPartyDealFlow.Acceptor
import net.corda.flows.TwoPartyDealFlow.AutoOffer import net.corda.flows.TwoPartyDealFlow.AutoOffer
import net.corda.flows.TwoPartyDealFlow.Instigator import net.corda.flows.TwoPartyDealFlow.Instigator
import net.corda.irs.contract.InterestRateSwap import net.corda.irs.contract.InterestRateSwap
import net.corda.jackson.JacksonSupport
import net.corda.node.utilities.databaseTransaction import net.corda.node.utilities.databaseTransaction
import net.corda.testing.initiateSingleShotFlow import net.corda.testing.initiateSingleShotFlow
import net.corda.testing.node.InMemoryMessagingNetwork import net.corda.testing.node.InMemoryMessagingNetwork
import net.corda.testing.node.MockIdentityService import net.corda.testing.node.MockIdentityService
import net.i2p.crypto.eddsa.KeyPairGenerator
import java.security.SecureRandom
import net.corda.webserver.utilities.JsonSupport
import java.time.LocalDate import java.time.LocalDate
import java.util.* import java.util.*
@ -33,7 +31,7 @@ import java.util.*
* A simulation in which banks execute interest rate swaps with each other, including the fixing events. * A simulation in which banks execute interest rate swaps with each other, including the fixing events.
*/ */
class IRSSimulation(networkSendManuallyPumped: Boolean, runAsync: Boolean, latencyInjector: InMemoryMessagingNetwork.LatencyCalculator?) : Simulation(networkSendManuallyPumped, runAsync, latencyInjector) { class IRSSimulation(networkSendManuallyPumped: Boolean, runAsync: Boolean, latencyInjector: InMemoryMessagingNetwork.LatencyCalculator?) : Simulation(networkSendManuallyPumped, runAsync, latencyInjector) {
val om = JsonSupport.createInMemoryMapper(MockIdentityService(network.identities)) val om = JacksonSupport.createInMemoryMapper(MockIdentityService(network.identities))
init { init {
currentDateAndTime = LocalDate.of(2016, 3, 8).atStartOfDay() currentDateAndTime = LocalDate.of(2016, 3, 8).atStartOfDay()

View File

@ -9,6 +9,7 @@ include 'node'
include 'node:capsule' include 'node:capsule'
include 'node:webserver' include 'node:webserver'
include 'client' include 'client'
include 'client:jackson'
include 'experimental' include 'experimental'
include 'experimental:sandbox' include 'experimental:sandbox'
include 'test-utils' include 'test-utils'
@ -22,4 +23,3 @@ include 'samples:network-visualiser'
include 'samples:simm-valuation-demo' include 'samples:simm-valuation-demo'
include 'samples:raft-notary-demo' include 'samples:raft-notary-demo'
include 'samples:bank-of-corda-demo' include 'samples:bank-of-corda-demo'

View File

@ -3,8 +3,11 @@ package net.corda.testing.http
import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.kotlin.KotlinModule import com.fasterxml.jackson.module.kotlin.KotlinModule
import net.corda.core.utilities.loggerFor import net.corda.core.utilities.loggerFor
import net.corda.webserver.utilities.JsonSupport import net.corda.jackson.JacksonSupport
import okhttp3.* import okhttp3.MediaType
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody
import java.net.URL import java.net.URL
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
@ -19,7 +22,7 @@ object HttpUtils {
.readTimeout(60, TimeUnit.SECONDS).build() .readTimeout(60, TimeUnit.SECONDS).build()
} }
val defaultMapper: ObjectMapper by lazy { val defaultMapper: ObjectMapper by lazy {
ObjectMapper().registerModule(JsonSupport.javaTimeModule).registerModule(KotlinModule()) ObjectMapper().registerModule(JacksonSupport.javaTimeModule).registerModule(KotlinModule())
} }
fun putJson(url: URL, data: String) : Boolean { fun putJson(url: URL, data: String) : Boolean {