From 8ec33d67e4907d524894f4cb91abc9cb8965c25a Mon Sep 17 00:00:00 2001 From: Matthew Nesbit Date: Tue, 15 Aug 2017 09:14:15 +0100 Subject: [PATCH] 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. --- client/jackson/build.gradle | 1 - .../net/corda/jackson/JacksonSupport.kt | 38 +---------- .../net/corda/jackson/JacksonSupportTest.kt | 3 +- client/rpc/build.gradle | 3 + .../rpc/StandaloneCordaRPCJavaClientTest.java | 33 +++++++++- .../kotlin/rpc/StandaloneCordaRPClientTest.kt | 21 +++++- .../kotlin/net/corda/core/contracts/Amount.kt | 64 +++++++++++++++++++ docs/source/changelog.rst | 5 ++ docs/source/writing-cordapps.rst | 2 + finance/build.gradle | 7 +- .../kotlin/net/corda/finance/CurrencyUtils.kt | 63 ------------------ .../net/corda/plugin/FinanceJSONSupport.kt | 50 +++++++++++++++ .../net/corda/plugin/FinancePluginRegistry.kt | 11 ++++ .../net.corda.core.node.CordaPluginRegistry | 1 + .../net/corda/finance/CurrencyUtilsTest.kt | 16 ++--- .../net/corda/node/internal/AbstractNode.kt | 9 +-- .../node/services/schema/NodeSchemaService.kt | 4 +- .../services/vault/VaultQueryJavaTests.java | 47 ++++++++------ samples/bank-of-corda-demo/build.gradle | 10 +-- samples/irs-demo/build.gradle | 10 +-- .../kotlin/net/corda/irs/IRSDemoTest.kt | 4 +- .../kotlin/net/corda/irs/plugin/IRSPlugin.kt | 4 ++ .../corda/netmap/simulation/IRSSimulation.kt | 4 +- samples/simm-valuation-demo/build.gradle | 12 ++-- .../net/corda/vega/plugin/SimmPlugin.kt | 3 + samples/trader-demo/build.gradle | 12 ++-- webserver/build.gradle | 1 - .../corda/webserver/internal/NodeWebServer.kt | 8 ++- .../service/WebServerPluginRegistry.kt | 7 ++ .../webserver/servlets/ObjectMapperConfig.kt | 5 +- 30 files changed, 285 insertions(+), 173 deletions(-) create mode 100644 finance/src/main/kotlin/net/corda/plugin/FinanceJSONSupport.kt create mode 100644 finance/src/main/kotlin/net/corda/plugin/FinancePluginRegistry.kt create mode 100644 finance/src/main/resources/META-INF/services/net.corda.core.node.CordaPluginRegistry diff --git a/client/jackson/build.gradle b/client/jackson/build.gradle index f591484aa0..fc8d1b62b7 100644 --- a/client/jackson/build.gradle +++ b/client/jackson/build.gradle @@ -5,7 +5,6 @@ apply plugin: 'com.jfrog.artifactory' dependencies { compile project(':core') - compile project(':finance') testCompile project(':test-utils') compile "org.jetbrains.kotlin:kotlin-stdlib-jre8:$kotlin_version" diff --git a/client/jackson/src/main/kotlin/net/corda/jackson/JacksonSupport.kt b/client/jackson/src/main/kotlin/net/corda/jackson/JacksonSupport.kt index 907484e477..cf2ed02f52 100644 --- a/client/jackson/src/main/kotlin/net/corda/jackson/JacksonSupport.kt +++ b/client/jackson/src/main/kotlin/net/corda/jackson/JacksonSupport.kt @@ -5,11 +5,9 @@ import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.core.* import com.fasterxml.jackson.databind.* 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.datatype.jsr310.JavaTimeModule import com.fasterxml.jackson.module.kotlin.KotlinModule -import net.corda.contracts.BusinessCalendar import net.corda.core.contracts.Amount import net.corda.core.contracts.ContractState 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.WireTransaction import net.corda.core.utilities.OpaqueBytes -import net.corda.finance.parseCurrency import net.i2p.crypto.eddsa.EdDSAPublicKey import org.bouncycastle.asn1.x500.X500Name import java.math.BigDecimal import java.security.PublicKey -import java.time.LocalDate import java.util.* /** @@ -84,8 +80,6 @@ object JacksonSupport { addSerializer(SecureHash.SHA256::class.java, SecureHashSerializer) addDeserializer(SecureHash::class.java, SecureHashDeserializer()) addDeserializer(SecureHash.SHA256::class.java, SecureHashDeserializer()) - addSerializer(BusinessCalendar::class.java, CalendarSerializer) - addDeserializer(BusinessCalendar::class.java, CalendarDeserializer) // For ed25519 pubkeys addSerializer(EdDSAPublicKey::class.java, PublicKeySerializer) @@ -277,36 +271,6 @@ object JacksonSupport { } } - data class BusinessCalendarWrapper(val holidayDates: List) { - fun toCalendar() = BusinessCalendar(holidayDates) - } - - object CalendarSerializer : JsonSerializer() { - 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() { - 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() { override fun serialize(obj: EdDSAPublicKey, generator: JsonGenerator, provider: SerializerProvider) { check(obj.params == Crypto.EDDSA_ED25519_SHA512.algSpec) @@ -349,7 +313,7 @@ object JacksonSupport { object AmountDeserializer : JsonDeserializer>() { override fun deserialize(parser: JsonParser, context: DeserializationContext): Amount<*> { try { - return parseCurrency(parser.text) + return Amount.parseCurrency(parser.text) } catch (e: Exception) { try { val tree = parser.readValueAsTree() diff --git a/client/jackson/src/test/kotlin/net/corda/jackson/JacksonSupportTest.kt b/client/jackson/src/test/kotlin/net/corda/jackson/JacksonSupportTest.kt index baa47ce900..13acd147ab 100644 --- a/client/jackson/src/test/kotlin/net/corda/jackson/JacksonSupportTest.kt +++ b/client/jackson/src/test/kotlin/net/corda/jackson/JacksonSupportTest.kt @@ -8,7 +8,6 @@ import net.corda.core.crypto.SignatureMetadata import net.corda.core.crypto.TransactionSignature import net.corda.core.crypto.generateKeyPair import net.corda.core.transactions.SignedTransaction -import net.corda.finance.parseCurrency import net.corda.testing.ALICE_PUBKEY import net.corda.testing.DUMMY_NOTARY import net.corda.testing.MINI_CORP @@ -53,7 +52,7 @@ class JacksonSupportTest : TestDependencyInjectionBase() { @Test fun writeAmount() { 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 diff --git a/client/rpc/build.gradle b/client/rpc/build.gradle index 61159ac74d..18a2ece101 100644 --- a/client/rpc/build.gradle +++ b/client/rpc/build.gradle @@ -50,6 +50,9 @@ processSmokeTestResources { from(project(':node:capsule').tasks['buildCordaJAR']) { 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 diff --git a/client/rpc/src/smoke-test/java/net/corda/java/rpc/StandaloneCordaRPCJavaClientTest.java b/client/rpc/src/smoke-test/java/net/corda/java/rpc/StandaloneCordaRPCJavaClientTest.java index 38abeae9c7..cf76d09aa6 100644 --- a/client/rpc/src/smoke-test/java/net/corda/java/rpc/StandaloneCordaRPCJavaClientTest.java +++ b/client/rpc/src/smoke-test/java/net/corda/java/rpc/StandaloneCordaRPCJavaClientTest.java @@ -18,11 +18,17 @@ import org.junit.After; import org.junit.Before; 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.concurrent.ExecutionException; import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Stream; import static kotlin.test.AssertionsKt.assertEquals; +import static kotlin.test.AssertionsKt.fail; import static net.corda.contracts.GetBalances.getCashBalance; public class StandaloneCordaRPCJavaClientTest { @@ -32,6 +38,7 @@ public class StandaloneCordaRPCJavaClientTest { private AtomicInteger port = new AtomicInteger(15000); + private NodeProcess.Factory factory; private NodeProcess notary; private CordaRPCOps rpcProxy; private CordaRPCConnection connection; @@ -49,7 +56,9 @@ public class StandaloneCordaRPCJavaClientTest { @Before public void setUp() { - notary = new NodeProcess.Factory().create(notaryConfig); + factory = new NodeProcess.Factory(); + copyFinanceCordapp(); + notary = factory.create(notaryConfig); connection = notary.connect(); rpcProxy = connection.getProxy(); 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 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() { List nodeDataSnapshot = rpcProxy.networkMapSnapshot(); return nodeDataSnapshot.get(0); diff --git a/client/rpc/src/smoke-test/kotlin/net/corda/kotlin/rpc/StandaloneCordaRPClientTest.kt b/client/rpc/src/smoke-test/kotlin/net/corda/kotlin/rpc/StandaloneCordaRPClientTest.kt index 4a6a9dfdf4..79d0117557 100644 --- a/client/rpc/src/smoke-test/kotlin/net/corda/kotlin/rpc/StandaloneCordaRPClientTest.kt +++ b/client/rpc/src/smoke-test/kotlin/net/corda/kotlin/rpc/StandaloneCordaRPClientTest.kt @@ -7,7 +7,7 @@ import net.corda.contracts.asset.Cash import net.corda.contracts.getCashBalance import net.corda.contracts.getCashBalances 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.node.NodeInfo import net.corda.core.node.services.Vault @@ -32,8 +32,10 @@ import org.junit.Before import org.junit.Test import java.io.FilterInputStream import java.io.InputStream +import java.nio.file.Paths import java.util.* import java.util.concurrent.atomic.AtomicInteger +import kotlin.streams.toList import kotlin.test.assertEquals import kotlin.test.assertFalse import kotlin.test.assertNotEquals @@ -43,11 +45,12 @@ class StandaloneCordaRPClientTest { private companion object { val log = loggerFor() val user = User("user1", "test", permissions = setOf("ALL")) - val port = AtomicInteger(15000) + val port = AtomicInteger(15200) const val attachmentSize = 2116 val timeout = 60.seconds } + private lateinit var factory: NodeProcess.Factory private lateinit var notary: NodeProcess private lateinit var rpcProxy: CordaRPCOps private lateinit var connection: CordaRPCConnection @@ -64,7 +67,9 @@ class StandaloneCordaRPClientTest { @Before fun setUp() { - notary = NodeProcess.Factory().create(notaryConfig) + factory = NodeProcess.Factory() + copyFinanceCordapp() + notary = factory.create(notaryConfig) connection = notary.connect() rpcProxy = connection.proxy 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 fun `test attachments`() { val attachment = InputStreamAndHash.createInMemoryTestZip(attachmentSize, 1) @@ -189,6 +203,7 @@ class StandaloneCordaRPClientTest { @Test fun `test cash balances`() { val startCash = rpcProxy.getCashBalances() + println(startCash) assertTrue(startCash.isEmpty(), "Should not start with any cash") val flowHandle = rpcProxy.startFlow(::CashIssueFlow, 629.DOLLARS, OpaqueBytes.of(0), notaryNode.legalIdentity) diff --git a/core/src/main/kotlin/net/corda/core/contracts/Amount.kt b/core/src/main/kotlin/net/corda/core/contracts/Amount.kt index 977e936eb7..6de842ff63 100644 --- a/core/src/main/kotlin/net/corda/core/contracts/Amount.kt +++ b/core/src/main/kotlin/net/corda/core/contracts/Amount.kt @@ -107,6 +107,70 @@ data class Amount(val quantity: Long, val displayTokenSize: BigDecimal, */ @JvmStatic fun Iterable>.sumOrZero(token: T) = if (iterator().hasNext()) sumOrThrow() else Amount.zero(token) + + private val currencySymbols: Map = 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 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 { + 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 { diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst index 4cc46a138c..d3a9ee6755 100644 --- a/docs/source/changelog.rst +++ b/docs/source/changelog.rst @@ -51,6 +51,11 @@ UNRELEASED no longer auto-suggests these extension functions in completion unless you add import lines for them yourself (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 ------------ diff --git a/docs/source/writing-cordapps.rst b/docs/source/writing-cordapps.rst index c63be2b40f..e99bd03e70 100644 --- a/docs/source/writing-cordapps.rst +++ b/docs/source/writing-cordapps.rst @@ -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 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 To learn about how to use gradle to build your cordapp against Corda and generate an artifact please read diff --git a/finance/build.gradle b/finance/build.gradle index 9604e46672..e51891d563 100644 --- a/finance/build.gradle +++ b/finance/build.gradle @@ -5,13 +5,16 @@ apply plugin: 'kotlin-jpa' apply plugin: CanonicalizerPlugin apply plugin: 'net.corda.plugins.publish-utils' apply plugin: 'net.corda.plugins.quasar-utils' +apply plugin: 'net.corda.plugins.cordformation' apply plugin: 'com.jfrog.artifactory' description 'Corda finance modules' dependencies { - compile project(':core') - compile project(':node-schemas') + // Note the :finance module is a CorDapp in its own right + // and CorDapps using :finance features should use 'cordapp' not 'compile' linkage. + cordaCompile project(':core') + cordaCompile project(':node-schemas') testCompile project(':test-utils') testCompile project(path: ':core', configuration: 'testArtifacts') diff --git a/finance/src/main/kotlin/net/corda/finance/CurrencyUtils.kt b/finance/src/main/kotlin/net/corda/finance/CurrencyUtils.kt index 2a566aacb7..a440ba7ac6 100644 --- a/finance/src/main/kotlin/net/corda/finance/CurrencyUtils.kt +++ b/finance/src/main/kotlin/net/corda/finance/CurrencyUtils.kt @@ -31,66 +31,3 @@ infix fun Currency.`issued by`(deposit: PartyAndReference) = issuedBy(deposit) infix fun Amount.`issued by`(deposit: PartyAndReference) = issuedBy(deposit) infix fun Currency.issuedBy(deposit: PartyAndReference) = Issued(deposit, this) infix fun Amount.issuedBy(deposit: PartyAndReference) = Amount(quantity, displayTokenSize, token.issuedBy(deposit)) - -private val currencySymbols: Map = mapOf( - "$" to USD, - "£" to GBP, - "€" to EUR, - "¥" to JPY, - "₽" to RUB -) - -private val currencyCodes: Map 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 { - 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") -} \ No newline at end of file diff --git a/finance/src/main/kotlin/net/corda/plugin/FinanceJSONSupport.kt b/finance/src/main/kotlin/net/corda/plugin/FinanceJSONSupport.kt new file mode 100644 index 0000000000..dc6e6227f2 --- /dev/null +++ b/finance/src/main/kotlin/net/corda/plugin/FinanceJSONSupport.kt @@ -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) { + fun toCalendar() = BusinessCalendar(holidayDates) +} + +object CalendarSerializer : JsonSerializer() { + 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() { + 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}") + } + } +} diff --git a/finance/src/main/kotlin/net/corda/plugin/FinancePluginRegistry.kt b/finance/src/main/kotlin/net/corda/plugin/FinancePluginRegistry.kt new file mode 100644 index 0000000000..98faa3db5a --- /dev/null +++ b/finance/src/main/kotlin/net/corda/plugin/FinancePluginRegistry.kt @@ -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 = setOf( + CashSchemaV1 + ) +} \ No newline at end of file diff --git a/finance/src/main/resources/META-INF/services/net.corda.core.node.CordaPluginRegistry b/finance/src/main/resources/META-INF/services/net.corda.core.node.CordaPluginRegistry new file mode 100644 index 0000000000..08be123cbf --- /dev/null +++ b/finance/src/main/resources/META-INF/services/net.corda.core.node.CordaPluginRegistry @@ -0,0 +1 @@ +net.corda.plugin.FinancePluginRegistry diff --git a/finance/src/test/kotlin/net/corda/finance/CurrencyUtilsTest.kt b/finance/src/test/kotlin/net/corda/finance/CurrencyUtilsTest.kt index 6e81ffc478..4ee90333ea 100644 --- a/finance/src/test/kotlin/net/corda/finance/CurrencyUtilsTest.kt +++ b/finance/src/test/kotlin/net/corda/finance/CurrencyUtilsTest.kt @@ -14,17 +14,17 @@ class CurrencyUtilsTest { @Test fun parseCurrency() { - assertEquals(Amount(1234L, GBP), parseCurrency("£12.34")) - assertEquals(Amount(1200L, GBP), parseCurrency("£12")) - assertEquals(Amount(1000L, USD), parseCurrency("$10")) - assertEquals(Amount(5000L, JPY), parseCurrency("¥5000")) - assertEquals(Amount(500000L, RUB), parseCurrency("₽5000")) - assertEquals(Amount(1500000000L, CHF), parseCurrency("15,000,000 CHF")) + assertEquals(Amount(1234L, GBP), Amount.parseCurrency("£12.34")) + assertEquals(Amount(1200L, GBP), Amount.parseCurrency("£12")) + assertEquals(Amount(1000L, USD), Amount.parseCurrency("$10")) + assertEquals(Amount(5000L, JPY), Amount.parseCurrency("¥5000")) + assertEquals(Amount(500000L, RUB), Amount.parseCurrency("₽5000")) + assertEquals(Amount(1500000000L, CHF), Amount.parseCurrency("15,000,000 CHF")) } @Test fun rendering() { - assertEquals("5000 JPY", parseCurrency("¥5000").toString()) - assertEquals("50.12 USD", parseCurrency("$50.12").toString()) + assertEquals("5000 JPY", Amount.parseCurrency("¥5000").toString()) + assertEquals("50.12 USD", Amount.parseCurrency("$50.12").toString()) } } \ No newline at end of file diff --git a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt index 4b5db6c46a..13e35d4107 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -27,9 +27,6 @@ import net.corda.core.transactions.SignedTransaction import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.debug 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.NotaryChangeHandler import net.corda.node.services.NotifyTransactionHandler @@ -383,11 +380,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, .filter { it.isUserInvokable() } + // Add any core flows here listOf( - 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) + ContractUpgradeFlow::class.java) } /** diff --git a/node/src/main/kotlin/net/corda/node/services/schema/NodeSchemaService.kt b/node/src/main/kotlin/net/corda/node/services/schema/NodeSchemaService.kt index 2772990e7a..d08e4128b8 100644 --- a/node/src/main/kotlin/net/corda/node/services/schema/NodeSchemaService.kt +++ b/node/src/main/kotlin/net/corda/node/services/schema/NodeSchemaService.kt @@ -16,7 +16,6 @@ import net.corda.node.services.persistence.DBTransactionMappingStorage import net.corda.node.services.persistence.DBTransactionStorage import net.corda.node.services.transactions.PersistentUniquenessProvider import net.corda.node.services.vault.VaultSchemaV1 -import net.corda.schemas.CashSchemaV1 /** * Most basic implementation of [SchemaService]. @@ -43,8 +42,7 @@ class NodeSchemaService(customSchemas: Set = emptySet()) : SchemaS // 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) val requiredSchemas: Map = - mapOf(Pair(CashSchemaV1, SchemaService.SchemaOptions()), - Pair(CommonSchemaV1, SchemaService.SchemaOptions()), + mapOf(Pair(CommonSchemaV1, SchemaService.SchemaOptions()), Pair(VaultSchemaV1, SchemaService.SchemaOptions()), Pair(NodeServicesV1, SchemaService.SchemaOptions())) diff --git a/node/src/test/java/net/corda/node/services/vault/VaultQueryJavaTests.java b/node/src/test/java/net/corda/node/services/vault/VaultQueryJavaTests.java index 52d3169f08..576e428ff6 100644 --- a/node/src/test/java/net/corda/node/services/vault/VaultQueryJavaTests.java +++ b/node/src/test/java/net/corda/node/services/vault/VaultQueryJavaTests.java @@ -3,16 +3,21 @@ package net.corda.node.services.vault; import com.google.common.collect.ImmutableSet; import kotlin.Pair; 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.crypto.EncodingUtils; import net.corda.core.identity.AbstractParty; 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.QueryCriteria.LinearStateQueryCriteria; import net.corda.core.node.services.vault.QueryCriteria.VaultCustomQueryCriteria; import net.corda.core.node.services.vault.QueryCriteria.VaultQueryCriteria; +import net.corda.core.schemas.MappedSchema; import net.corda.core.utilities.OpaqueBytes; import net.corda.node.utilities.CordaPersistence; import net.corda.schemas.CashSchemaV1; @@ -34,12 +39,14 @@ import java.util.stream.Collectors; import java.util.stream.Stream; 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.MAX_PAGE_SIZE; import static net.corda.core.utilities.ByteArrays.toHexString; import static net.corda.testing.CoreTestUtils.*; -import static net.corda.testing.TestConstants.*; -import static net.corda.contracts.asset.CashUtilities.*; +import static net.corda.testing.TestConstants.getDUMMY_NOTARY; +import static net.corda.testing.TestConstants.getDUMMY_NOTARY_KEY; import static net.corda.testing.node.MockServicesKt.makeTestDatabaseAndMockServices; import static net.corda.testing.node.MockServicesKt.makeTestIdentityService; import static org.assertj.core.api.Assertions.assertThat; @@ -56,8 +63,10 @@ public class VaultQueryJavaTests extends TestDependencyInjectionBase { ArrayList keys = new ArrayList<>(); keys.add(getMEGA_CORP_KEY()); keys.add(getDUMMY_NOTARY_KEY()); + Set requiredSchemas = new HashSet<>(); + requiredSchemas.add(CashSchemaV1.INSTANCE); IdentityService identitySvc = makeTestIdentityService(); - Pair databaseAndServices = makeTestDatabaseAndMockServices(Collections.EMPTY_SET, keys, () -> identitySvc); + Pair databaseAndServices = makeTestDatabaseAndMockServices(requiredSchemas, keys, () -> identitySvc); issuerServices = new MockServices(getDUMMY_CASH_ISSUER_KEY(), getBOC_KEY()); database = databaseAndServices.getFirst(); services = databaseAndServices.getSecond(); @@ -74,7 +83,7 @@ public class VaultQueryJavaTests extends TestDependencyInjectionBase { */ /** - * Static queryBy() tests + * Static queryBy() tests */ @Test @@ -125,15 +134,15 @@ public class VaultQueryJavaTests extends TestDependencyInjectionBase { Amount amount = new Amount<>(100, Currency.getInstance("USD")); VaultFiller.fillWithSomeTestCash(services, - new Amount(100, Currency.getInstance("USD")), + new Amount(100, Currency.getInstance("USD")), issuerServices, - TestConstants.getDUMMY_NOTARY(), - 3, - 3, - new Random(), - new OpaqueBytes("1".getBytes()), - null, - CashUtilities.getDUMMY_CASH_ISSUER()); + TestConstants.getDUMMY_NOTARY(), + 3, + 3, + new Random(), + new OpaqueBytes("1".getBytes()), + null, + CashUtilities.getDUMMY_CASH_ISSUER()); VaultFiller.consumeCash(services, amount, getDUMMY_NOTARY()); @@ -177,7 +186,7 @@ public class VaultQueryJavaTests extends TestDependencyInjectionBase { QueryCriteria compositeCriteria1 = dealCriteriaAll.or(linearCriteriaAll); QueryCriteria compositeCriteria2 = vaultCriteria.and(compositeCriteria1); - PageSpecification pageSpec = new PageSpecification(DEFAULT_PAGE_NUM, MAX_PAGE_SIZE); + PageSpecification pageSpec = new PageSpecification(DEFAULT_PAGE_NUM, MAX_PAGE_SIZE); Sort.SortColumn sortByUid = new Sort.SortColumn(new SortAttribute.Standard(Sort.LinearStateAttribute.UUID), Sort.Direction.DESC); Sort sorting = new Sort(ImmutableSet.of(sortByUid)); Vault.Page results = vaultQuerySvc.queryBy(LinearState.class, compositeCriteria2, pageSpec, sorting); @@ -226,12 +235,12 @@ public class VaultQueryJavaTests extends TestDependencyInjectionBase { } catch (NoSuchFieldException e) { e.printStackTrace(); } - return tx; + return tx; }); } /** - * Dynamic trackBy() tests + * Dynamic trackBy() tests */ @Test @@ -287,7 +296,7 @@ public class VaultQueryJavaTests extends TestDependencyInjectionBase { QueryCriteria dealOrLinearIdCriteria = dealCriteria.or(linearCriteria); QueryCriteria compositeCriteria = dealOrLinearIdCriteria.and(vaultCriteria); - PageSpecification pageSpec = new PageSpecification(DEFAULT_PAGE_NUM, MAX_PAGE_SIZE); + PageSpecification pageSpec = new PageSpecification(DEFAULT_PAGE_NUM, MAX_PAGE_SIZE); Sort.SortColumn sortByUid = new Sort.SortColumn(new SortAttribute.Standard(Sort.LinearStateAttribute.UUID), Sort.Direction.DESC); Sort sorting = new Sort(ImmutableSet.of(sortByUid)); DataFeed, Vault.Update> results = vaultQuerySvc.trackBy(ContractState.class, compositeCriteria, pageSpec, sorting); @@ -302,7 +311,7 @@ public class VaultQueryJavaTests extends TestDependencyInjectionBase { } /** - * Aggregation Functions + * Aggregation Functions */ @Test diff --git a/samples/bank-of-corda-demo/build.gradle b/samples/bank-of-corda-demo/build.gradle index d2a42095f2..e77f74563c 100644 --- a/samples/bank-of-corda-demo/build.gradle +++ b/samples/bank-of-corda-demo/build.gradle @@ -24,13 +24,15 @@ configurations { dependencies { 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 cordaCompile project(path: ":node:capsule", configuration: 'runtimeArtifacts') cordaCompile project(path: ":webserver:webcapsule", configuration: 'runtimeArtifacts') cordaCompile project(':core') cordaCompile project(':client:jfx') cordaCompile project(':client:rpc') - cordaCompile project(':finance') cordaCompile project(':webserver') cordaCompile project(':test-utils') @@ -54,7 +56,7 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) { advertisedServices = ["corda.notary.validating"] p2pPort 10002 rpcPort 10003 - cordapps = [] + cordapps = ["net.corda:finance:$corda_release_version"] } node { 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 rpcPort 10006 webPort 10007 - cordapps = [] + cordapps = ["net.corda:finance:$corda_release_version"] rpcUsers = [ ['username' : "bankUser", 'password' : "test", @@ -77,7 +79,7 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) { p2pPort 10008 rpcPort 10009 webPort 10010 - cordapps = [] + cordapps = ["net.corda:finance:$corda_release_version"] rpcUsers = [ ['username' : "bigCorpUser", 'password' : "test", diff --git a/samples/irs-demo/build.gradle b/samples/irs-demo/build.gradle index 70d42a9d19..6913d5d0b4 100644 --- a/samples/irs-demo/build.gradle +++ b/samples/irs-demo/build.gradle @@ -27,11 +27,13 @@ configurations { dependencies { compile "org.jetbrains.kotlin:kotlin-stdlib-jre8:$kotlin_version" + // The irs demo CorDapp depends upon Cash CorDapp features + cordapp project(':finance') + // Corda integration dependencies cordaCompile project(path: ":node:capsule", configuration: 'runtimeArtifacts') cordaCompile project(path: ":webserver:webcapsule", configuration: 'runtimeArtifacts') cordaCompile project(':core') - cordaCompile project(':finance') cordaCompile project(':webserver') // Javax is required for webapis @@ -55,7 +57,7 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) { p2pPort 10002 rpcPort 10003 webPort 10004 - cordapps = [] + cordapps = ["net.corda:finance:$corda_release_version"] useTestClock true } node { @@ -64,7 +66,7 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) { p2pPort 10005 rpcPort 10006 webPort 10007 - cordapps = [] + cordapps = ["net.corda:finance:$corda_release_version"] useTestClock true } node { @@ -73,7 +75,7 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) { p2pPort 10008 rpcPort 10009 webPort 10010 - cordapps = [] + cordapps = ["net.corda:finance:$corda_release_version"] useTestClock true } } diff --git a/samples/irs-demo/src/integration-test/kotlin/net/corda/irs/IRSDemoTest.kt b/samples/irs-demo/src/integration-test/kotlin/net/corda/irs/IRSDemoTest.kt index dc4c021c4b..4b1ac21f94 100644 --- a/samples/irs-demo/src/integration-test/kotlin/net/corda/irs/IRSDemoTest.kt +++ b/samples/irs-demo/src/integration-test/kotlin/net/corda/irs/IRSDemoTest.kt @@ -1,10 +1,10 @@ package net.corda.irs import net.corda.client.rpc.CordaRPCClient +import net.corda.core.internal.concurrent.transpose import net.corda.core.messaging.vaultTrackBy import net.corda.core.node.services.ServiceInfo import net.corda.core.toFuture -import net.corda.core.internal.concurrent.transpose import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.getOrThrow 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.transactions.SimpleNotaryService import net.corda.nodeapi.User +import net.corda.plugin.registerFinanceJSONMappers import net.corda.testing.DUMMY_BANK_A import net.corda.testing.DUMMY_BANK_B 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 mapper = net.corda.jackson.JacksonSupport.createDefaultMapper(it.first.rpc) + registerFinanceJSONMappers(mapper) HttpApi.fromHostAndPort(it.second, "api/irs", mapper = mapper) } val nextFixingDates = getFixingDateObservable(nodeA.configuration) diff --git a/samples/irs-demo/src/main/kotlin/net/corda/irs/plugin/IRSPlugin.kt b/samples/irs-demo/src/main/kotlin/net/corda/irs/plugin/IRSPlugin.kt index 80de4531b1..4223b12f16 100644 --- a/samples/irs-demo/src/main/kotlin/net/corda/irs/plugin/IRSPlugin.kt +++ b/samples/irs-demo/src/main/kotlin/net/corda/irs/plugin/IRSPlugin.kt @@ -1,6 +1,8 @@ package net.corda.irs.plugin +import com.fasterxml.jackson.databind.ObjectMapper import net.corda.irs.api.InterestRateSwapAPI +import net.corda.plugin.registerFinanceJSONMappers import net.corda.webserver.services.WebServerPluginRegistry import java.util.function.Function @@ -9,4 +11,6 @@ class IRSPlugin : WebServerPluginRegistry { override val staticServeDirs: Map = mapOf( "irsdemo" to javaClass.classLoader.getResource("irsweb").toExternalForm() ) + + override fun customizeJSONSerialization(om: ObjectMapper): Unit = registerFinanceJSONMappers(om) } diff --git a/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/IRSSimulation.kt b/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/IRSSimulation.kt index d229f24cc7..f3c489b896 100644 --- a/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/IRSSimulation.kt +++ b/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/IRSSimulation.kt @@ -9,8 +9,8 @@ import net.corda.core.flows.FlowLogic import net.corda.core.flows.InitiatedBy import net.corda.core.flows.InitiatingFlow import net.corda.core.identity.Party -import net.corda.core.internal.concurrent.* import net.corda.core.internal.FlowStateMachine +import net.corda.core.internal.concurrent.* import net.corda.core.node.services.queryBy import net.corda.core.toFuture 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.jackson.JacksonSupport import net.corda.node.services.identity.InMemoryIdentityService +import net.corda.plugin.registerFinanceJSONMappers import net.corda.testing.DUMMY_CA import net.corda.testing.node.InMemoryMessagingNetwork import rx.Observable @@ -44,6 +45,7 @@ class IRSSimulation(networkSendManuallyPumped: Boolean, runAsync: Boolean, laten override fun startMainSimulation(): CordaFuture { val future = openFuture() om = JacksonSupport.createInMemoryMapper(InMemoryIdentityService((banks + regulators + networkMap).map { it.info.legalIdentityAndCert }, trustRoot = DUMMY_CA.certificate)) + registerFinanceJSONMappers(om) startIRSDealBetween(0, 1).thenMatch({ // Next iteration is a pause. diff --git a/samples/simm-valuation-demo/build.gradle b/samples/simm-valuation-demo/build.gradle index ba1bf69d30..779f4f6b92 100644 --- a/samples/simm-valuation-demo/build.gradle +++ b/samples/simm-valuation-demo/build.gradle @@ -30,11 +30,13 @@ configurations { dependencies { compile "org.jetbrains.kotlin:kotlin-stdlib-jre8:$kotlin_version" + // The SIMM demo CorDapp depends upon Cash CorDapp features + cordapp project(':finance') + // Corda integration dependencies cordaCompile project(path: ":node:capsule", configuration: 'runtimeArtifacts') cordaCompile project(path: ":webserver:webcapsule", configuration: 'runtimeArtifacts') cordaCompile project(':core') - cordaCompile project(':finance') cordaCompile project(':webserver') // 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" advertisedServices = ["corda.notary.validating"] p2pPort 10002 - cordapps = [] + cordapps = ["net.corda:finance:$corda_release_version"] } node { name "CN=Bank A,O=Bank A,L=London,C=GB" advertisedServices = [] p2pPort 10004 webPort 10005 - cordapps = [] + cordapps = ["net.corda:finance:$corda_release_version"] } node { name "CN=Bank B,O=Bank B,L=New York,C=US" advertisedServices = [] p2pPort 10006 webPort 10007 - cordapps = [] + cordapps = ["net.corda:finance:$corda_release_version"] } node { name "CN=Bank C,O=Bank C,L=Tokyo,C=Japan" advertisedServices = [] p2pPort 10008 webPort 10009 - cordapps = [] + cordapps = ["net.corda:finance:$corda_release_version"] } } diff --git a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/plugin/SimmPlugin.kt b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/plugin/SimmPlugin.kt index 3eda5bef8e..c7df3b0f3b 100644 --- a/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/plugin/SimmPlugin.kt +++ b/samples/simm-valuation-demo/src/main/kotlin/net/corda/vega/plugin/SimmPlugin.kt @@ -1,6 +1,8 @@ package net.corda.vega.plugin +import com.fasterxml.jackson.databind.ObjectMapper import net.corda.core.node.CordaPluginRegistry +import net.corda.plugin.registerFinanceJSONMappers import net.corda.vega.api.PortfolioApi import net.corda.webserver.services.WebServerPluginRegistry import java.util.function.Function @@ -14,4 +16,5 @@ import java.util.function.Function class SimmPlugin : WebServerPluginRegistry { override val webApis = listOf(Function(::PortfolioApi)) override val staticServeDirs: Map = mapOf("simmvaluationdemo" to javaClass.classLoader.getResource("simmvaluationweb").toExternalForm()) + override fun customizeJSONSerialization(om: ObjectMapper): Unit = registerFinanceJSONMappers(om) } \ No newline at end of file diff --git a/samples/trader-demo/build.gradle b/samples/trader-demo/build.gradle index 5cec069227..d566cf549a 100644 --- a/samples/trader-demo/build.gradle +++ b/samples/trader-demo/build.gradle @@ -24,11 +24,13 @@ configurations { dependencies { compile "org.jetbrains.kotlin:kotlin-stdlib-jre8:$kotlin_version" + // The trader demo CorDapp depends upon Cash CorDapp features + cordapp project(':finance') + // Corda integration dependencies cordaCompile project(path: ":node:capsule", configuration: 'runtimeArtifacts') cordaCompile project(path: ":webserver:webcapsule", configuration: 'runtimeArtifacts') cordaCompile project(':core') - cordaCompile project(':finance') // Corda Plugins: dependent flows and services 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" advertisedServices = ["corda.notary.validating"] p2pPort 10002 - cordapps = [] + cordapps = ["net.corda:finance:$corda_release_version"] } node { name "CN=Bank A,O=Bank A,L=London,C=GB" advertisedServices = [] p2pPort 10005 rpcPort 10006 - cordapps = [] + cordapps = ["net.corda:finance:$corda_release_version"] rpcUsers = ext.rpcUsers } node { @@ -68,7 +70,7 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) { advertisedServices = [] p2pPort 10008 rpcPort 10009 - cordapps = [] + cordapps = ["net.corda:finance:$corda_release_version"] rpcUsers = ext.rpcUsers } node { @@ -76,7 +78,7 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) { advertisedServices = [] p2pPort 10011 rpcPort 10012 - cordapps = [] + cordapps = ["net.corda:finance:$corda_release_version"] rpcUsers = ext.rpcUsers } } diff --git a/webserver/build.gradle b/webserver/build.gradle index 05c2daf185..d3b9b1c075 100644 --- a/webserver/build.gradle +++ b/webserver/build.gradle @@ -31,7 +31,6 @@ processTestResources { dependencies { compile project(':core') - compile project(':finance') compile project(':client:rpc') compile project(':client:jackson') diff --git a/webserver/src/main/kotlin/net/corda/webserver/internal/NodeWebServer.kt b/webserver/src/main/kotlin/net/corda/webserver/internal/NodeWebServer.kt index 6ad3c21ec9..10fe8410e2 100644 --- a/webserver/src/main/kotlin/net/corda/webserver/internal/NodeWebServer.kt +++ b/webserver/src/main/kotlin/net/corda/webserver/internal/NodeWebServer.kt @@ -4,6 +4,7 @@ import com.google.common.html.HtmlEscapers.htmlEscaper import net.corda.client.rpc.CordaRPCClient import net.corda.core.messaging.CordaRPCOps import net.corda.core.utilities.loggerFor +import net.corda.jackson.JacksonSupport import net.corda.nodeapi.ArtemisMessagingComponent import net.corda.webserver.WebServerConfig import net.corda.webserver.services.WebServerPluginRegistry @@ -127,8 +128,13 @@ class NodeWebServer(val config: WebServerConfig) { addServlet(DataUploadServlet::class.java, "/upload/*") addServlet(AttachmentDownloadServlet::class.java, "/attachments/*") + val rpcObjectMapper = pluginRegistries.fold(JacksonSupport.createDefaultMapper(localRpc)) { om, plugin -> + plugin.customizeJSONSerialization(om) + om + } + val resourceConfig = ResourceConfig() - .register(ObjectMapperConfig(localRpc)) + .register(ObjectMapperConfig(rpcObjectMapper)) .register(ResponseFilter()) .register(APIServerImpl(localRpc)) diff --git a/webserver/src/main/kotlin/net/corda/webserver/service/WebServerPluginRegistry.kt b/webserver/src/main/kotlin/net/corda/webserver/service/WebServerPluginRegistry.kt index 69f5cb612c..6f7be07364 100644 --- a/webserver/src/main/kotlin/net/corda/webserver/service/WebServerPluginRegistry.kt +++ b/webserver/src/main/kotlin/net/corda/webserver/service/WebServerPluginRegistry.kt @@ -1,5 +1,6 @@ package net.corda.webserver.services +import com.fasterxml.jackson.databind.ObjectMapper import net.corda.core.messaging.CordaRPCOps import java.util.function.Function @@ -21,4 +22,10 @@ interface WebServerPluginRegistry { */ val staticServeDirs: Map 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 {} + } \ No newline at end of file diff --git a/webserver/src/main/kotlin/net/corda/webserver/servlets/ObjectMapperConfig.kt b/webserver/src/main/kotlin/net/corda/webserver/servlets/ObjectMapperConfig.kt index 56965cf877..8dc7635944 100644 --- a/webserver/src/main/kotlin/net/corda/webserver/servlets/ObjectMapperConfig.kt +++ b/webserver/src/main/kotlin/net/corda/webserver/servlets/ObjectMapperConfig.kt @@ -1,8 +1,6 @@ package net.corda.webserver.servlets 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.Provider @@ -11,7 +9,6 @@ import javax.ws.rs.ext.Provider * and to organise serializers / deserializers for java.time.* classes as necessary. */ @Provider -class ObjectMapperConfig(rpc: CordaRPCOps) : ContextResolver { - val defaultObjectMapper = JacksonSupport.createDefaultMapper(rpc) +class ObjectMapperConfig(val defaultObjectMapper: ObjectMapper) : ContextResolver { override fun getContext(type: Class<*>) = defaultObjectMapper }