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 }