Remove finance dependencies

Re-enable code now DealState PR is in.

Add plugable JSON serialisation

Add docs for new plugin api.

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

Use :finance module as a proper CorDapp

Move parseCurrency back onto Amount companion.

Fix smoke tests

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

View File

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

View File

@ -5,11 +5,9 @@ import com.fasterxml.jackson.annotation.JsonProperty
import com.fasterxml.jackson.core.*
import com.fasterxml.jackson.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<LocalDate>) {
fun toCalendar() = BusinessCalendar(holidayDates)
}
object CalendarSerializer : JsonSerializer<BusinessCalendar>() {
override fun serialize(obj: BusinessCalendar, generator: JsonGenerator, context: SerializerProvider) {
val calendarName = BusinessCalendar.calendars.find { BusinessCalendar.getInstance(it) == obj }
if (calendarName != null) {
generator.writeString(calendarName)
} else {
generator.writeObject(BusinessCalendarWrapper(obj.holidayDates))
}
}
}
object CalendarDeserializer : JsonDeserializer<BusinessCalendar>() {
override fun deserialize(parser: JsonParser, context: DeserializationContext): BusinessCalendar {
return try {
try {
val array = StringArrayDeserializer.instance.deserialize(parser, context)
BusinessCalendar.getInstance(*array)
} catch (e: Exception) {
parser.readValueAs(BusinessCalendarWrapper::class.java).toCalendar()
}
} catch (e: Exception) {
throw JsonParseException(parser, "Invalid calendar(s) ${parser.text}: ${e.message}")
}
}
}
object PublicKeySerializer : JsonSerializer<EdDSAPublicKey>() {
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<Amount<*>>() {
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<JsonNode>()

View File

@ -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

View File

@ -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

View File

@ -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<Path> paths = Files.walk(Paths.get("build", "resources", "smokeTest"))) {
paths.forEach(file -> {
if (file.toString().contains("corda-finance")) {
try {
Files.copy(file, pluginsDir.resolve(file.getFileName()));
} catch (IOException ex) {
fail("Failed to copy finance jar");
}
}
});
} catch (IOException e) {
fail("Failed to walk files");
}
}
private NodeInfo fetchNotaryIdentity() {
List<NodeInfo> nodeDataSnapshot = rpcProxy.networkMapSnapshot();
return nodeDataSnapshot.get(0);

View File

@ -7,7 +7,7 @@ import net.corda.contracts.asset.Cash
import net.corda.contracts.getCashBalance
import net.corda.contracts.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<StandaloneCordaRPClientTest>()
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)

View File

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

View File

@ -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
------------

View File

@ -92,6 +92,8 @@ The ``WebServerPluginRegistry`` class defines the following:
be distributed within the CorDapp jars. This static content will not be available if the bundled web server is not
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

View File

@ -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')

View File

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

View File

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

View File

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

View File

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

View File

@ -14,17 +14,17 @@ class CurrencyUtilsTest {
@Test
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())
}
}

View File

@ -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)
}
/**

View File

@ -16,7 +16,6 @@ import net.corda.node.services.persistence.DBTransactionMappingStorage
import net.corda.node.services.persistence.DBTransactionStorage
import net.corda.node.services.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<MappedSchema> = 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<MappedSchema, SchemaService.SchemaOptions> =
mapOf(Pair(CashSchemaV1, SchemaService.SchemaOptions()),
Pair(CommonSchemaV1, SchemaService.SchemaOptions()),
mapOf(Pair(CommonSchemaV1, SchemaService.SchemaOptions()),
Pair(VaultSchemaV1, SchemaService.SchemaOptions()),
Pair(NodeServicesV1, SchemaService.SchemaOptions()))

View File

@ -3,16 +3,21 @@ package net.corda.node.services.vault;
import com.google.common.collect.ImmutableSet;
import 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<KeyPair> keys = new ArrayList<>();
keys.add(getMEGA_CORP_KEY());
keys.add(getDUMMY_NOTARY_KEY());
Set<MappedSchema> requiredSchemas = new HashSet<>();
requiredSchemas.add(CashSchemaV1.INSTANCE);
IdentityService identitySvc = makeTestIdentityService();
Pair<CordaPersistence, MockServices> databaseAndServices = makeTestDatabaseAndMockServices(Collections.EMPTY_SET, keys, () -> identitySvc);
Pair<CordaPersistence, MockServices> databaseAndServices = makeTestDatabaseAndMockServices(requiredSchemas, keys, () -> identitySvc);
issuerServices = new MockServices(getDUMMY_CASH_ISSUER_KEY(), getBOC_KEY());
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<Currency> amount = new Amount<>(100, Currency.getInstance("USD"));
VaultFiller.fillWithSomeTestCash(services,
new Amount<Currency>(100, Currency.getInstance("USD")),
new Amount<Currency>(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<LinearState> 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.Page<ContractState>, Vault.Update<ContractState>> results = vaultQuerySvc.trackBy(ContractState.class, compositeCriteria, pageSpec, sorting);
@ -302,7 +311,7 @@ public class VaultQueryJavaTests extends TestDependencyInjectionBase {
}
/**
* Aggregation Functions
* Aggregation Functions
*/
@Test

View File

@ -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",

View File

@ -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
}
}

View File

@ -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)

View File

@ -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<String, String> = mapOf(
"irsdemo" to javaClass.classLoader.getResource("irsweb").toExternalForm()
)
override fun customizeJSONSerialization(om: ObjectMapper): Unit = registerFinanceJSONMappers(om)
}

View File

@ -9,8 +9,8 @@ import net.corda.core.flows.FlowLogic
import net.corda.core.flows.InitiatedBy
import net.corda.core.flows.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<Unit> {
val future = openFuture<Unit>()
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.

View File

@ -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"]
}
}

View File

@ -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<String, String> = mapOf("simmvaluationdemo" to javaClass.classLoader.getResource("simmvaluationweb").toExternalForm())
override fun customizeJSONSerialization(om: ObjectMapper): Unit = registerFinanceJSONMappers(om)
}

View File

@ -24,11 +24,13 @@ configurations {
dependencies {
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
}
}

View File

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

View File

@ -4,6 +4,7 @@ import com.google.common.html.HtmlEscapers.htmlEscaper
import net.corda.client.rpc.CordaRPCClient
import net.corda.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))

View File

@ -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<String, String> get() = emptyMap()
/**
* Optionally register extra JSON serializers to the default ObjectMapper provider
* @param om The [ObjectMapper] to register custom types against.
*/
fun customizeJSONSerialization(om: ObjectMapper): Unit {}
}

View File

@ -1,8 +1,6 @@
package net.corda.webserver.servlets
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<ObjectMapper> {
val defaultObjectMapper = JacksonSupport.createDefaultMapper(rpc)
class ObjectMapperConfig(val defaultObjectMapper: ObjectMapper) : ContextResolver<ObjectMapper> {
override fun getContext(type: Class<*>) = defaultObjectMapper
}