Remove finance dependencies

Re-enable code now DealState PR is in.

Add plugable JSON serialisation

Add docs for new plugin api.

Move parseCurrency back to core to prevent dependency issues with crash shell parsing.

Use :finance module as a proper CorDapp

Move parseCurrency back onto Amount companion.

Fix smoke tests

Fixup after merge.
This commit is contained in:
Matthew Nesbit
2017-08-15 09:14:15 +01:00
parent 4278bce5c8
commit 8ec33d67e4
30 changed files with 285 additions and 173 deletions

View File

@ -5,7 +5,6 @@ apply plugin: 'com.jfrog.artifactory'
dependencies { dependencies {
compile project(':core') compile project(':core')
compile project(':finance')
testCompile project(':test-utils') testCompile project(':test-utils')
compile "org.jetbrains.kotlin:kotlin-stdlib-jre8:$kotlin_version" compile "org.jetbrains.kotlin:kotlin-stdlib-jre8:$kotlin_version"

View File

@ -5,11 +5,9 @@ import com.fasterxml.jackson.annotation.JsonProperty
import com.fasterxml.jackson.core.* import com.fasterxml.jackson.core.*
import com.fasterxml.jackson.databind.* import com.fasterxml.jackson.databind.*
import com.fasterxml.jackson.databind.deser.std.NumberDeserializers import com.fasterxml.jackson.databind.deser.std.NumberDeserializers
import com.fasterxml.jackson.databind.deser.std.StringArrayDeserializer
import com.fasterxml.jackson.databind.module.SimpleModule import com.fasterxml.jackson.databind.module.SimpleModule
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 net.corda.contracts.BusinessCalendar
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
@ -29,12 +27,10 @@ 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.OpaqueBytes import net.corda.core.utilities.OpaqueBytes
import net.corda.finance.parseCurrency
import net.i2p.crypto.eddsa.EdDSAPublicKey import net.i2p.crypto.eddsa.EdDSAPublicKey
import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.asn1.x500.X500Name
import java.math.BigDecimal import java.math.BigDecimal
import java.security.PublicKey import java.security.PublicKey
import java.time.LocalDate
import java.util.* import java.util.*
/** /**
@ -84,8 +80,6 @@ object JacksonSupport {
addSerializer(SecureHash.SHA256::class.java, SecureHashSerializer) addSerializer(SecureHash.SHA256::class.java, SecureHashSerializer)
addDeserializer(SecureHash::class.java, SecureHashDeserializer()) addDeserializer(SecureHash::class.java, SecureHashDeserializer())
addDeserializer(SecureHash.SHA256::class.java, SecureHashDeserializer()) addDeserializer(SecureHash.SHA256::class.java, SecureHashDeserializer())
addSerializer(BusinessCalendar::class.java, CalendarSerializer)
addDeserializer(BusinessCalendar::class.java, CalendarDeserializer)
// For ed25519 pubkeys // For ed25519 pubkeys
addSerializer(EdDSAPublicKey::class.java, PublicKeySerializer) addSerializer(EdDSAPublicKey::class.java, PublicKeySerializer)
@ -277,36 +271,6 @@ object JacksonSupport {
} }
} }
data class BusinessCalendarWrapper(val holidayDates: List<LocalDate>) {
fun toCalendar() = BusinessCalendar(holidayDates)
}
object CalendarSerializer : JsonSerializer<BusinessCalendar>() {
override fun serialize(obj: BusinessCalendar, generator: JsonGenerator, context: SerializerProvider) {
val calendarName = BusinessCalendar.calendars.find { BusinessCalendar.getInstance(it) == obj }
if (calendarName != null) {
generator.writeString(calendarName)
} else {
generator.writeObject(BusinessCalendarWrapper(obj.holidayDates))
}
}
}
object CalendarDeserializer : JsonDeserializer<BusinessCalendar>() {
override fun deserialize(parser: JsonParser, context: DeserializationContext): BusinessCalendar {
return try {
try {
val array = StringArrayDeserializer.instance.deserialize(parser, context)
BusinessCalendar.getInstance(*array)
} catch (e: Exception) {
parser.readValueAs(BusinessCalendarWrapper::class.java).toCalendar()
}
} catch (e: Exception) {
throw JsonParseException(parser, "Invalid calendar(s) ${parser.text}: ${e.message}")
}
}
}
object PublicKeySerializer : JsonSerializer<EdDSAPublicKey>() { object PublicKeySerializer : JsonSerializer<EdDSAPublicKey>() {
override fun serialize(obj: EdDSAPublicKey, generator: JsonGenerator, provider: SerializerProvider) { override fun serialize(obj: EdDSAPublicKey, generator: JsonGenerator, provider: SerializerProvider) {
check(obj.params == Crypto.EDDSA_ED25519_SHA512.algSpec) check(obj.params == Crypto.EDDSA_ED25519_SHA512.algSpec)
@ -349,7 +313,7 @@ object JacksonSupport {
object AmountDeserializer : JsonDeserializer<Amount<*>>() { object AmountDeserializer : JsonDeserializer<Amount<*>>() {
override fun deserialize(parser: JsonParser, context: DeserializationContext): Amount<*> { override fun deserialize(parser: JsonParser, context: DeserializationContext): Amount<*> {
try { try {
return parseCurrency(parser.text) return Amount.parseCurrency(parser.text)
} catch (e: Exception) { } catch (e: Exception) {
try { try {
val tree = parser.readValueAsTree<JsonNode>() val tree = parser.readValueAsTree<JsonNode>()

View File

@ -8,7 +8,6 @@ import net.corda.core.crypto.SignatureMetadata
import net.corda.core.crypto.TransactionSignature import net.corda.core.crypto.TransactionSignature
import net.corda.core.crypto.generateKeyPair import net.corda.core.crypto.generateKeyPair
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
import net.corda.finance.parseCurrency
import net.corda.testing.ALICE_PUBKEY import net.corda.testing.ALICE_PUBKEY
import net.corda.testing.DUMMY_NOTARY import net.corda.testing.DUMMY_NOTARY
import net.corda.testing.MINI_CORP import net.corda.testing.MINI_CORP
@ -53,7 +52,7 @@ class JacksonSupportTest : TestDependencyInjectionBase() {
@Test @Test
fun writeAmount() { fun writeAmount() {
val writer = mapper.writer().without(SerializationFeature.INDENT_OUTPUT) val writer = mapper.writer().without(SerializationFeature.INDENT_OUTPUT)
assertEquals("""{"notional":"25000000.00 USD"}""", writer.writeValueAsString(Dummy(parseCurrency("$25000000")))) assertEquals("""{"notional":"25000000.00 USD"}""", writer.writeValueAsString(Dummy(Amount.parseCurrency("$25000000"))))
} }
@Test @Test

View File

@ -50,6 +50,9 @@ processSmokeTestResources {
from(project(':node:capsule').tasks['buildCordaJAR']) { from(project(':node:capsule').tasks['buildCordaJAR']) {
rename 'corda-(.*)', 'corda.jar' rename 'corda-(.*)', 'corda.jar'
} }
from(project(':finance').tasks['jar']) {
rename 'finance-(.*)', 'finance.jar'
}
} }
// To find potential version conflicts, run "gradle htmlDependencyReport" and then look in // To find potential version conflicts, run "gradle htmlDependencyReport" and then look in

View File

@ -18,11 +18,17 @@ import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*; import java.util.*;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Stream;
import static kotlin.test.AssertionsKt.assertEquals; import static kotlin.test.AssertionsKt.assertEquals;
import static kotlin.test.AssertionsKt.fail;
import static net.corda.contracts.GetBalances.getCashBalance; import static net.corda.contracts.GetBalances.getCashBalance;
public class StandaloneCordaRPCJavaClientTest { public class StandaloneCordaRPCJavaClientTest {
@ -32,6 +38,7 @@ public class StandaloneCordaRPCJavaClientTest {
private AtomicInteger port = new AtomicInteger(15000); private AtomicInteger port = new AtomicInteger(15000);
private NodeProcess.Factory factory;
private NodeProcess notary; private NodeProcess notary;
private CordaRPCOps rpcProxy; private CordaRPCOps rpcProxy;
private CordaRPCConnection connection; private CordaRPCConnection connection;
@ -49,7 +56,9 @@ public class StandaloneCordaRPCJavaClientTest {
@Before @Before
public void setUp() { public void setUp() {
notary = new NodeProcess.Factory().create(notaryConfig); factory = new NodeProcess.Factory();
copyFinanceCordapp();
notary = factory.create(notaryConfig);
connection = notary.connect(); connection = notary.connect();
rpcProxy = connection.getProxy(); rpcProxy = connection.getProxy();
notaryNode = fetchNotaryIdentity(); notaryNode = fetchNotaryIdentity();
@ -64,6 +73,28 @@ public class StandaloneCordaRPCJavaClientTest {
} }
} }
private void copyFinanceCordapp() {
Path pluginsDir = (factory.baseDirectory(notaryConfig).resolve("plugins"));
try {
Files.createDirectories(pluginsDir);
} catch (IOException ex) {
fail("Failed to create directories");
}
try (Stream<Path> paths = Files.walk(Paths.get("build", "resources", "smokeTest"))) {
paths.forEach(file -> {
if (file.toString().contains("corda-finance")) {
try {
Files.copy(file, pluginsDir.resolve(file.getFileName()));
} catch (IOException ex) {
fail("Failed to copy finance jar");
}
}
});
} catch (IOException e) {
fail("Failed to walk files");
}
}
private NodeInfo fetchNotaryIdentity() { private NodeInfo fetchNotaryIdentity() {
List<NodeInfo> nodeDataSnapshot = rpcProxy.networkMapSnapshot(); List<NodeInfo> nodeDataSnapshot = rpcProxy.networkMapSnapshot();
return nodeDataSnapshot.get(0); return nodeDataSnapshot.get(0);

View File

@ -7,7 +7,7 @@ import net.corda.contracts.asset.Cash
import net.corda.contracts.getCashBalance import net.corda.contracts.getCashBalance
import net.corda.contracts.getCashBalances import net.corda.contracts.getCashBalances
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.internal.InputStreamAndHash import net.corda.core.internal.*
import net.corda.core.messaging.* import net.corda.core.messaging.*
import net.corda.core.node.NodeInfo import net.corda.core.node.NodeInfo
import net.corda.core.node.services.Vault import net.corda.core.node.services.Vault
@ -32,8 +32,10 @@ import org.junit.Before
import org.junit.Test import org.junit.Test
import java.io.FilterInputStream import java.io.FilterInputStream
import java.io.InputStream import java.io.InputStream
import java.nio.file.Paths
import java.util.* import java.util.*
import java.util.concurrent.atomic.AtomicInteger import java.util.concurrent.atomic.AtomicInteger
import kotlin.streams.toList
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertFalse import kotlin.test.assertFalse
import kotlin.test.assertNotEquals import kotlin.test.assertNotEquals
@ -43,11 +45,12 @@ class StandaloneCordaRPClientTest {
private companion object { private companion object {
val log = loggerFor<StandaloneCordaRPClientTest>() val log = loggerFor<StandaloneCordaRPClientTest>()
val user = User("user1", "test", permissions = setOf("ALL")) val user = User("user1", "test", permissions = setOf("ALL"))
val port = AtomicInteger(15000) val port = AtomicInteger(15200)
const val attachmentSize = 2116 const val attachmentSize = 2116
val timeout = 60.seconds val timeout = 60.seconds
} }
private lateinit var factory: NodeProcess.Factory
private lateinit var notary: NodeProcess private lateinit var notary: NodeProcess
private lateinit var rpcProxy: CordaRPCOps private lateinit var rpcProxy: CordaRPCOps
private lateinit var connection: CordaRPCConnection private lateinit var connection: CordaRPCConnection
@ -64,7 +67,9 @@ class StandaloneCordaRPClientTest {
@Before @Before
fun setUp() { fun setUp() {
notary = NodeProcess.Factory().create(notaryConfig) factory = NodeProcess.Factory()
copyFinanceCordapp()
notary = factory.create(notaryConfig)
connection = notary.connect() connection = notary.connect()
rpcProxy = connection.proxy rpcProxy = connection.proxy
notaryNode = fetchNotaryIdentity() notaryNode = fetchNotaryIdentity()
@ -79,6 +84,15 @@ class StandaloneCordaRPClientTest {
} }
} }
private fun copyFinanceCordapp() {
val pluginsDir = (factory.baseDirectory(notaryConfig) / "plugins").createDirectories()
// Find the finance jar file for the smoke tests of this module
val financeJar = Paths.get("build", "resources", "smokeTest").list {
it.filter { "corda-finance" in it.toString() }.toList().single()
}
financeJar.copyToDirectory(pluginsDir)
}
@Test @Test
fun `test attachments`() { fun `test attachments`() {
val attachment = InputStreamAndHash.createInMemoryTestZip(attachmentSize, 1) val attachment = InputStreamAndHash.createInMemoryTestZip(attachmentSize, 1)
@ -189,6 +203,7 @@ class StandaloneCordaRPClientTest {
@Test @Test
fun `test cash balances`() { fun `test cash balances`() {
val startCash = rpcProxy.getCashBalances() val startCash = rpcProxy.getCashBalances()
println(startCash)
assertTrue(startCash.isEmpty(), "Should not start with any cash") assertTrue(startCash.isEmpty(), "Should not start with any cash")
val flowHandle = rpcProxy.startFlow(::CashIssueFlow, 629.DOLLARS, OpaqueBytes.of(0), notaryNode.legalIdentity) val flowHandle = rpcProxy.startFlow(::CashIssueFlow, 629.DOLLARS, OpaqueBytes.of(0), notaryNode.legalIdentity)

View File

@ -107,6 +107,70 @@ data class Amount<T : Any>(val quantity: Long, val displayTokenSize: BigDecimal,
*/ */
@JvmStatic @JvmStatic
fun <T : Any> Iterable<Amount<T>>.sumOrZero(token: T) = if (iterator().hasNext()) sumOrThrow() else Amount.zero(token) fun <T : Any> Iterable<Amount<T>>.sumOrZero(token: T) = if (iterator().hasNext()) sumOrThrow() else Amount.zero(token)
private val currencySymbols: Map<String, Currency> = mapOf(
"$" to Currency.getInstance("USD"),
"£" to Currency.getInstance("GBP"),
"" to Currency.getInstance("EUR"),
"¥" to Currency.getInstance("JPY"),
"" to Currency.getInstance("RUB")
)
private val currencyCodes: Map<String, Currency> by lazy {
Currency.getAvailableCurrencies().associateBy { it.currencyCode }
}
/**
* Returns an amount that is equal to the given currency amount in text. Examples of what is supported:
*
* - 12 USD
* - 14.50 USD
* - 10 USD
* - 30 CHF
* - $10.24
* - £13
* - €5000
*
* Note this method does NOT respect internationalisation rules: it ignores commas and uses . as the
* decimal point separator, always. It also ignores the users locale:
*
* - $ is always USD,
* - £ is always GBP
* - € is always the Euro
* - ¥ is always Japanese Yen.
* - ₽ is always the Russian ruble.
*
* Thus an input of $12 expecting some other countries dollar will not work. Do your own parsing if
* you need correct handling of currency amounts with locale-sensitive handling.
*
* @throws IllegalArgumentException if the input string was not understood.
*/
@JvmStatic
fun parseCurrency(input: String): Amount<Currency> {
val i = input.filter { it != ',' }
try {
// First check the symbols at the front.
for ((symbol, currency) in currencySymbols) {
if (i.startsWith(symbol)) {
val rest = i.substring(symbol.length)
return Amount.fromDecimal(BigDecimal(rest), currency)
}
}
// Now check the codes at the end.
val split = i.split(' ')
if (split.size == 2) {
val (rest, code) = split
for ((cc, currency) in currencyCodes) {
if (cc == code) {
return Amount.fromDecimal(BigDecimal(rest), currency)
}
}
}
} catch(e: Exception) {
throw IllegalArgumentException("Could not parse $input as a currency", e)
}
throw IllegalArgumentException("Did not recognise the currency in $input or could not parse")
}
} }
init { init {

View File

@ -51,6 +51,11 @@ UNRELEASED
no longer auto-suggests these extension functions in completion unless you add import lines for them yourself no longer auto-suggests these extension functions in completion unless you add import lines for them yourself
(this is Kotlin IDE bug KT-15286). (this is Kotlin IDE bug KT-15286).
* ``:finance`` module now acting as a CorDapp with regard to flow registration, schemas and serializable types.
* ``WebServerPluginRegistry`` now has a ``customizeJSONSerialization`` which can be overridden to extend the REST JSON
serializers. In particular the IRS demos must now register the ``BusinessCalendar`` serializers.
Milestone 14 Milestone 14
------------ ------------

View File

@ -92,6 +92,8 @@ The ``WebServerPluginRegistry`` class defines the following:
be distributed within the CorDapp jars. This static content will not be available if the bundled web server is not be distributed within the CorDapp jars. This static content will not be available if the bundled web server is not
started started
* ``customizeJSONSerialization``, which can be overridden to register custom JSON serializers if required by the REST api.
* The static web content itself should be placed inside the ``src/main/resources`` directory * The static web content itself should be placed inside the ``src/main/resources`` directory
To learn about how to use gradle to build your cordapp against Corda and generate an artifact please read To learn about how to use gradle to build your cordapp against Corda and generate an artifact please read

View File

@ -5,13 +5,16 @@ apply plugin: 'kotlin-jpa'
apply plugin: CanonicalizerPlugin apply plugin: CanonicalizerPlugin
apply plugin: 'net.corda.plugins.publish-utils' apply plugin: 'net.corda.plugins.publish-utils'
apply plugin: 'net.corda.plugins.quasar-utils' apply plugin: 'net.corda.plugins.quasar-utils'
apply plugin: 'net.corda.plugins.cordformation'
apply plugin: 'com.jfrog.artifactory' apply plugin: 'com.jfrog.artifactory'
description 'Corda finance modules' description 'Corda finance modules'
dependencies { dependencies {
compile project(':core') // Note the :finance module is a CorDapp in its own right
compile project(':node-schemas') // and CorDapps using :finance features should use 'cordapp' not 'compile' linkage.
cordaCompile project(':core')
cordaCompile project(':node-schemas')
testCompile project(':test-utils') testCompile project(':test-utils')
testCompile project(path: ':core', configuration: 'testArtifacts') testCompile project(path: ':core', configuration: 'testArtifacts')

View File

@ -31,66 +31,3 @@ infix fun Currency.`issued by`(deposit: PartyAndReference) = issuedBy(deposit)
infix fun Amount<Currency>.`issued by`(deposit: PartyAndReference) = issuedBy(deposit) infix fun Amount<Currency>.`issued by`(deposit: PartyAndReference) = issuedBy(deposit)
infix fun Currency.issuedBy(deposit: PartyAndReference) = Issued(deposit, this) infix fun Currency.issuedBy(deposit: PartyAndReference) = Issued(deposit, this)
infix fun Amount<Currency>.issuedBy(deposit: PartyAndReference) = Amount(quantity, displayTokenSize, token.issuedBy(deposit)) infix fun Amount<Currency>.issuedBy(deposit: PartyAndReference) = Amount(quantity, displayTokenSize, token.issuedBy(deposit))
private val currencySymbols: Map<String, Currency> = mapOf(
"$" to USD,
"£" to GBP,
"" to EUR,
"¥" to JPY,
"" to RUB
)
private val currencyCodes: Map<String, Currency> by lazy {
Currency.getAvailableCurrencies().associateBy { it.currencyCode }
}
/**
* Returns an amount that is equal to the given currency amount in text. Examples of what is supported:
*
* - 12 USD
* - 14.50 USD
* - 10 USD
* - 30 CHF
* - $10.24
* - £13
* - €5000
*
* Note this method does NOT respect internationalisation rules: it ignores commas and uses . as the
* decimal point separator, always. It also ignores the users locale:
*
* - $ is always USD,
* - £ is always GBP
* - € is always the Euro
* - ¥ is always Japanese Yen.
* - ₽ is always the Russian ruble.
*
* Thus an input of $12 expecting some other countries dollar will not work. Do your own parsing if
* you need correct handling of currency amounts with locale-sensitive handling.
*
* @throws IllegalArgumentException if the input string was not understood.
*/
fun parseCurrency(input: String): Amount<Currency> {
val i = input.filter { it != ',' }
try {
// First check the symbols at the front.
for ((symbol, currency) in currencySymbols) {
if (i.startsWith(symbol)) {
val rest = i.substring(symbol.length)
return Amount.fromDecimal(BigDecimal(rest), currency)
}
}
// Now check the codes at the end.
val split = i.split(' ')
if (split.size == 2) {
val (rest, code) = split
for ((cc, currency) in currencyCodes) {
if (cc == code) {
return Amount.fromDecimal(BigDecimal(rest), currency)
}
}
}
} catch(e: Exception) {
throw IllegalArgumentException("Could not parse $input as a currency", e)
}
throw IllegalArgumentException("Did not recognise the currency in $input or could not parse")
}

View File

@ -0,0 +1,50 @@
@file:JvmName("FinanceJSONSupport")
package net.corda.plugin
import com.fasterxml.jackson.core.JsonGenerator
import com.fasterxml.jackson.core.JsonParseException
import com.fasterxml.jackson.core.JsonParser
import com.fasterxml.jackson.databind.*
import com.fasterxml.jackson.databind.deser.std.StringArrayDeserializer
import com.fasterxml.jackson.databind.module.SimpleModule
import net.corda.contracts.BusinessCalendar
import java.time.LocalDate
fun registerFinanceJSONMappers(objectMapper: ObjectMapper): Unit {
val financeModule = SimpleModule("finance").apply {
addSerializer(BusinessCalendar::class.java, CalendarSerializer)
addDeserializer(BusinessCalendar::class.java, CalendarDeserializer)
}
objectMapper.registerModule(financeModule)
}
data class BusinessCalendarWrapper(val holidayDates: List<LocalDate>) {
fun toCalendar() = BusinessCalendar(holidayDates)
}
object CalendarSerializer : JsonSerializer<BusinessCalendar>() {
override fun serialize(obj: BusinessCalendar, generator: JsonGenerator, context: SerializerProvider) {
val calendarName = BusinessCalendar.calendars.find { BusinessCalendar.getInstance(it) == obj }
if (calendarName != null) {
generator.writeString(calendarName)
} else {
generator.writeObject(BusinessCalendarWrapper(obj.holidayDates))
}
}
}
object CalendarDeserializer : JsonDeserializer<BusinessCalendar>() {
override fun deserialize(parser: JsonParser, context: DeserializationContext): BusinessCalendar {
return try {
try {
val array = StringArrayDeserializer.instance.deserialize(parser, context)
BusinessCalendar.getInstance(*array)
} catch (e: Exception) {
parser.readValueAs(BusinessCalendarWrapper::class.java).toCalendar()
}
} catch (e: Exception) {
throw JsonParseException(parser, "Invalid calendar(s) ${parser.text}: ${e.message}")
}
}
}

View File

@ -0,0 +1,11 @@
package net.corda.plugin
import net.corda.core.node.CordaPluginRegistry
import net.corda.core.schemas.MappedSchema
import net.corda.schemas.CashSchemaV1
class FinancePluginRegistry : CordaPluginRegistry() {
override val requiredSchemas: Set<MappedSchema> = setOf(
CashSchemaV1
)
}

View File

@ -0,0 +1 @@
net.corda.plugin.FinancePluginRegistry

View File

@ -14,17 +14,17 @@ class CurrencyUtilsTest {
@Test @Test
fun parseCurrency() { fun parseCurrency() {
assertEquals(Amount(1234L, GBP), parseCurrency("£12.34")) assertEquals(Amount(1234L, GBP), Amount.parseCurrency("£12.34"))
assertEquals(Amount(1200L, GBP), parseCurrency("£12")) assertEquals(Amount(1200L, GBP), Amount.parseCurrency("£12"))
assertEquals(Amount(1000L, USD), parseCurrency("$10")) assertEquals(Amount(1000L, USD), Amount.parseCurrency("$10"))
assertEquals(Amount(5000L, JPY), parseCurrency("¥5000")) assertEquals(Amount(5000L, JPY), Amount.parseCurrency("¥5000"))
assertEquals(Amount(500000L, RUB), parseCurrency("₽5000")) assertEquals(Amount(500000L, RUB), Amount.parseCurrency("₽5000"))
assertEquals(Amount(1500000000L, CHF), parseCurrency("15,000,000 CHF")) assertEquals(Amount(1500000000L, CHF), Amount.parseCurrency("15,000,000 CHF"))
} }
@Test @Test
fun rendering() { fun rendering() {
assertEquals("5000 JPY", parseCurrency("¥5000").toString()) assertEquals("5000 JPY", Amount.parseCurrency("¥5000").toString())
assertEquals("50.12 USD", parseCurrency("$50.12").toString()) assertEquals("50.12 USD", Amount.parseCurrency("$50.12").toString())
} }
} }

View File

@ -27,9 +27,6 @@ import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.NetworkHostAndPort
import net.corda.core.utilities.debug import net.corda.core.utilities.debug
import net.corda.core.utilities.toNonEmptySet import net.corda.core.utilities.toNonEmptySet
import net.corda.flows.CashExitFlow
import net.corda.flows.CashIssueFlow
import net.corda.flows.CashPaymentFlow
import net.corda.node.services.ContractUpgradeHandler import net.corda.node.services.ContractUpgradeHandler
import net.corda.node.services.NotaryChangeHandler import net.corda.node.services.NotaryChangeHandler
import net.corda.node.services.NotifyTransactionHandler import net.corda.node.services.NotifyTransactionHandler
@ -383,11 +380,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
.filter { it.isUserInvokable() } + .filter { it.isUserInvokable() } +
// Add any core flows here // Add any core flows here
listOf( listOf(
ContractUpgradeFlow::class.java, ContractUpgradeFlow::class.java)
// TODO Remove all Cash flows from default list once they are split into separate CorDapp.
CashIssueFlow::class.java,
CashExitFlow::class.java,
CashPaymentFlow::class.java)
} }
/** /**

View File

@ -16,7 +16,6 @@ import net.corda.node.services.persistence.DBTransactionMappingStorage
import net.corda.node.services.persistence.DBTransactionStorage import net.corda.node.services.persistence.DBTransactionStorage
import net.corda.node.services.transactions.PersistentUniquenessProvider import net.corda.node.services.transactions.PersistentUniquenessProvider
import net.corda.node.services.vault.VaultSchemaV1 import net.corda.node.services.vault.VaultSchemaV1
import net.corda.schemas.CashSchemaV1
/** /**
* Most basic implementation of [SchemaService]. * Most basic implementation of [SchemaService].
@ -43,8 +42,7 @@ class NodeSchemaService(customSchemas: Set<MappedSchema> = emptySet()) : SchemaS
// Required schemas are those used by internal Corda services // Required schemas are those used by internal Corda services
// For example, cash is used by the vault for coin selection (but will be extracted as a standalone CorDapp in future) // For example, cash is used by the vault for coin selection (but will be extracted as a standalone CorDapp in future)
val requiredSchemas: Map<MappedSchema, SchemaService.SchemaOptions> = val requiredSchemas: Map<MappedSchema, SchemaService.SchemaOptions> =
mapOf(Pair(CashSchemaV1, SchemaService.SchemaOptions()), mapOf(Pair(CommonSchemaV1, SchemaService.SchemaOptions()),
Pair(CommonSchemaV1, SchemaService.SchemaOptions()),
Pair(VaultSchemaV1, SchemaService.SchemaOptions()), Pair(VaultSchemaV1, SchemaService.SchemaOptions()),
Pair(NodeServicesV1, SchemaService.SchemaOptions())) Pair(NodeServicesV1, SchemaService.SchemaOptions()))

View File

@ -3,16 +3,21 @@ package net.corda.node.services.vault;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import kotlin.Pair; import kotlin.Pair;
import net.corda.contracts.DealState; import net.corda.contracts.DealState;
import net.corda.contracts.asset.*; import net.corda.contracts.asset.Cash;
import net.corda.contracts.asset.CashUtilities;
import net.corda.core.contracts.*; import net.corda.core.contracts.*;
import net.corda.core.crypto.EncodingUtils; import net.corda.core.crypto.EncodingUtils;
import net.corda.core.identity.AbstractParty; import net.corda.core.identity.AbstractParty;
import net.corda.core.messaging.DataFeed; import net.corda.core.messaging.DataFeed;
import net.corda.core.node.services.*; import net.corda.core.node.services.IdentityService;
import net.corda.core.node.services.Vault;
import net.corda.core.node.services.VaultQueryException;
import net.corda.core.node.services.VaultQueryService;
import net.corda.core.node.services.vault.*; import net.corda.core.node.services.vault.*;
import net.corda.core.node.services.vault.QueryCriteria.LinearStateQueryCriteria; import net.corda.core.node.services.vault.QueryCriteria.LinearStateQueryCriteria;
import net.corda.core.node.services.vault.QueryCriteria.VaultCustomQueryCriteria; import net.corda.core.node.services.vault.QueryCriteria.VaultCustomQueryCriteria;
import net.corda.core.node.services.vault.QueryCriteria.VaultQueryCriteria; import net.corda.core.node.services.vault.QueryCriteria.VaultQueryCriteria;
import net.corda.core.schemas.MappedSchema;
import net.corda.core.utilities.OpaqueBytes; import net.corda.core.utilities.OpaqueBytes;
import net.corda.node.utilities.CordaPersistence; import net.corda.node.utilities.CordaPersistence;
import net.corda.schemas.CashSchemaV1; import net.corda.schemas.CashSchemaV1;
@ -34,12 +39,14 @@ import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import java.util.stream.StreamSupport; import java.util.stream.StreamSupport;
import static net.corda.contracts.asset.CashUtilities.getDUMMY_CASH_ISSUER;
import static net.corda.contracts.asset.CashUtilities.getDUMMY_CASH_ISSUER_KEY;
import static net.corda.core.node.services.vault.QueryCriteriaUtils.DEFAULT_PAGE_NUM; import static net.corda.core.node.services.vault.QueryCriteriaUtils.DEFAULT_PAGE_NUM;
import static net.corda.core.node.services.vault.QueryCriteriaUtils.MAX_PAGE_SIZE; import static net.corda.core.node.services.vault.QueryCriteriaUtils.MAX_PAGE_SIZE;
import static net.corda.core.utilities.ByteArrays.toHexString; import static net.corda.core.utilities.ByteArrays.toHexString;
import static net.corda.testing.CoreTestUtils.*; import static net.corda.testing.CoreTestUtils.*;
import static net.corda.testing.TestConstants.*; import static net.corda.testing.TestConstants.getDUMMY_NOTARY;
import static net.corda.contracts.asset.CashUtilities.*; import static net.corda.testing.TestConstants.getDUMMY_NOTARY_KEY;
import static net.corda.testing.node.MockServicesKt.makeTestDatabaseAndMockServices; import static net.corda.testing.node.MockServicesKt.makeTestDatabaseAndMockServices;
import static net.corda.testing.node.MockServicesKt.makeTestIdentityService; import static net.corda.testing.node.MockServicesKt.makeTestIdentityService;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
@ -56,8 +63,10 @@ public class VaultQueryJavaTests extends TestDependencyInjectionBase {
ArrayList<KeyPair> keys = new ArrayList<>(); ArrayList<KeyPair> keys = new ArrayList<>();
keys.add(getMEGA_CORP_KEY()); keys.add(getMEGA_CORP_KEY());
keys.add(getDUMMY_NOTARY_KEY()); keys.add(getDUMMY_NOTARY_KEY());
Set<MappedSchema> requiredSchemas = new HashSet<>();
requiredSchemas.add(CashSchemaV1.INSTANCE);
IdentityService identitySvc = makeTestIdentityService(); IdentityService identitySvc = makeTestIdentityService();
Pair<CordaPersistence, MockServices> databaseAndServices = makeTestDatabaseAndMockServices(Collections.EMPTY_SET, keys, () -> identitySvc); Pair<CordaPersistence, MockServices> databaseAndServices = makeTestDatabaseAndMockServices(requiredSchemas, keys, () -> identitySvc);
issuerServices = new MockServices(getDUMMY_CASH_ISSUER_KEY(), getBOC_KEY()); issuerServices = new MockServices(getDUMMY_CASH_ISSUER_KEY(), getBOC_KEY());
database = databaseAndServices.getFirst(); database = databaseAndServices.getFirst();
services = databaseAndServices.getSecond(); services = databaseAndServices.getSecond();

View File

@ -24,13 +24,15 @@ configurations {
dependencies { dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib-jre8:$kotlin_version" compile "org.jetbrains.kotlin:kotlin-stdlib-jre8:$kotlin_version"
// The bank of corda CorDapp depends upon Cash CorDapp features
cordapp project(':finance')
// Corda integration dependencies // Corda integration dependencies
cordaCompile project(path: ":node:capsule", configuration: 'runtimeArtifacts') cordaCompile project(path: ":node:capsule", configuration: 'runtimeArtifacts')
cordaCompile project(path: ":webserver:webcapsule", configuration: 'runtimeArtifacts') cordaCompile project(path: ":webserver:webcapsule", configuration: 'runtimeArtifacts')
cordaCompile project(':core') cordaCompile project(':core')
cordaCompile project(':client:jfx') cordaCompile project(':client:jfx')
cordaCompile project(':client:rpc') cordaCompile project(':client:rpc')
cordaCompile project(':finance')
cordaCompile project(':webserver') cordaCompile project(':webserver')
cordaCompile project(':test-utils') cordaCompile project(':test-utils')
@ -54,7 +56,7 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
advertisedServices = ["corda.notary.validating"] advertisedServices = ["corda.notary.validating"]
p2pPort 10002 p2pPort 10002
rpcPort 10003 rpcPort 10003
cordapps = [] cordapps = ["net.corda:finance:$corda_release_version"]
} }
node { node {
name "CN=BankOfCorda,O=R3,L=New York,C=US" name "CN=BankOfCorda,O=R3,L=New York,C=US"
@ -62,7 +64,7 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
p2pPort 10005 p2pPort 10005
rpcPort 10006 rpcPort 10006
webPort 10007 webPort 10007
cordapps = [] cordapps = ["net.corda:finance:$corda_release_version"]
rpcUsers = [ rpcUsers = [
['username' : "bankUser", ['username' : "bankUser",
'password' : "test", 'password' : "test",
@ -77,7 +79,7 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
p2pPort 10008 p2pPort 10008
rpcPort 10009 rpcPort 10009
webPort 10010 webPort 10010
cordapps = [] cordapps = ["net.corda:finance:$corda_release_version"]
rpcUsers = [ rpcUsers = [
['username' : "bigCorpUser", ['username' : "bigCorpUser",
'password' : "test", 'password' : "test",

View File

@ -27,11 +27,13 @@ configurations {
dependencies { dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib-jre8:$kotlin_version" compile "org.jetbrains.kotlin:kotlin-stdlib-jre8:$kotlin_version"
// The irs demo CorDapp depends upon Cash CorDapp features
cordapp project(':finance')
// Corda integration dependencies // Corda integration dependencies
cordaCompile project(path: ":node:capsule", configuration: 'runtimeArtifacts') cordaCompile project(path: ":node:capsule", configuration: 'runtimeArtifacts')
cordaCompile project(path: ":webserver:webcapsule", configuration: 'runtimeArtifacts') cordaCompile project(path: ":webserver:webcapsule", configuration: 'runtimeArtifacts')
cordaCompile project(':core') cordaCompile project(':core')
cordaCompile project(':finance')
cordaCompile project(':webserver') cordaCompile project(':webserver')
// Javax is required for webapis // Javax is required for webapis
@ -55,7 +57,7 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
p2pPort 10002 p2pPort 10002
rpcPort 10003 rpcPort 10003
webPort 10004 webPort 10004
cordapps = [] cordapps = ["net.corda:finance:$corda_release_version"]
useTestClock true useTestClock true
} }
node { node {
@ -64,7 +66,7 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
p2pPort 10005 p2pPort 10005
rpcPort 10006 rpcPort 10006
webPort 10007 webPort 10007
cordapps = [] cordapps = ["net.corda:finance:$corda_release_version"]
useTestClock true useTestClock true
} }
node { node {
@ -73,7 +75,7 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
p2pPort 10008 p2pPort 10008
rpcPort 10009 rpcPort 10009
webPort 10010 webPort 10010
cordapps = [] cordapps = ["net.corda:finance:$corda_release_version"]
useTestClock true useTestClock true
} }
} }

View File

@ -1,10 +1,10 @@
package net.corda.irs package net.corda.irs
import net.corda.client.rpc.CordaRPCClient import net.corda.client.rpc.CordaRPCClient
import net.corda.core.internal.concurrent.transpose
import net.corda.core.messaging.vaultTrackBy import net.corda.core.messaging.vaultTrackBy
import net.corda.core.node.services.ServiceInfo import net.corda.core.node.services.ServiceInfo
import net.corda.core.toFuture import net.corda.core.toFuture
import net.corda.core.internal.concurrent.transpose
import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.NetworkHostAndPort
import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.getOrThrow
import net.corda.core.utilities.loggerFor import net.corda.core.utilities.loggerFor
@ -15,6 +15,7 @@ import net.corda.irs.utilities.uploadFile
import net.corda.node.services.config.FullNodeConfiguration import net.corda.node.services.config.FullNodeConfiguration
import net.corda.node.services.transactions.SimpleNotaryService import net.corda.node.services.transactions.SimpleNotaryService
import net.corda.nodeapi.User import net.corda.nodeapi.User
import net.corda.plugin.registerFinanceJSONMappers
import net.corda.testing.DUMMY_BANK_A import net.corda.testing.DUMMY_BANK_A
import net.corda.testing.DUMMY_BANK_B import net.corda.testing.DUMMY_BANK_B
import net.corda.testing.DUMMY_NOTARY import net.corda.testing.DUMMY_NOTARY
@ -61,6 +62,7 @@ class IRSDemoTest : IntegrationTestCategory {
val (_, nodeAApi, nodeBApi) = listOf(controller, nodeA, nodeB).zip(listOf(controllerAddr, nodeAAddr, nodeBAddr)).map { val (_, nodeAApi, nodeBApi) = listOf(controller, nodeA, nodeB).zip(listOf(controllerAddr, nodeAAddr, nodeBAddr)).map {
val mapper = net.corda.jackson.JacksonSupport.createDefaultMapper(it.first.rpc) val mapper = net.corda.jackson.JacksonSupport.createDefaultMapper(it.first.rpc)
registerFinanceJSONMappers(mapper)
HttpApi.fromHostAndPort(it.second, "api/irs", mapper = mapper) HttpApi.fromHostAndPort(it.second, "api/irs", mapper = mapper)
} }
val nextFixingDates = getFixingDateObservable(nodeA.configuration) val nextFixingDates = getFixingDateObservable(nodeA.configuration)

View File

@ -1,6 +1,8 @@
package net.corda.irs.plugin package net.corda.irs.plugin
import com.fasterxml.jackson.databind.ObjectMapper
import net.corda.irs.api.InterestRateSwapAPI import net.corda.irs.api.InterestRateSwapAPI
import net.corda.plugin.registerFinanceJSONMappers
import net.corda.webserver.services.WebServerPluginRegistry import net.corda.webserver.services.WebServerPluginRegistry
import java.util.function.Function import java.util.function.Function
@ -9,4 +11,6 @@ class IRSPlugin : WebServerPluginRegistry {
override val staticServeDirs: Map<String, String> = mapOf( override val staticServeDirs: Map<String, String> = mapOf(
"irsdemo" to javaClass.classLoader.getResource("irsweb").toExternalForm() "irsdemo" to javaClass.classLoader.getResource("irsweb").toExternalForm()
) )
override fun customizeJSONSerialization(om: ObjectMapper): Unit = registerFinanceJSONMappers(om)
} }

View File

@ -9,8 +9,8 @@ import net.corda.core.flows.FlowLogic
import net.corda.core.flows.InitiatedBy import net.corda.core.flows.InitiatedBy
import net.corda.core.flows.InitiatingFlow import net.corda.core.flows.InitiatingFlow
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.internal.concurrent.*
import net.corda.core.internal.FlowStateMachine import net.corda.core.internal.FlowStateMachine
import net.corda.core.internal.concurrent.*
import net.corda.core.node.services.queryBy import net.corda.core.node.services.queryBy
import net.corda.core.toFuture import net.corda.core.toFuture
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
@ -21,6 +21,7 @@ import net.corda.irs.contract.InterestRateSwap
import net.corda.irs.flows.FixingFlow import net.corda.irs.flows.FixingFlow
import net.corda.jackson.JacksonSupport import net.corda.jackson.JacksonSupport
import net.corda.node.services.identity.InMemoryIdentityService import net.corda.node.services.identity.InMemoryIdentityService
import net.corda.plugin.registerFinanceJSONMappers
import net.corda.testing.DUMMY_CA import net.corda.testing.DUMMY_CA
import net.corda.testing.node.InMemoryMessagingNetwork import net.corda.testing.node.InMemoryMessagingNetwork
import rx.Observable import rx.Observable
@ -44,6 +45,7 @@ class IRSSimulation(networkSendManuallyPumped: Boolean, runAsync: Boolean, laten
override fun startMainSimulation(): CordaFuture<Unit> { override fun startMainSimulation(): CordaFuture<Unit> {
val future = openFuture<Unit>() val future = openFuture<Unit>()
om = JacksonSupport.createInMemoryMapper(InMemoryIdentityService((banks + regulators + networkMap).map { it.info.legalIdentityAndCert }, trustRoot = DUMMY_CA.certificate)) om = JacksonSupport.createInMemoryMapper(InMemoryIdentityService((banks + regulators + networkMap).map { it.info.legalIdentityAndCert }, trustRoot = DUMMY_CA.certificate))
registerFinanceJSONMappers(om)
startIRSDealBetween(0, 1).thenMatch({ startIRSDealBetween(0, 1).thenMatch({
// Next iteration is a pause. // Next iteration is a pause.

View File

@ -30,11 +30,13 @@ configurations {
dependencies { dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib-jre8:$kotlin_version" compile "org.jetbrains.kotlin:kotlin-stdlib-jre8:$kotlin_version"
// The SIMM demo CorDapp depends upon Cash CorDapp features
cordapp project(':finance')
// Corda integration dependencies // Corda integration dependencies
cordaCompile project(path: ":node:capsule", configuration: 'runtimeArtifacts') cordaCompile project(path: ":node:capsule", configuration: 'runtimeArtifacts')
cordaCompile project(path: ":webserver:webcapsule", configuration: 'runtimeArtifacts') cordaCompile project(path: ":webserver:webcapsule", configuration: 'runtimeArtifacts')
cordaCompile project(':core') cordaCompile project(':core')
cordaCompile project(':finance')
cordaCompile project(':webserver') cordaCompile project(':webserver')
// Javax is required for webapis // Javax is required for webapis
@ -66,28 +68,28 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
name "CN=Notary Service,O=R3,OU=corda,L=Zurich,C=CH" name "CN=Notary Service,O=R3,OU=corda,L=Zurich,C=CH"
advertisedServices = ["corda.notary.validating"] advertisedServices = ["corda.notary.validating"]
p2pPort 10002 p2pPort 10002
cordapps = [] cordapps = ["net.corda:finance:$corda_release_version"]
} }
node { node {
name "CN=Bank A,O=Bank A,L=London,C=GB" name "CN=Bank A,O=Bank A,L=London,C=GB"
advertisedServices = [] advertisedServices = []
p2pPort 10004 p2pPort 10004
webPort 10005 webPort 10005
cordapps = [] cordapps = ["net.corda:finance:$corda_release_version"]
} }
node { node {
name "CN=Bank B,O=Bank B,L=New York,C=US" name "CN=Bank B,O=Bank B,L=New York,C=US"
advertisedServices = [] advertisedServices = []
p2pPort 10006 p2pPort 10006
webPort 10007 webPort 10007
cordapps = [] cordapps = ["net.corda:finance:$corda_release_version"]
} }
node { node {
name "CN=Bank C,O=Bank C,L=Tokyo,C=Japan" name "CN=Bank C,O=Bank C,L=Tokyo,C=Japan"
advertisedServices = [] advertisedServices = []
p2pPort 10008 p2pPort 10008
webPort 10009 webPort 10009
cordapps = [] cordapps = ["net.corda:finance:$corda_release_version"]
} }
} }

View File

@ -1,6 +1,8 @@
package net.corda.vega.plugin package net.corda.vega.plugin
import com.fasterxml.jackson.databind.ObjectMapper
import net.corda.core.node.CordaPluginRegistry import net.corda.core.node.CordaPluginRegistry
import net.corda.plugin.registerFinanceJSONMappers
import net.corda.vega.api.PortfolioApi import net.corda.vega.api.PortfolioApi
import net.corda.webserver.services.WebServerPluginRegistry import net.corda.webserver.services.WebServerPluginRegistry
import java.util.function.Function import java.util.function.Function
@ -14,4 +16,5 @@ import java.util.function.Function
class SimmPlugin : WebServerPluginRegistry { class SimmPlugin : WebServerPluginRegistry {
override val webApis = listOf(Function(::PortfolioApi)) override val webApis = listOf(Function(::PortfolioApi))
override val staticServeDirs: Map<String, String> = mapOf("simmvaluationdemo" to javaClass.classLoader.getResource("simmvaluationweb").toExternalForm()) override val staticServeDirs: Map<String, String> = mapOf("simmvaluationdemo" to javaClass.classLoader.getResource("simmvaluationweb").toExternalForm())
override fun customizeJSONSerialization(om: ObjectMapper): Unit = registerFinanceJSONMappers(om)
} }

View File

@ -24,11 +24,13 @@ configurations {
dependencies { dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib-jre8:$kotlin_version" compile "org.jetbrains.kotlin:kotlin-stdlib-jre8:$kotlin_version"
// The trader demo CorDapp depends upon Cash CorDapp features
cordapp project(':finance')
// Corda integration dependencies // Corda integration dependencies
cordaCompile project(path: ":node:capsule", configuration: 'runtimeArtifacts') cordaCompile project(path: ":node:capsule", configuration: 'runtimeArtifacts')
cordaCompile project(path: ":webserver:webcapsule", configuration: 'runtimeArtifacts') cordaCompile project(path: ":webserver:webcapsule", configuration: 'runtimeArtifacts')
cordaCompile project(':core') cordaCompile project(':core')
cordaCompile project(':finance')
// Corda Plugins: dependent flows and services // Corda Plugins: dependent flows and services
cordapp project(':samples:bank-of-corda-demo') cordapp project(':samples:bank-of-corda-demo')
@ -53,14 +55,14 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
name "CN=Notary Service,O=R3,OU=corda,L=Zurich,C=CH" name "CN=Notary Service,O=R3,OU=corda,L=Zurich,C=CH"
advertisedServices = ["corda.notary.validating"] advertisedServices = ["corda.notary.validating"]
p2pPort 10002 p2pPort 10002
cordapps = [] cordapps = ["net.corda:finance:$corda_release_version"]
} }
node { node {
name "CN=Bank A,O=Bank A,L=London,C=GB" name "CN=Bank A,O=Bank A,L=London,C=GB"
advertisedServices = [] advertisedServices = []
p2pPort 10005 p2pPort 10005
rpcPort 10006 rpcPort 10006
cordapps = [] cordapps = ["net.corda:finance:$corda_release_version"]
rpcUsers = ext.rpcUsers rpcUsers = ext.rpcUsers
} }
node { node {
@ -68,7 +70,7 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
advertisedServices = [] advertisedServices = []
p2pPort 10008 p2pPort 10008
rpcPort 10009 rpcPort 10009
cordapps = [] cordapps = ["net.corda:finance:$corda_release_version"]
rpcUsers = ext.rpcUsers rpcUsers = ext.rpcUsers
} }
node { node {
@ -76,7 +78,7 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
advertisedServices = [] advertisedServices = []
p2pPort 10011 p2pPort 10011
rpcPort 10012 rpcPort 10012
cordapps = [] cordapps = ["net.corda:finance:$corda_release_version"]
rpcUsers = ext.rpcUsers rpcUsers = ext.rpcUsers
} }
} }

View File

@ -31,7 +31,6 @@ processTestResources {
dependencies { dependencies {
compile project(':core') compile project(':core')
compile project(':finance')
compile project(':client:rpc') compile project(':client:rpc')
compile project(':client:jackson') compile project(':client:jackson')

View File

@ -4,6 +4,7 @@ import com.google.common.html.HtmlEscapers.htmlEscaper
import net.corda.client.rpc.CordaRPCClient import net.corda.client.rpc.CordaRPCClient
import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.CordaRPCOps
import net.corda.core.utilities.loggerFor import net.corda.core.utilities.loggerFor
import net.corda.jackson.JacksonSupport
import net.corda.nodeapi.ArtemisMessagingComponent import net.corda.nodeapi.ArtemisMessagingComponent
import net.corda.webserver.WebServerConfig import net.corda.webserver.WebServerConfig
import net.corda.webserver.services.WebServerPluginRegistry import net.corda.webserver.services.WebServerPluginRegistry
@ -127,8 +128,13 @@ class NodeWebServer(val config: WebServerConfig) {
addServlet(DataUploadServlet::class.java, "/upload/*") addServlet(DataUploadServlet::class.java, "/upload/*")
addServlet(AttachmentDownloadServlet::class.java, "/attachments/*") addServlet(AttachmentDownloadServlet::class.java, "/attachments/*")
val rpcObjectMapper = pluginRegistries.fold(JacksonSupport.createDefaultMapper(localRpc)) { om, plugin ->
plugin.customizeJSONSerialization(om)
om
}
val resourceConfig = ResourceConfig() val resourceConfig = ResourceConfig()
.register(ObjectMapperConfig(localRpc)) .register(ObjectMapperConfig(rpcObjectMapper))
.register(ResponseFilter()) .register(ResponseFilter())
.register(APIServerImpl(localRpc)) .register(APIServerImpl(localRpc))

View File

@ -1,5 +1,6 @@
package net.corda.webserver.services package net.corda.webserver.services
import com.fasterxml.jackson.databind.ObjectMapper
import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.CordaRPCOps
import java.util.function.Function import java.util.function.Function
@ -21,4 +22,10 @@ interface WebServerPluginRegistry {
*/ */
val staticServeDirs: Map<String, String> get() = emptyMap() val staticServeDirs: Map<String, String> get() = emptyMap()
/**
* Optionally register extra JSON serializers to the default ObjectMapper provider
* @param om The [ObjectMapper] to register custom types against.
*/
fun customizeJSONSerialization(om: ObjectMapper): Unit {}
} }

View File

@ -1,8 +1,6 @@
package net.corda.webserver.servlets 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.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
@ -11,7 +9,6 @@ import javax.ws.rs.ext.Provider
* and to organise serializers / deserializers for java.time.* classes as necessary. * and to organise serializers / deserializers for java.time.* classes as necessary.
*/ */
@Provider @Provider
class ObjectMapperConfig(rpc: CordaRPCOps) : ContextResolver<ObjectMapper> { class ObjectMapperConfig(val defaultObjectMapper: ObjectMapper) : ContextResolver<ObjectMapper> {
val defaultObjectMapper = JacksonSupport.createDefaultMapper(rpc)
override fun getContext(type: Class<*>) = defaultObjectMapper override fun getContext(type: Class<*>) = defaultObjectMapper
} }