Merge pull request #1265 from corda/mnesbit-nofinance-jacksonapi

Remove finance dependencies in node and client/jackson and make finance a CorDapp.
This commit is contained in:
Matthew Nesbit 2017-08-18 16:17:36 +01:00 committed by GitHub
commit 82173bc1a7
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
}